From e492673db2d38f4541e15d695769d03de06f3a07 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Wed, 11 May 2022 18:08:17 +0200 Subject: [PATCH 01/68] add wscc reduced order examples Signed-off-by: Jan Dinkelbach --- dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp index 529bbdb3d1..37f9accc66 100644 --- a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp +++ b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp @@ -149,7 +149,11 @@ int main(int argc, char *argv[]) { // log node voltage auto logger = DataLogger::make(simName, true, logDownSampling); for (auto node : sys.mNodes) +<<<<<<< HEAD logger->logAttribute(node->name() + ".V", node->attribute("v")); +======= + logger->addAttribute(node->name() + ".V", node->attribute("v")); +>>>>>>> 15f7252d (add wscc reduced order examples) // log generator vars for (auto comp : sys.mComponents) { From 00128497b79f204ca0e8e79a1a1e8d6272ac0696 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 12 May 2022 11:41:11 +0200 Subject: [PATCH 02/68] add smib reduced order load step examples Signed-off-by: Jan Dinkelbach --- .../examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp index bbff8cad7e..82ea168219 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp @@ -158,8 +158,7 @@ int main(int argc, char* argv[]) { // Logging // log node voltage auto logger = DataLogger::make(simName, true, logDownSampling); - for (auto node : systemDP.mNodes) - logger->logAttribute(node->name() + ".V", node->attribute("v")); + for (auto node : systemDP.mNodes) logger->logAttribute(node->name() + ".V", node->attribute("v")); // log generator vars logger->logAttribute(genDP->name() + ".Tm", genDP->attribute("Tm")); From 23c18d71152b8f2df45bb8d4671e3e4d3bbbd469 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Wed, 15 Jun 2022 12:18:41 +0200 Subject: [PATCH 03/68] added iterative 4 Order SG for EMT and DP domain Signed-off-by: Martin Moraga --- .../include/dpsim-models/Components.h | 2 + .../DP/DP_Ph1_SynchronGenerator4OrderIter.h | 107 +++++++ .../EMT/EMT_Ph3_SynchronGenerator4OrderIter.h | 103 +++++++ .../dpsim-models/Solver/MNASyncGenInterface.h | 42 +++ dpsim-models/src/CMakeLists.txt | 2 + .../DP/DP_Ph1_SynchronGenerator4OrderIter.cpp | 251 +++++++++++++++++ .../EMT_Ph3_SynchronGenerator4OrderIter.cpp | 264 ++++++++++++++++++ dpsim/examples/cxx/CMakeLists.txt | 3 + ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 188 +++++++++++++ ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 190 +++++++++++++ .../EMT_SMIB_ReducedOrderSG_LoadStep.cpp | 2 +- .../EMT_SynGen4OrderIter_SMIB_Fault.cpp | 223 +++++++++++++++ .../SP_SynGen4OrderDCIM_SMIB_Fault.cpp | 24 +- dpsim/examples/cxx/Examples.h | 4 + dpsim/include/dpsim/MNASolver.h | 4 + dpsim/include/dpsim/MNASolverDirect.h | 1 + dpsim/src/MNASolver.cpp | 5 + dpsim/src/MNASolverDirect.cpp | 71 +++-- 18 files changed, 1457 insertions(+), 29 deletions(-) create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h create mode 100644 dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h create mode 100644 dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h create mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp create mode 100644 dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp create mode 100644 dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp create mode 100644 dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp create mode 100644 dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 1e4af743f9..236cd2f9ee 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,7 @@ #include #include #include +#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h new file mode 100644 index 0000000000..ca2fba4f2a --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h @@ -0,0 +1,107 @@ +/* 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 4 Order Synchronous generator model for transient stability analysis + /// + /// This model is based on Eremia section 2.1.6. + class SynchronGenerator4OrderIter : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + protected: + /// + Int mNumIterations2; + + /// sim flags + bool mVoltageForm; + + // #### Model specific variables #### + /// + Matrix mVdq_prev; + /// previous voltage behind the transient impedance (p.u.) + Matrix mEdq_t; + Matrix mEdq_t_pred; + Matrix mEdq_t_corr; + /// derivative voltage behind the transient impedance (p.u.) + Matrix mdEdq_t; + Matrix mdEdq_t_corr; + /// + Real mElecTorque_corr; + /// + Real mdOmMech = 0; + Real mdOmMech_corr = 0; + Real mOmMech_pred; + Real mOmMech_corr; + /// prediction of mechanical system angle + Real mThetaMech_pred; + Real mThetaMech_corr; + /// + Real mDelta_pred; + Real mDelta_corr; + Real mdDelta = 0; + Real mdDelta_corr = 0; + + /// State Matrix x(k+1) = Ax(k) + Bu(k) + C + /// A Matrix + Matrix mA; + /// B Matrix + Matrix mB; + /// Constant Matrix + Matrix mC; + + /// Transformation matrix dp->dq + MatrixComp mDpToDq; + + /// Vector to create abc vector from a component + MatrixComp mShiftVector; + + public: + /// + SynchronGenerator4OrderIter(String uid, String name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator4OrderIter(String name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(String name); + + // #### General Functions #### + /// + void specificInitialization(); + /// + void calculateStateMatrix(); + /// + void stepInPerUnit(); + // + void correctorStep(); + /// + void updateVoltage(const Matrix& leftVector); + /// + bool checkVoltageDifference(); + /// + Matrix parkTransform(Real theta, const Matrix& abcVector); + + + /// Setters + /// + void useVoltageForm(bool state) {mVoltageForm = state;} + + // #### MNA Functions #### + void mnaApplyRightSideVectorStamp(Matrix& rightVector); + void mnaPostStep(const Matrix& leftVector); + void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h new file mode 100644 index 0000000000..5e279ecfc6 --- /dev/null +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h @@ -0,0 +1,103 @@ +/* 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. + class SynchronGenerator4OrderIter : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + protected: + /// + Int mNumIterations2; + + /// sim flags + bool mVoltageForm; + + // #### Model specific variables #### + /// + Matrix mVdq0_prev; + /// previous voltage behind the transient impedance (p.u.) + Matrix mEdq0_t; + Matrix mEdq0_t_pred; + Matrix mEdq0_t_corr; + /// derivative voltage behind the transient impedance (p.u.) + Matrix mdEdq0_t; + Matrix mdEdq0_t_corr; + /// + Real mElecTorque_corr; + /// + Real mdOmMech = 0; + Real mdOmMech_corr = 0; + Real mOmMech_pred; + Real mOmMech_corr; + /// prediction of mechanical system angle + Real mThetaMech_pred; + Real mThetaMech_corr; + /// + Real mDelta_pred; + Real mDelta_corr; + Real mdDelta = 0; + Real mdDelta_corr = 0; + + /// State Matrix x(k+1) = Ax(k) + Bu(k) + C + /// A Matrix + Matrix mA; + /// B Matrix + Matrix mB; + /// Constant Matrix + Matrix mC; + + public: + /// + SynchronGenerator4OrderIter(String uid, String name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator4OrderIter(String name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(String name); + + // #### General Functions #### + /// + void specificInitialization(); + /// + void calculateStateMatrix(); + /// + void stepInPerUnit(); + // + void correctorStep(); + /// + void updateVoltage(const Matrix& leftVector); + /// + bool checkVoltageDifference(); + /// + Matrix parkTransform(Real theta, const Matrix& abcVector); + /// + Matrix inverseParkTransform(Real theta, const Matrix& dq0Vector); + + + /// Setters + /// + void useVoltageForm(bool state) {mVoltageForm = state;} + + // #### MNA Functions #### + void mnaApplyRightSideVectorStamp(Matrix& rightVector); + void mnaPostStep(const Matrix& leftVector); + void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; + }; +} +} +} 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..3a59267348 --- /dev/null +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -0,0 +1,42 @@ +/* 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 { + public: + typedef std::shared_ptr Ptr; + typedef std::vector List; + + /// Returns true if it needs to iterate + virtual void correctorStep()=0; + /// + virtual void updateVoltage(const Matrix& leftVector)=0; + /// + virtual bool checkVoltageDifference() {return false;} + + /// Setters + /// + void setMaxIterations(Int maxIterations) {mMaxIter = maxIterations;} + /// + void setTolerance(Real Tolerance) {mTolerance = Tolerance;} + + /// + Int mNumIter = 0; + /// + Int mMaxIter = 10; + /// + Real mTolerance = 1e-6; + }; +} diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 3a333308bd..758b5e7876 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp + DP/DP_Ph1_SynchronGenerator4OrderIter.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp DP/DP_Ph1_Inverter.cpp @@ -86,6 +87,7 @@ list(APPEND MODELS_SOURCES EMT/EMT_Ph3_SynchronGenerator6aOrderVBR.cpp EMT/EMT_Ph3_SynchronGenerator6bOrderVBR.cpp EMT/EMT_Ph3_SynchronGeneratorDQ.cpp + EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp EMT/EMT_Ph3_SynchronGeneratorDQTrapez.cpp # EMT/EMT_Ph3_SynchronGeneratorDQSmpl.cpp # EMT/EMT_Ph3_SynchronGeneratorDQSmplCompSource.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp new file mode 100644 index 0000000000..d53e251ae7 --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp @@ -0,0 +1,251 @@ +/* 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::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter + (String uid, String name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + + mPhaseType = PhaseType::Single; + setTerminalNumber(1); + + // model flags + mVoltageForm = false; + + // model variables + mIntfVoltage = MatrixComp::Zero(1, 1); + mIntfCurrent = MatrixComp::Zero(1, 1); + mEdq_t = Matrix::Zero(2,1); + mEdq_t_pred = Matrix::Zero(2,1); + mEdq_t_corr = Matrix::Zero(2,1); + mdEdq_t = Matrix::Zero(2,1); + mdEdq_t_corr = Matrix::Zero(2,1); + + // + mShiftVector = Matrix::Zero(3,1); + mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; + + // Register attributes + addAttribute("NIterations", &mNumIterations2 , Flags::read); + addAttribute("Edq0_t", &mEdq_t, Flags::read); +} + +DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter + (String name, Logger::Level logLevel) + : SynchronGenerator4OrderIter(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderIter::clone(String name) { + auto copy = SynchronGenerator4OrderIter::make(name, mLogLevel); + + return copy; +} + +void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { + + // Initialize matrix of state representation + mA = Matrix::Zero(2,2); + mB = Matrix::Zero(2,2); + mC = Matrix::Zero(2,1); + calculateStateMatrix(); + + // 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; + + // initialize transformation matrix dp->dq + mDpToDq = Matrix::Zero(1,2); + + 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}" + "\n--- Model specific initialization finished ---", + + mEdq_t(0,0), + mEdq_t(1,0), + mMaxIter, + mTolerance + ); + mSLog->flush(); +} + +void DP::Ph1::SynchronGenerator4OrderIter::calculateStateMatrix() { + if (mVoltageForm) { + Real Td_t = mTd0_t * (mLd_t / mLd); + Real Tq_t = mTq0_t * (mLq_t / mLq); + mA << -1. / Tq_t , 0.0, + 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.0, + (1. / Td_t) * mEf * (mLd_t / mLd); + } + else { // Currents form + mA << -1./mTq0_t , 0.0, + 0.0 , -1/mTd0_t; + mB << 0.0 , (1. / mTq0_t) * (mLq-mLq_t), + (-1. / mTd0_t) * (mLd-mLd_t) , 0.0 ; + mC << 0.0, + (1./mTd0_t) * mEf; + } +} + +void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { + // set number of iteratios equal to zero + mNumIter = 0; + + // Predictor step (euler) + + //predict mechanical variables at t=k+1 + if (mSimTime>0.0) { + mElecTorque = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); + mdOmMech = 1 / (2.* mH) * (mMechTorque - mElecTorque); + mOmMech_pred = mOmMech + mTimeStep * mdOmMech; + mdDelta = (mOmMech_pred - 1.) * mBase_OmMech; + mDelta_pred = mDelta + mTimeStep * mdDelta; + mThetaMech_pred = mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; + } else { + mdOmMech = 0; + mOmMech_pred = mOmMech; + mdDelta = 0; + mDelta_pred = mDelta; + mThetaMech_pred = mThetaMech; + } + + //predict voltage behind transient reactance + if (mVoltageForm) + mdEdq_t = mA * mEdq_t + mB * mVdq + mC; + else + mdEdq_t = mA * mEdq_t + mB * mIdq + mC; + mEdq_t_pred = mEdq_t + mTimeStep * mdEdq_t; + + // predict armature currents for at t=k+1 + mIdq(0,0) = (mEdq_t_pred(1,0) - mVdq(1,0) ) / mLd_t; + mIdq(1,0) = (mVdq(0,0) - mEdq_t_pred(0,0) ) / mLq_t; + + // convert currents into the abc reference frame + mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); + mIntfCurrent(0,0) = (mDpToDq * mIdq)(0,0) * mBase_I_RMS; +} + +void DP::Ph1::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), mIntfCurrent(0, 0)); +} + +void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { + // corrector step (trapezoidal rule) + + if (mNumIter==0) + return; + + //predict mechanical variables + if (mSimTime>0.0) { + mElecTorque_corr = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); + mdOmMech_corr = 1 / (2.* mH) * (mMechTorque - mElecTorque_corr); + mOmMech_corr = mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); + mdDelta_corr = (mOmMech_corr - 1.) * mBase_OmMech; + mDelta_corr = mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); + mThetaMech_corr = mThetaMech + mTimeStep / 2. *(mOmMech + mOmMech_corr) * mBase_OmMech; + } else { + mElecTorque_corr = mElecTorque; + mdOmMech_corr = 0; + mOmMech_corr = mOmMech; + mdDelta_corr = 0; + mDelta_corr = mDelta; + mThetaMech_corr = mThetaMech; + } + + //predict voltage behind transient reactance + if (mVoltageForm) + mdEdq_t_corr = mA * mEdq_t + mB * mVdq + mC; + else + mdEdq_t_corr = mA * mEdq_t + mB * mIdq + mC; + mEdq_t_corr = mEdq_t + mTimeStep / 2 * (mdEdq_t + mdEdq_t_corr); + + // armature currents for at t=k+1 + mIdq(0,0) = (mEdq_t_corr(1,0) - mVdq(1,0) ) / mLd_t; + mIdq(1,0) = (mVdq(0,0) - mEdq_t_corr(0,0) ) / mLq_t; + + // convert currents into the abc reference frame + mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); + mIntfCurrent(0,0) = (mDpToDq * mIdq)(0,0) * mBase_I_RMS; + + // stamp currents + mnaApplyRightSideVectorStamp(mRightVector); +} + +void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVector) { + mVdq_prev = mVdq; + mIntfVoltage(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage into dq reference frame + MatrixComp Vabc_ = mIntfVoltage(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix Vabc = Matrix(3,1); + Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); + if (mNumIter==0) { + mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; + } else { + mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; + } +} + +bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { + if (mNumIter==0) { + // if no corrector step has been performed yet + mNumIter = 1; + return true; + } + + Matrix voltageDifference = mVdq - mVdq_prev; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { + if (mNumIter > mMaxIter) { + return false; + } else { + mNumIter = mNumIter + 1; + return true; + } + } else { + return false; + } + +} + +void DP::Ph1::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { + // update variables + mEdq_t = mEdq_t_corr; + mOmMech = mOmMech_corr; + mThetaMech = mThetaMech_corr; + mDelta = mDelta_corr; + + // + mNumIterations2 = mNumIter; +} + +Matrix DP::Ph1::SynchronGenerator4OrderIter::parkTransform(Real theta, const Matrix& abcVector) { + Matrix dq0Vector(3, 1); + Matrix dqVector(2, 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; + dqVector << dq0Vector(0,0), dq0Vector(1,0); + return dqVector; +} \ No newline at end of file diff --git a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp new file mode 100644 index 0000000000..67884c32d5 --- /dev/null +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp @@ -0,0 +1,264 @@ +/* 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::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter + (String uid, String name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + + mPhaseType = PhaseType::ABC; + setTerminalNumber(1); + + // model flags + mVoltageForm = false; + + // model variables + mIntfVoltage = Matrix::Zero(3,1); + mIntfCurrent = Matrix::Zero(3,1); + mEdq0_t = Matrix::Zero(3,1); + mEdq0_t_pred = Matrix::Zero(3,1); + mEdq0_t_corr = Matrix::Zero(3,1); + mdEdq0_t = Matrix::Zero(3,1); + mdEdq0_t_corr = Matrix::Zero(3,1); + + // Register attributes + addAttribute("NIterations", &mNumIterations2 , Flags::read); + addAttribute("Edq0_t", &mEdq0_t, Flags::read); +} + +EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter + (String name, Logger::Level logLevel) + : SynchronGenerator4OrderIter(name, name, logLevel) { +} + +SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderIter::clone(String name) { + auto copy = SynchronGenerator4OrderIter::make(name, mLogLevel); + + return copy; +} + +void EMT::Ph3::SynchronGenerator4OrderIter::specificInitialization() { + + // Initialize matrix of state representation + mA = Matrix::Zero(3,3); + mB = Matrix::Zero(3,3); + mC = Matrix::Zero(3,1); + calculateStateMatrix(); + + // 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; + + 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}" + "\n--- Model specific initialization finished ---", + + mEdq0_t(0,0), + mEdq0_t(1,0), + mMaxIter, + mTolerance + ); + mSLog->flush(); +} + +void EMT::Ph3::SynchronGenerator4OrderIter::calculateStateMatrix() { + if (mVoltageForm) { + Real Td_t = mTd0_t * (mLd_t / mLd); + Real Tq_t = mTq0_t * (mLq_t / mLq); + mA << -1. / Tq_t , 0.0, 0.0, + 0.0 , -1 / Td_t, 0.0, + 0.0 , 0.0, 0.0; + mB << (1. / Tq_t) * (mLq-mLq_t) / mLq, 0.0, 0.0, + 0.0, (1. / Td_t) * (mLd-mLd_t) / mLd, 0.0, + 0.0, 0.0, 0.0; + mC << 0.0, + (1. / Td_t) * mEf * (mLd_t / mLd), + 0.0; + } + else { // Currents form + mA << -1./mTq0_t , 0.0, 0.0, + 0.0 , -1/mTd0_t, 0.0, + 0.0 , 0.0, 0.0; + mB << 0.0 , (1. / mTq0_t) * (mLq-mLq_t), 0.0, + (-1. / mTd0_t) * (mLd-mLd_t) , 0.0 , 0.0, + 0.0 , 0.0 , 0.0; + mC << 0.0, + (1./mTd0_t) * mEf, + 0.0; + } +} + +void EMT::Ph3::SynchronGenerator4OrderIter::stepInPerUnit() { + // set number of iteratios equal to zero + mNumIter = 0; + + // Predictor step (euler) + + //predict mechanical variables at t=k+1 + if (mSimTime>0.0) { + mElecTorque = mVdq0(0,0) * mIdq0(0,0) + mVdq0(1,0) * mIdq0(1,0); + mdOmMech = 1 / (2.* mH) * (mMechTorque - mElecTorque); + mOmMech_pred = mOmMech + mTimeStep * mdOmMech; + mdDelta = (mOmMech_pred - 1.) * mBase_OmMech; + mDelta_pred = mDelta + mTimeStep * mdDelta; + mThetaMech_pred = mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; + } else { + mdOmMech = 0; + mOmMech_pred = mOmMech; + mdDelta = 0; + mDelta_pred = mDelta; + mThetaMech_pred = mThetaMech; + } + + //predict voltage behind transient reactance + if (mVoltageForm) + mdEdq0_t = mA * mEdq0_t + mB * mVdq0 + mC; + else + mdEdq0_t = mA * mEdq0_t + mB * mIdq0 + mC; + mEdq0_t_pred = mEdq0_t + mTimeStep * mdEdq0_t; + + // predict armature currents for at t=k+1 + mIdq0(0,0) = (mEdq0_t_pred(1,0) - mVdq0(1,0) ) / mLd_t; + mIdq0(1,0) = (mVdq0(0,0) - mEdq0_t_pred(0,0) ) / mLq_t; + mIdq0(2,0) = 0.0; + + // convert currents into the abc domain + mIntfCurrent = inverseParkTransform(mThetaMech_pred, mIdq0); + mIntfCurrent = mIntfCurrent * mBase_I; +} + +void EMT::Ph3::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(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::SynchronGenerator4OrderIter::correctorStep() { + // corrector step (trapezoidal rule) + + if (mNumIter==0) + return; + + //predict mechanical variables + if (mSimTime>0.0) { + mElecTorque_corr = mVdq0(0,0) * mIdq0(0,0) + mVdq0(1,0) * mIdq0(1,0); + mdOmMech_corr = 1 / (2.* mH) * (mMechTorque - mElecTorque_corr); + mOmMech_corr = mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); + mdDelta_corr = (mOmMech_corr - 1.) * mBase_OmMech; + mDelta_corr = mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); + mThetaMech_corr = mThetaMech + mTimeStep / 2. *(mOmMech + mOmMech_corr) * mBase_OmMech; + } else { + mElecTorque_corr = mElecTorque; + mdOmMech_corr = 0; + mOmMech_corr = mOmMech; + mdDelta_corr = 0; + mDelta_corr = mDelta; + mThetaMech_corr = mThetaMech; + } + + //predict voltage behind transient reactance + if (mVoltageForm) + mdEdq0_t_corr = mA * mEdq0_t + mB * mVdq0 + mC; + else + mdEdq0_t_corr = mA * mEdq0_t + mB * mIdq0 + mC; + mEdq0_t_corr = mEdq0_t + mTimeStep / 2 * (mdEdq0_t + mdEdq0_t_corr); + + // armature currents for at t=k+1 + mIdq0(0,0) = (mEdq0_t_corr(1,0) - mVdq0(1,0) ) / mLd_t; + mIdq0(1,0) = (mVdq0(0,0) - mEdq0_t_corr(0,0) ) / mLq_t; + + // convert currents into the abc domain + mIntfCurrent = inverseParkTransform(mThetaMech_corr, mIdq0); + mIntfCurrent = mIntfCurrent * mBase_I; + + // stamp currents + mnaApplyRightSideVectorStamp(mRightVector); +} + +void EMT::Ph3::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVector) { + mVdq0_prev = 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 + if (mNumIter==0) { + mVdq0 = parkTransform(mThetaMech_pred, mIntfVoltage); + } else { + mVdq0 = parkTransform(mThetaMech_corr, mIntfVoltage); + } + mVdq0 = mVdq0 / mBase_V; +} + +bool EMT::Ph3::SynchronGenerator4OrderIter::checkVoltageDifference() { + if (mNumIter==0) { + // if no corrector step has been performed yet + mNumIter = 1; + return true; + } + + Matrix voltageDifference = mVdq0 - mVdq0_prev; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { + if (mNumIter > mMaxIter) { + return false; + } else { + mNumIter = mNumIter + 1; + return true; + } + } else + return false; +} + +void EMT::Ph3::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { + // update variables + mEdq0_t = mEdq0_t_corr; + mOmMech = mOmMech_corr; + mThetaMech = mThetaMech_corr; + mDelta = mDelta_corr; + + // + mNumIterations2 = mNumIter; +} + +Matrix EMT::Ph3::SynchronGenerator4OrderIter::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::SynchronGenerator4OrderIter::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/examples/cxx/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 424c1e9666..09e55c107b 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -75,11 +75,14 @@ set(CIRCUIT_SOURCES 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 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..d4d4362a64 --- /dev/null +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -0,0 +1,188 @@ +#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 = "DP_SMIB_ReducedOrderSGIterative_LoadStep"; + Real timeStep = 10e-6; + Real finalTime = 35; + + // Default configuration + String sgType = defaultConfig.sgType; + Real loadStepEventTime = defaultConfig.loadStepEventTime; + Real H = syngenKundur.H; + + // Command line args processing + CommandLineArgs args(argc, argv); + if (argc > 1) { + timeStep = args.timeStep; + //finalTime = args.duration; + //if (args.name != "dpsim") + if (args.options.find("SimName") != args.options.end()) + simName = args.getOptionString("SimName"); + if (args.options.find("sgType") != args.options.end()) + sgType = args.getOptionString("sgType"); + if (args.options.find("loadStepEventTime") != args.options.end()) + loadStepEventTime = args.getOptionReal("loadStepEventTime"); + if (args.options.find("inertia") != args.options.end()) + H = args.getOptionReal("inertia"); + } + + // Configure logging + Logger::Level logLevel = Logger::Level::info; + + // apply downsampling for simulation step sizes lower than 10us + Real logDownSampling; + if (timeStep < 1e-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->addAttribute("v1", n1PF->attribute("v")); + loggerPF->addAttribute("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 = DP::Ph1::SynchronGenerator4OrderIter::make("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); + genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); + genDP->setMaxIterations(defaultConfig.maxIter); + genDP->setTolerance(defaultConfig.tolerance); + + //Grid bus as Slack + //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->addAttribute(node->name() + ".V", node->attribute("v")); + + // log generator vars + //logger->addAttribute(genDP->name() + ".Tm", genDP->attribute("Tm")); + logger->addAttribute(genDP->name() + ".Te", genDP->attribute("Te")); + //logger->addAttribute(genDP->name() + ".omega", genDP->attribute("w_r")); + logger->addAttribute(genDP->name() + ".delta", genDP->attribute("delta")); + logger->addAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); + //logger->addAttribute(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.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simDP.addLogger(logger); + //simDP.doSystemMatrixRecomputation(true); + + // Events + simDP.addEvent(loadStepEvent); + + simDP.run(); +} \ 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..6349c714b0 --- /dev/null +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -0,0 +1,190 @@ +#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 + String sgType = defaultConfig.sgType; + Real loadStepEventTime = defaultConfig.loadStepEventTime; + Real H = syngenKundur.H; + + // Command line args processing + CommandLineArgs args(argc, argv); + if (argc > 1) { + timeStep = args.timeStep; + finalTime = args.duration; + if (args.name != "dpsim") + simName = args.name; + if (args.options.find("sgType") != args.options.end()) + sgType = args.getOptionString("sgType"); + if (args.options.find("loadStepEventTime") != args.options.end()) + loadStepEventTime = args.getOptionReal("loadStepEventTime"); + if (args.options.find("inertia") != args.options.end()) + H = args.getOptionReal("inertia"); + } + + // 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->addAttribute("v1", n1PF->attribute("v")); + loggerPF->addAttribute("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::SynchronGenerator4OrderIter::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(defaultConfig.maxIter); + genEMT->setTolerance(defaultConfig.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->addAttribute(node->name() + ".V", node->attribute("v")); + + // log generator vars + //logger->addAttribute(genEMT->name() + ".Tm", genEMT->attribute("Tm")); + logger->addAttribute(genEMT->name() + ".Te", genEMT->attribute("Te")); + logger->addAttribute(genEMT->name() + ".omega", genEMT->attribute("w_r")); + logger->addAttribute(genEMT->name() + ".delta", genEMT->attribute("delta")); + logger->addAttribute(genEMT->name() + ".NIterations", genEMT->attribute("NIterations")); + //logger->addAttribute(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.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simEMT.addLogger(logger); + //simEMT.doSystemMatrixRecomputation(true); + + // Events + simEMT.addEvent(loadStepEvent); + + simEMT.run(); +} \ No newline at end of file diff --git a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp index c4b673356d..6de4b0c0aa 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp @@ -21,7 +21,7 @@ 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 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..e076179e30 --- /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->addAttribute("v1", n1PF->attribute("v")); + loggerPF->addAttribute("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::SynchronGenerator4OrderIter::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->addAttribute("v2", n2EMT->attribute("v")); + //loggerEMT->addAttribute("v_gen", genEMT->attribute("v_intf")); + //loggerEMT->addAttribute("i_gen", genEMT->attribute("i_intf")); + loggerEMT->addAttribute("Etorque", genEMT->attribute("Te")); + loggerEMT->addAttribute("delta", genEMT->attribute("delta")); + loggerEMT->addAttribute("w_r", genEMT->attribute("w_r")); + loggerEMT->addAttribute("Vdq0", genEMT->attribute("Vdq0")); + loggerEMT->addAttribute("Idq0", genEMT->attribute("Idq0")); + loggerEMT->addAttribute("Edq0", genEMT->attribute("Edq0_t")); + loggerEMT->addAttribute("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.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + + // 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); +} \ 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 index 9f2ae212b7..ba7f2b8695 100644 --- a/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp @@ -11,14 +11,14 @@ Examples::Grids::SMIB::ScenarioConfig2 GridParams; // Generator parameters Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; -int main(int argc, char* argv[]) { +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 timeStep = 10e-6; Real finalTime = 20; // Command line args processing @@ -47,7 +47,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); @@ -57,10 +57,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); @@ -92,7 +92,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(); @@ -110,9 +110,9 @@ int main(int argc, char* argv[]) { genSP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, syngenKundur.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)); //Grid bus as Slack @@ -121,9 +121,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); @@ -165,6 +165,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/Examples.h b/dpsim/examples/cxx/Examples.h index 9cb9ac9cb6..b431f2c95e 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 = 20; + Real tolerance = 1e-10; }; struct GridParams { diff --git a/dpsim/include/dpsim/MNASolver.h b/dpsim/include/dpsim/MNASolver.h index 8f5e12dd19..226387e1a0 100644 --- a/dpsim/include/dpsim/MNASolver.h +++ b/dpsim/include/dpsim/MNASolver.h @@ -17,9 +17,11 @@ #include #include #include +<<<<<<< HEAD:dpsim/include/dpsim/MNASolver.h #include #include #include +#include #include #include @@ -75,6 +77,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..c4414b26f8 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; 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..774cb1a87c 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -215,26 +215,65 @@ std::shared_ptr MnaSolverDirect::createLogTask() template 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) - for (auto stamp : mRightVectorStamps) - mRightSideVector += *stamp; + if (mSyncGen.size()==0) { + // Reset source vector + mRightSideVector.setZero(); + + // 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){ + 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()); + } else { + // if there is iterative syncGens, then it is necessary to iterate + bool iterate = true; + while (iterate) { + // 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()); + } - if (!mIsInInitialization) - MnaSolver::updateSwitchStatus(); + for (auto syncGen : mSyncGen) + //update voltages + syncGen->updateVoltage(mLeftSideVector); - 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 if there is sync generators that need iterate + int count=0; + for (auto syncGen : mSyncGen) { + if (syncGen->checkVoltageDifference()) + count = count+1; + } + if (count==0) + iterate=false; + } } - // TODO split into separate task? (dependent on x, updating all v attributes) for (UInt nodeIdx = 0; nodeIdx < mNumNetNodes; ++nodeIdx) mNodes[nodeIdx]->mnaUpdateVoltage(**mLeftSideVector); From af20536e015c2ef51ff811141286d08ae821725a Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 15 Sep 2022 15:14:24 +0200 Subject: [PATCH 04/68] small changes in study case LoadStep Signed-off-by: Martin Moraga --- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 27 ++++++++++++------- dpsim/examples/cxx/Examples.h | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index d4d4362a64..0e42d43632 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -26,15 +26,20 @@ int main(int argc, char* argv[]) { String sgType = defaultConfig.sgType; Real loadStepEventTime = defaultConfig.loadStepEventTime; Real H = syngenKundur.H; + Real tolerance = defaultConfig.tolerance; + int maxIter = defaultConfig.maxIter; // Command line args processing CommandLineArgs args(argc, argv); if (argc > 1) { - timeStep = args.timeStep; - //finalTime = args.duration; - //if (args.name != "dpsim") 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("sgType") != args.options.end()) sgType = args.getOptionString("sgType"); if (args.options.find("loadStepEventTime") != args.options.end()) @@ -43,6 +48,13 @@ int main(int argc, char* argv[]) { H = args.getOptionReal("inertia"); } + 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: " << sgType << std::endl; + // Configure logging Logger::Level logLevel = Logger::Level::info; @@ -130,10 +142,9 @@ int main(int argc, char* argv[]) { syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t); genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genDP->setMaxIterations(defaultConfig.maxIter); - genDP->setTolerance(defaultConfig.tolerance); + genDP->setMaxIterations(maxIter); + genDP->setTolerance(tolerance); - //Grid bus as Slack //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); extnetDP->setParameters(gridParams.VnomMV); @@ -161,12 +172,9 @@ int main(int argc, char* argv[]) { logger->addAttribute(node->name() + ".V", node->attribute("v")); // log generator vars - //logger->addAttribute(genDP->name() + ".Tm", genDP->attribute("Tm")); logger->addAttribute(genDP->name() + ".Te", genDP->attribute("Te")); - //logger->addAttribute(genDP->name() + ".omega", genDP->attribute("w_r")); logger->addAttribute(genDP->name() + ".delta", genDP->attribute("delta")); logger->addAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); - //logger->addAttribute(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); @@ -179,7 +187,6 @@ int main(int argc, char* argv[]) { simDP.setDomain(Domain::DP); simDP.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); simDP.addLogger(logger); - //simDP.doSystemMatrixRecomputation(true); // Events simDP.addEvent(loadStepEvent); diff --git a/dpsim/examples/cxx/Examples.h b/dpsim/examples/cxx/Examples.h index b431f2c95e..ed7af6ae35 100644 --- a/dpsim/examples/cxx/Examples.h +++ b/dpsim/examples/cxx/Examples.h @@ -377,7 +377,7 @@ namespace Scenario6 { Real loadStepEventTime = 10.0; // parameters of iterative model - Real maxIter = 20; + Real maxIter = 25; Real tolerance = 1e-10; }; From 86373aae7ceb856df073fc4530958e12086ea420 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Wed, 21 Sep 2022 13:41:22 +0200 Subject: [PATCH 05/68] minimal changes in iterative procedure Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderIter.h | 3 --- .../dpsim-models/Solver/MNASyncGenInterface.h | 2 +- .../src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp | 14 ++++++-------- .../DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp | 1 - 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h index ca2fba4f2a..ba615fc626 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h @@ -22,9 +22,6 @@ namespace Ph1 { public MNASyncGenInterface, public SharedFactory { protected: - /// - Int mNumIterations2; - /// sim flags bool mVoltageForm; diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 3a59267348..9f4ffa4ed1 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -35,7 +35,7 @@ namespace CPS { /// Int mNumIter = 0; /// - Int mMaxIter = 10; + Int mMaxIter = 25; /// Real mTolerance = 1e-6; }; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp index d53e251ae7..2f954f0d3f 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp @@ -34,7 +34,7 @@ DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; // Register attributes - addAttribute("NIterations", &mNumIterations2 , Flags::read); + addAttribute("NIterations", &mNumIter , Flags::read); addAttribute("Edq0_t", &mEdq_t, Flags::read); } @@ -147,7 +147,8 @@ void DP::Ph1::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { // corrector step (trapezoidal rule) - if (mNumIter==0) + mNumIter = mNumIter + 1; + if (mNumIter==1) return; //predict mechanical variables @@ -203,9 +204,9 @@ void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVecto } bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { - if (mNumIter==0) { + if (mNumIter==1) { // if no corrector step has been performed yet - mNumIter = 1; + //mNumIter = 1; return true; } @@ -214,7 +215,7 @@ bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { if (mNumIter > mMaxIter) { return false; } else { - mNumIter = mNumIter + 1; + //mNumIter = mNumIter + 1; return true; } } else { @@ -229,9 +230,6 @@ void DP::Ph1::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) mOmMech = mOmMech_corr; mThetaMech = mThetaMech_corr; mDelta = mDelta_corr; - - // - mNumIterations2 = mNumIter; } Matrix DP::Ph1::SynchronGenerator4OrderIter::parkTransform(Real theta, const Matrix& abcVector) { diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index 0e42d43632..f26d7e23f2 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -173,7 +173,6 @@ int main(int argc, char* argv[]) { // log generator vars logger->addAttribute(genDP->name() + ".Te", genDP->attribute("Te")); - logger->addAttribute(genDP->name() + ".delta", genDP->attribute("delta")); logger->addAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); // load step event From 4eb1caccf6328c3a4ddf4b476c80b95570bd706a Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Mon, 7 Nov 2022 09:44:25 +0100 Subject: [PATCH 06/68] add SG 6 order iterative Signed-off-by: Martin Moraga --- .../include/dpsim-models/Components.h | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderIter.h | 4 +- .../DP/DP_Ph1_SynchronGenerator6OrderIter.h | 110 ++++++++ .../include/dpsim-models/Definitions.h | 3 +- .../EMT/EMT_Ph3_SynchronGenerator4OrderIter.h | 4 +- dpsim-models/src/CIM/Reader.cpp | 16 +- dpsim-models/src/CMakeLists.txt | 1 + .../DP/DP_Ph1_SynchronGenerator6OrderIter.cpp | 253 ++++++++++++++++++ 8 files changed, 385 insertions(+), 7 deletions(-) create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h create mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 236cd2f9ee..3e6edf47eb 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h index ba615fc626..d8d363f056 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h @@ -8,8 +8,8 @@ #pragma once -#include -#include +#include +#include namespace CPS { namespace DP { diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h new file mode 100644 index 0000000000..c414c6e3df --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h @@ -0,0 +1,110 @@ +/* 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 6 Order Synchronous generator model for transient stability analysis + /// + /// This model is based on Eremia section 2.1.6. + class SynchronGenerator6OrderIter : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + protected: + /// sim flags + bool mVoltageForm; + + // #### Model specific variables #### + /// generator terminal at time k-1 + Matrix mVdq_prev; + /// predicted generator current at time k + Matrix mIdq_pred; + /// corrected generator current at time k + Matrix mIdq_corr; + /// voltage behind the transient impedance at time k-1 + Matrix mEdq; + /// predicted voltage behind the transient and subtransient impedance at time k+1 + Matrix mEdq_pred; + /// corrected voltage behind the transient and subtransient impedance at time k+1 + Matrix mEdq_corr; + /// corrected electrical torque + Real mElecTorque_corr; + /// predicted mechanical omega at time k + Real mOmMech_pred; + /// corrected mechanical omega at time k + Real mOmMech_corr; + /// prediction mechanical system angle at time k + Real mThetaMech_pred; + /// corrected mechanical system angle at time k + Real mThetaMech_corr; + /// predicted delta at time k + Real mDelta_pred; + /// corrected delta at time k + Real mDelta_corr; + + /// State Matrix backward euler: Edq(k) = A * Edq(k) + B * Idq + C * Ef + /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + C * Ef + /// A Matrix + Matrix mA; + Matrix mA_prev; + Matrix mA_corr; + /// B Matrix + Matrix mB; + Matrix mB_corr; + /// Constant Matrix + Matrix mC; + + /// Transformation matrix dp->dq + MatrixComp mDpToDq; + + /// Vector to create abc vector from a component + MatrixComp mShiftVector; + + public: + /// + SynchronGenerator6OrderIter(String uid, String name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator6OrderIter(String name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(String name); + + // #### General Functions #### + /// + void specificInitialization(); + /// + void calculateStateMatrix(); + /// + void stepInPerUnit(); + // + void correctorStep(); + /// + void updateVoltage(const Matrix& leftVector); + /// + bool checkVoltageDifference(); + /// + Matrix parkTransform(Real theta, const Matrix& abcVector); + + + /// Setters + /// + void useVoltageForm(bool state) {mVoltageForm = state;} + + // #### MNA Functions #### + void mnaApplyRightSideVectorStamp(Matrix& rightVector); + void mnaPostStep(const Matrix& leftVector); + void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/Definitions.h b/dpsim-models/include/dpsim-models/Definitions.h index 723775c7a4..22605dd87f 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, SG4OrderIter, SG6OrderIter, 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_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h index 5e279ecfc6..1cb6fbbf94 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h @@ -8,8 +8,8 @@ #pragma once -#include -#include +#include +#include namespace CPS { namespace EMT { diff --git a/dpsim-models/src/CIM/Reader.cpp b/dpsim-models/src/CIM/Reader.cpp index 89345f6552..b8350fe703 100644 --- a/dpsim-models/src/CIM/Reader.cpp +++ b/dpsim-models/src/CIM/Reader.cpp @@ -524,7 +524,9 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin || mGeneratorType == GeneratorType::SG6aOrderVBR || mGeneratorType == GeneratorType::SG6bOrderVBR || mGeneratorType == GeneratorType::SG4OrderVBR - || mGeneratorType == GeneratorType::SG3OrderVBR) { + || mGeneratorType == GeneratorType::SG3OrderVBR + || mGeneratorType == GeneratorType::SG4OrderIter + || mGeneratorType == GeneratorType::SG6OrderIter) { Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M); Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k); @@ -594,6 +596,18 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Td0_t); return gen; + } else if (mGeneratorType == GeneratorType::SG4OrderIter) { + mSLog->info(" GeneratorType is SynchronGenerator4OrderIter."); + 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::SG6OrderIter) { + mSLog->info(" GeneratorType is SynchronGenerator6OrderIter."); + 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; } } } diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 758b5e7876..760772e7bf 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -38,6 +38,7 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderIter.cpp + DP/DP_Ph1_SynchronGenerator6OrderIter.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp DP/DP_Ph1_Inverter.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp new file mode 100644 index 0000000000..99fabe13cb --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp @@ -0,0 +1,253 @@ +/* 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::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter + (String uid, String name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + + mPhaseType = PhaseType::Single; + setTerminalNumber(1); + + // model flags (deprecated) + mVoltageForm = false; + + // model variables + mIntfVoltage = MatrixComp::Zero(1, 1); + mIntfCurrent = MatrixComp::Zero(1, 1); + + // Initialize matrix + mIdq_pred = Matrix::Zero(2,1); + mIdq_corr = Matrix::Zero(2,1); + mEdq = Matrix::Zero(4,1); + mEdq_pred = Matrix::Zero(4,1); + mEdq_corr = Matrix::Zero(4,1); + + // + mShiftVector = Matrix::Zero(3,1); + mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; + + // Register attributes + addAttribute("NIterations", &mNumIter , Flags::read); + addAttribute("Edq0_t", &mEdq, Flags::read); +} + +DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter + (String name, Logger::Level logLevel) + : SynchronGenerator6OrderIter(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderIter::clone(String name) { + auto copy = SynchronGenerator6OrderIter::make(name, mLogLevel); + + return copy; +} + +void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { + + // Initialize matrix of state representation + mA = Matrix::Zero(4,4); + mA_prev = Matrix::Zero(4,4); + mA_corr = Matrix::Zero(4,4); + mB = Matrix::Zero(4,2); + mB_corr = Matrix::Zero(4,2); + mC = Matrix::Zero(4,1); + calculateStateMatrix(); + + // initial voltage behind the transient reactance in the dq0 reference frame + mEdq(0,0) = (mLq - mLq_t) * mIdq(1,0); + mEdq(1,0) = - (mLd- mLd_t) * mIdq(0,0) + mEf; + mEdq(2,0) = mEdq(0,0) + (mLq_t - mLq_s) * mIdq(1,0); + mEdq(3,0) = mEdq(1,0) - (mLd_t - mLd_s) * mIdq(0,0); + + // initialize transformation matrix dp->dq + mDpToDq = Matrix::Zero(1,2); + + 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}" + "\n--- Model specific initialization finished ---", + + mEdq(0,0), + mEdq(1,0), + mEdq(2,0), + mEdq(3,0), + mMaxIter, + mTolerance + ); + mSLog->flush(); +} + +void DP::Ph1::SynchronGenerator6OrderIter::calculateStateMatrix() { + mA << 1 - mTimeStep / mTq0_t, 0.0, 0.0, 0.0, + 0.0, 1 - mTimeStep / mTd0_t, 0.0, 0.0, + mTimeStep / mTq0_s, 0.0, 1 - mTimeStep / mTq0_s, 0.0, + 0.0, mTimeStep / mTd0_s, 0.0, 1 - mTimeStep / mTd0_s; + mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, + 0.0, 1 - mTimeStep / (2 * mTd0_t), 0.0, 0.0, + mTimeStep / (2 * mTq0_s), 0.0, 1 - mTimeStep / (2 * mTq0_s), 0.0, + 0.0, mTimeStep / (2 * mTd0_s), 0.0, 1 - mTimeStep / (2 * mTd0_s); + mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, + 0.0, - mTimeStep / (2 * mTd0_t), 0.0, 0.0, + mTimeStep / (2 * mTq0_s), 0.0, - mTimeStep / (2 * mTq0_s), 0.0, + 0.0, mTimeStep / (2 * mTd0_s), 0.0, - mTimeStep / (2 * mTd0_s); + mB << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, + - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0, + 0.0, (mLq_t - mLq_s) * mTimeStep / mTq0_s, + - (mLd_t - mLd_s) * mTimeStep / mTd0_s, 0.0; + mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), + - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0, + 0.0, (mLq_t - mLq_s) * mTimeStep / (2 * mTq0_s), + - (mLd_t - mLd_s) * mTimeStep / (2 * mTd0_s), 0.0; + mC << 0.0, + (mTimeStep / mTd0_t), + 0.0, + 0.0; +} + +void DP::Ph1::SynchronGenerator6OrderIter::stepInPerUnit() { + // set number of iteratios equal to zero + mNumIter = 0; + + // Predictor step (backward euler) + if (mSimTime>0.0) { + // calculate electrical torque at t=k-1 + mElecTorque = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); + // predict mechanical variables at t=k + mOmMech_pred = mOmMech + mTimeStep / (2 * mH) * (mMechTorque - mElecTorque); + mDelta_pred = mDelta + mTimeStep * mBase_OmMech * (mOmMech - 1); + mThetaMech_pred = mThetaMech + mTimeStep * mOmMech * mBase_OmMech; + } else { + mOmMech_pred = mOmMech; + mDelta_pred = mDelta; + mThetaMech_pred = mThetaMech; + } + + //predict voltage behind transient reactance + mEdq_pred = mA * mEdq + mB * mIdq + mC * mEf; + + // predict armature currents for at t=k+1 + mIdq_pred(0,0) = (mEdq_pred(3,0) - mVdq(1,0) ) / mLd_s; + mIdq_pred(1,0) = (mVdq(0,0) - mEdq_pred(2,0) ) / mLq_s; + + // convert currents into the abc reference frame + mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); + mIntfCurrent(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; +} + +void DP::Ph1::SynchronGenerator6OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), mIntfCurrent(0, 0)); +} + +void DP::Ph1::SynchronGenerator6OrderIter::correctorStep() { + // corrector step (trapezoidal rule) + mNumIter = mNumIter + 1; + + if (mSimTime>0.0) { + // calculate electrical torque at t=k + mElecTorque_corr = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); + // correct mechanical variables at t=k + mOmMech_corr = mOmMech + mTimeStep / (4. * mH) * (2 * mMechTorque - mElecTorque - mElecTorque_corr); + mDelta_corr = mDelta + mTimeStep / 2. * mBase_OmMech * (mOmMech + mOmMech_pred - 2); + mThetaMech_corr = mThetaMech + mTimeStep / 2. *(mOmMech + mOmMech_pred) * mBase_OmMech; + } else { + mElecTorque_corr = mElecTorque; + mOmMech_corr = mOmMech_pred; + mDelta_corr = mDelta_pred; + mThetaMech_corr = mThetaMech_pred; + } + + //predict voltage behind transient reactance + mEdq_corr = mA_prev * mEdq + mA_corr * mEdq_pred + mB_corr * (mIdq + mIdq_pred) + mC * mEf ; + + // armature currents for at t=k+1 + mIdq_corr(0,0) = (mEdq_corr(1,0) - mVdq(1,0) ) / mLd_t; + mIdq_corr(1,0) = (mVdq(0,0) - mEdq_corr(0,0) ) / mLq_t; + + // convert currents into the abc reference frame + mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); + mIntfCurrent(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; + + // stamp currents + mnaApplyRightSideVectorStamp(mRightVector); +} + +void DP::Ph1::SynchronGenerator6OrderIter::updateVoltage(const Matrix& leftVector) { + mVdq_prev = mVdq; + mIntfVoltage(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage into dq reference frame + MatrixComp Vabc_ = mIntfVoltage(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix Vabc = Matrix(3,1); + Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); + if (mNumIter==0) { + mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; + } else { + mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; + } +} + +bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { + if (mNumIter==0) { + // if no corrector step has been performed yet + return true; + } + + Matrix voltageDifference = mVdq - mVdq_prev; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { + if (mNumIter == mMaxIter) { + return false; + } else { + // set predicted values equal to corrected values for the next iteration + mOmMech_pred = mOmMech_corr; + mDelta_pred = mDelta_corr; + mThetaMech_pred= mDelta_corr; + mIdq_pred = mIdq_corr; + mEdq_pred = mEdq_corr; + + return true; + } + } else { + return false; + } +} + +void DP::Ph1::SynchronGenerator6OrderIter::mnaPostStep(const Matrix& leftVector) { + // update variables + mOmMech = mOmMech_corr; + mThetaMech = mThetaMech_corr; + mDelta = mDelta_corr; + mEdq = mEdq_corr; + mIdq = mIdq_corr; +} + +Matrix DP::Ph1::SynchronGenerator6OrderIter::parkTransform(Real theta, const Matrix& abcVector) { + Matrix dq0Vector(3, 1); + Matrix dqVector(2, 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; + dqVector << dq0Vector(0,0), dq0Vector(1,0); + return dqVector; +} \ No newline at end of file From f314ec77637a02386d2aeff01c901333ef9df513 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Mon, 7 Nov 2022 14:45:34 +0100 Subject: [PATCH 07/68] add DP_SG4OrderDCIM Signed-off-by: Martin Moraga --- .../Base/Base_ReducedOrderSynchronGenerator.h | 16 ++ .../include/dpsim-models/Components.h | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.h | 80 ++++++++ .../DP/DP_Ph1_SynchronGenerator4OrderIter.h | 4 +- .../DP/DP_Ph1_SynchronGenerator6OrderIter.h | 10 +- ...EMT_Ph3_ReducedOrderSynchronGeneratorVBR.h | 4 +- .../EMT/EMT_Ph3_SynchronGenerator4OrderIter.h | 11 +- .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.h | 17 +- .../dpsim-models/Solver/MNASyncGenInterface.h | 8 +- dpsim-models/src/CMakeLists.txt | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp | 145 ++++++++++++++ .../DP/DP_Ph1_SynchronGenerator4OrderIter.cpp | 116 ++++++----- .../DP/DP_Ph1_SynchronGenerator6OrderIter.cpp | 114 ++++++----- .../EMT_Ph3_SynchronGenerator4OrderIter.cpp | 140 +++++++------ .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp | 15 +- .../cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp | 4 - dpsim/examples/cxx/CMakeLists.txt | 1 + ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 10 +- .../DP_SMIB_ReducedOrderSG_LoadStep.cpp | 3 +- .../DP_SynGen4OrderDCIM_SMIB_Fault.cpp | 187 ++++++++++++++++++ ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 18 +- .../EMT_SynGen4OrderIter_SMIB_Fault.cpp | 24 +-- .../SP_SynGen4OrderDCIM_SMIB_Fault.cpp | 8 +- dpsim/include/dpsim/MNASolver.h | 1 - dpsim/src/MNASolverDirect.cpp | 2 +- 25 files changed, 690 insertions(+), 250 deletions(-) create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h create mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp create mode 100644 dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp diff --git a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h index b070f562eb..ca38149c4f 100644 --- a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h +++ b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h @@ -255,6 +255,22 @@ namespace Base { /// Signal component modelling voltage regulator and exciter std::shared_ptr mExciter; + /// + void setBaseParameters(Real nomPower, Real nomVolt, Real nomFreq); + /// Initialization for 3 Order SynGen + void setOperationalParametersPerUnit(Real nomPower, + Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, + Real Ld_t, Real Td0_t); + /// Initialization for 4 Order SynGen + 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); + /// Initialization for 6 Order SynGen + 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, + Real Ld_s, Real Lq_s, Real Td0_s, Real Tq0_s, + Real Taa=0); /// Real mTimeStep; Real mSimTime; diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 3e6edf47eb..f0fb42a090 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h new file mode 100644 index 0000000000..a35354d159 --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h @@ -0,0 +1,80 @@ +/* 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 Delayed-Current-Injection (DCIM) implementation + /// of 4th order synchronous generator model + class SynchronGenerator4OrderDCIM : + public Base::ReducedOrderSynchronGenerator, + public SharedFactory { + protected: + // ### State variables [p.u.]### + /// voltage behing the transient reactance + const Attribute::Ptr mEdq_t; + /// + Matrix mStates; + Matrix mStates_prev; + + /// state representation matrix + /// + Real mAd; + /// + Real mBd; + /// + Real mAq; + /// + Real mBq; + /// + Real mCq; + /// + Matrix mA; + /// + Matrix mA_inv; + /// + Matrix mB; + /// + Matrix mC; + + /// Transformation matrix dp->dq + MatrixComp mDpToDq; + + /// Vector to create abc vector from a component + MatrixComp mShiftVector; + + /// Park Transformation + Matrix parkTransform(Real theta, const Matrix& abcVector); + + // #### General Functions #### + /// DPecific component initialization + void specificInitialization(); + /// + void stepInPerUnit(); + + // ### MNA Section ### + /// + void mnaApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaApplyRightSideVectorStamp(Matrix& rightVector); + void mnaPostStep(const Matrix& leftVector); + + public: + /// + SynchronGenerator4OrderDCIM(String uid, String name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator4OrderDCIM(String name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(String name); + }; +} +} +} \ No newline at end of file diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h index d8d363f056..cba20e4327 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h @@ -22,6 +22,8 @@ namespace Ph1 { public MNASyncGenInterface, public SharedFactory { protected: + using MNASyncGenInterface::mNumIter; + /// sim flags bool mVoltageForm; @@ -29,7 +31,7 @@ namespace Ph1 { /// Matrix mVdq_prev; /// previous voltage behind the transient impedance (p.u.) - Matrix mEdq_t; + const Attribute::Ptr mEdq_t; Matrix mEdq_t_pred; Matrix mEdq_t_corr; /// derivative voltage behind the transient impedance (p.u.) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h index c414c6e3df..371ec07a95 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include namespace CPS { namespace DP { @@ -33,7 +33,7 @@ namespace Ph1 { /// corrected generator current at time k Matrix mIdq_corr; /// voltage behind the transient impedance at time k-1 - Matrix mEdq; + const Attribute::Ptr mEdq_t; /// predicted voltage behind the transient and subtransient impedance at time k+1 Matrix mEdq_pred; /// corrected voltage behind the transient and subtransient impedance at time k+1 @@ -73,11 +73,11 @@ namespace Ph1 { public: /// - SynchronGenerator6OrderIter(String uid, String name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator6OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); /// - SynchronGenerator6OrderIter(String name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator6OrderIter(const String& name, Logger::Level logLevel = Logger::Level::off); /// - SimPowerComp::Ptr clone(String name); + SimPowerComp::Ptr clone(const String& name); // #### General Functions #### /// 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_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h index 1cb6fbbf94..a1400afd96 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h @@ -22,8 +22,7 @@ namespace Ph3 { public MNASyncGenInterface, public SharedFactory { protected: - /// - Int mNumIterations2; + /// sim flags bool mVoltageForm; @@ -32,7 +31,7 @@ namespace Ph3 { /// Matrix mVdq0_prev; /// previous voltage behind the transient impedance (p.u.) - Matrix mEdq0_t; + const Attribute::Ptr mEdq0_t; Matrix mEdq0_t_pred; Matrix mEdq0_t_corr; /// derivative voltage behind the transient impedance (p.u.) @@ -64,11 +63,11 @@ namespace Ph3 { public: /// - SynchronGenerator4OrderIter(String uid, String name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator4OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); /// - SynchronGenerator4OrderIter(String name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator4OrderIter(const String& name, Logger::Level logLevel = Logger::Level::off); /// - SimPowerComp::Ptr clone(String name); + SimPowerComp::Ptr clone(const String& name); // #### General Functions #### /// diff --git a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h index cb152c7c9c..b17cfbe2dc 100644 --- a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h @@ -25,16 +25,29 @@ namespace Ph1 { const Attribute::Ptr mEdq_t; protected: + Matrix mStates; + Matrix mStates_prev; + /// state representation matrix /// + Real mAd; + /// + Real mBd; + /// + Real mAq; + /// + Real mBq; + /// + Real mCq; + /// Matrix mA; /// + Matrix mA_inv; + /// Matrix mB; /// Matrix mC; - /// - void calculateStateMatrix(); /// Park Transformation /// diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 9f4ffa4ed1..cf66a7e68a 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -8,9 +8,9 @@ #pragma once -#include -#include -#include +#include +#include +#include namespace CPS { /// Interface to be used by synchronous generators @@ -33,7 +33,7 @@ namespace CPS { void setTolerance(Real Tolerance) {mTolerance = Tolerance;} /// - Int mNumIter = 0; + Attribute::Ptr mNumIter; /// Int mMaxIter = 25; /// diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 760772e7bf..e12aee3444 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp + DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator4OrderIter.cpp DP/DP_Ph1_SynchronGenerator6OrderIter.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp new file mode 100644 index 0000000000..8dee1234b7 --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -0,0 +1,145 @@ +/* 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::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM + (String uid, String name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(Attribute::create("Edq0_t", mAttributes)) { + + setTerminalNumber(1); + + // model variables + **mEdq_t = Matrix::Zero(2,1); + + // + mShiftVector = Matrix::Zero(3,1); + mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; + + // initialize transformation matrix dp->dq + mDpToDq = Matrix::Zero(1,2); +} + +DP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM + (String name, Logger::Level logLevel) + : SynchronGenerator4OrderDCIM(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderDCIM::clone(String name) { + + auto copy = SynchronGenerator4OrderDCIM::make(name, mLogLevel); + return copy; +} + +void DP::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; + + // + mStates = Matrix::Zero(4,1); + mStates << **mEdq_t, **mIdq; + mStates_prev = Matrix::Zero(6,1); + + // + mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); + mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); + + mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); + mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); + mCq = 2 * mTimeStep * mEf / (2 * mTd0_t + mTimeStep); + + // Initialize matrix of state representation + mA = Matrix::Zero(4,4); + mA << 1, 0, 0, -mAd, + 0, 1, -mAq, 0, + 0, -1, mLd_t, 0, + 1, 0, 0, mLq_t; + mA_inv = mA.inverse(); + + mB = Matrix::Zero(4,6); + mB << mBd, 0, 0, mAd, 0, 0, + 0, mBq, mAq, 0, 0, 0, + 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 1, 0; + + mC = Matrix::Zero(4,1); + mC << 0, mCq, 0, 0; + + mSLog->info( + "\n--- Model specific initialization ---" + "\nInitial Ed_t (per unit): {:f}" + "\nInitial Eq_t (per unit): {:f}" + "\n--- Model DPecific initialization finished ---", + + (**mEdq_t)(0,0), + (**mEdq_t)(1,0) + ); + mSLog->flush(); +} + +void DP::Ph1::SynchronGenerator4OrderDCIM::mnaApplySystemMatrixStamp(Matrix& systemMatrix) { +} + +void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { + if (mSimTime>0.0) { + // calculate mechanical variables at t=k+1 with forward euler + **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); + **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); + **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; + **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); + } + + // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) + mStates_prev << mStates, mVdq; + mStates = mA_inv * mB * mStates_prev + mA_inv * mC * mEf; + **mEdq_t << mStates(0,0), mStates(1,0); + **mIdq << mStates(2,0), mStates(3,0); + + // convert currents into the abc reference frame + mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; +} + +void DP::Ph1::SynchronGenerator4OrderDCIM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); +} + +void DP::Ph1::SynchronGenerator4OrderDCIM::mnaPostStep(const Matrix& leftVector) { + + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage into dq reference frame + MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix Vabc = Matrix(3,1); + Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); + **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; + + mSimTime = mSimTime + mTimeStep; +} + +Matrix DP::Ph1::SynchronGenerator4OrderDCIM::parkTransform(Real theta, const Matrix& abcVector) { + Matrix dq0Vector(3, 1); + Matrix dqVector(2, 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; + dqVector << dq0Vector(0,0), dq0Vector(1,0); + return dqVector; +} \ No newline at end of file diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp index 2f954f0d3f..22d68d8f54 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp @@ -6,13 +6,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *********************************************************************************/ -#include +#include using namespace CPS; DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter (String uid, String name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(Attribute::create("Edq0_t", mAttributes)) { mPhaseType = PhaseType::Single; setTerminalNumber(1); @@ -21,9 +22,7 @@ DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter mVoltageForm = false; // model variables - mIntfVoltage = MatrixComp::Zero(1, 1); - mIntfCurrent = MatrixComp::Zero(1, 1); - mEdq_t = Matrix::Zero(2,1); + **mEdq_t = Matrix::Zero(2,1); mEdq_t_pred = Matrix::Zero(2,1); mEdq_t_corr = Matrix::Zero(2,1); mdEdq_t = Matrix::Zero(2,1); @@ -33,9 +32,8 @@ DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter mShiftVector = Matrix::Zero(3,1); mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - // Register attributes - addAttribute("NIterations", &mNumIter , Flags::read); - addAttribute("Edq0_t", &mEdq_t, Flags::read); + // Initialize attributes + mNumIter = Attribute::create("NIterations", mAttributes, 0); } DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter @@ -58,8 +56,8 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { calculateStateMatrix(); // 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; + (**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 transformation matrix dp->dq mDpToDq = Matrix::Zero(1,2); @@ -72,8 +70,8 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - mEdq_t(0,0), - mEdq_t(1,0), + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), mMaxIter, mTolerance ); @@ -109,113 +107,111 @@ void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { //predict mechanical variables at t=k+1 if (mSimTime>0.0) { - mElecTorque = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); - mdOmMech = 1 / (2.* mH) * (mMechTorque - mElecTorque); - mOmMech_pred = mOmMech + mTimeStep * mdOmMech; + **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); + mdOmMech = 1 / (2.* mH) * (**mMechTorque - **mElecTorque); + mOmMech_pred = **mOmMech + mTimeStep * mdOmMech; mdDelta = (mOmMech_pred - 1.) * mBase_OmMech; - mDelta_pred = mDelta + mTimeStep * mdDelta; - mThetaMech_pred = mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; + mDelta_pred = **mDelta + mTimeStep * mdDelta; + mThetaMech_pred = **mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; } else { mdOmMech = 0; - mOmMech_pred = mOmMech; + mOmMech_pred = **mOmMech; mdDelta = 0; - mDelta_pred = mDelta; - mThetaMech_pred = mThetaMech; + mDelta_pred = **mDelta; + mThetaMech_pred = **mThetaMech; } //predict voltage behind transient reactance if (mVoltageForm) - mdEdq_t = mA * mEdq_t + mB * mVdq + mC; + mdEdq_t = mA * **mEdq_t + mB * **mVdq + mC; else - mdEdq_t = mA * mEdq_t + mB * mIdq + mC; - mEdq_t_pred = mEdq_t + mTimeStep * mdEdq_t; + mdEdq_t = mA * **mEdq_t + mB * **mIdq + mC; + mEdq_t_pred = **mEdq_t + mTimeStep * mdEdq_t; // predict armature currents for at t=k+1 - mIdq(0,0) = (mEdq_t_pred(1,0) - mVdq(1,0) ) / mLd_t; - mIdq(1,0) = (mVdq(0,0) - mEdq_t_pred(0,0) ) / mLq_t; + (**mIdq)(0,0) = (mEdq_t_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdq_t_pred(0,0) ) / mLq_t; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); - mIntfCurrent(0,0) = (mDpToDq * mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, matrixNodeIndex(0,0), mIntfCurrent(0, 0)); + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); } void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { // corrector step (trapezoidal rule) - mNumIter = mNumIter + 1; - if (mNumIter==1) + **mNumIter = **mNumIter + 1; + if (**mNumIter == 1) return; //predict mechanical variables if (mSimTime>0.0) { - mElecTorque_corr = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); - mdOmMech_corr = 1 / (2.* mH) * (mMechTorque - mElecTorque_corr); - mOmMech_corr = mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); + mElecTorque_corr = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); + mdOmMech_corr = 1 / (2.* mH) * (**mMechTorque - mElecTorque_corr); + mOmMech_corr = **mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); mdDelta_corr = (mOmMech_corr - 1.) * mBase_OmMech; - mDelta_corr = mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); - mThetaMech_corr = mThetaMech + mTimeStep / 2. *(mOmMech + mOmMech_corr) * mBase_OmMech; + mDelta_corr = **mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); + mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_corr) * mBase_OmMech; } else { - mElecTorque_corr = mElecTorque; + mElecTorque_corr = **mElecTorque; mdOmMech_corr = 0; - mOmMech_corr = mOmMech; + mOmMech_corr = **mOmMech; mdDelta_corr = 0; - mDelta_corr = mDelta; - mThetaMech_corr = mThetaMech; + mDelta_corr = **mDelta; + mThetaMech_corr = **mThetaMech; } //predict voltage behind transient reactance if (mVoltageForm) - mdEdq_t_corr = mA * mEdq_t + mB * mVdq + mC; + mdEdq_t_corr = mA * **mEdq_t + mB * **mVdq + mC; else - mdEdq_t_corr = mA * mEdq_t + mB * mIdq + mC; - mEdq_t_corr = mEdq_t + mTimeStep / 2 * (mdEdq_t + mdEdq_t_corr); + mdEdq_t_corr = mA * **mEdq_t + mB * **mIdq + mC; + mEdq_t_corr = **mEdq_t + mTimeStep / 2 * (mdEdq_t + mdEdq_t_corr); // armature currents for at t=k+1 - mIdq(0,0) = (mEdq_t_corr(1,0) - mVdq(1,0) ) / mLd_t; - mIdq(1,0) = (mVdq(0,0) - mEdq_t_corr(0,0) ) / mLq_t; + (**mIdq)(0,0) = (mEdq_t_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdq_t_corr(0,0) ) / mLq_t; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); - mIntfCurrent(0,0) = (mDpToDq * mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; // stamp currents - mnaApplyRightSideVectorStamp(mRightVector); + mnaApplyRightSideVectorStamp(**mRightVector); } void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVector) { - mVdq_prev = mVdq; - mIntfVoltage(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + mVdq_prev = **mVdq; + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage into dq reference frame - MatrixComp Vabc_ = mIntfVoltage(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - if (mNumIter==0) { - mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; + if (**mNumIter == 0) { + **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; } else { - mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; } } bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { - if (mNumIter==1) { + if (**mNumIter == 0) { // if no corrector step has been performed yet - //mNumIter = 1; return true; } - Matrix voltageDifference = mVdq - mVdq_prev; + Matrix voltageDifference = **mVdq - mVdq_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { - if (mNumIter > mMaxIter) { + if (**mNumIter == mMaxIter) { return false; } else { - //mNumIter = mNumIter + 1; return true; } } else { @@ -226,10 +222,10 @@ bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { void DP::Ph1::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { // update variables - mEdq_t = mEdq_t_corr; - mOmMech = mOmMech_corr; - mThetaMech = mThetaMech_corr; - mDelta = mDelta_corr; + **mEdq_t = mEdq_t_corr; + **mOmMech = mOmMech_corr; + **mThetaMech = mThetaMech_corr; + **mDelta = mDelta_corr; } Matrix DP::Ph1::SynchronGenerator4OrderIter::parkTransform(Real theta, const Matrix& abcVector) { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp index 99fabe13cb..a452068cf5 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp @@ -6,13 +6,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *********************************************************************************/ -#include +#include using namespace CPS; DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter - (String uid, String name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + (const String& uid, const String& name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(Attribute::create("Edq0_t", mAttributes)) { mPhaseType = PhaseType::Single; setTerminalNumber(1); @@ -20,14 +21,10 @@ DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter // model flags (deprecated) mVoltageForm = false; - // model variables - mIntfVoltage = MatrixComp::Zero(1, 1); - mIntfCurrent = MatrixComp::Zero(1, 1); - // Initialize matrix mIdq_pred = Matrix::Zero(2,1); mIdq_corr = Matrix::Zero(2,1); - mEdq = Matrix::Zero(4,1); + **mEdq_t = Matrix::Zero(4,1); mEdq_pred = Matrix::Zero(4,1); mEdq_corr = Matrix::Zero(4,1); @@ -35,17 +32,16 @@ DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter mShiftVector = Matrix::Zero(3,1); mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - // Register attributes - addAttribute("NIterations", &mNumIter , Flags::read); - addAttribute("Edq0_t", &mEdq, Flags::read); + // Initialize attributes + mNumIter = Attribute::create("NIterations", mAttributes, 0); } DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter - (String name, Logger::Level logLevel) + (const String& name, Logger::Level logLevel) : SynchronGenerator6OrderIter(name, name, logLevel) { } -SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderIter::clone(String name) { +SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderIter::clone(const String& name) { auto copy = SynchronGenerator6OrderIter::make(name, mLogLevel); return copy; @@ -63,10 +59,10 @@ void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { calculateStateMatrix(); // initial voltage behind the transient reactance in the dq0 reference frame - mEdq(0,0) = (mLq - mLq_t) * mIdq(1,0); - mEdq(1,0) = - (mLd- mLd_t) * mIdq(0,0) + mEf; - mEdq(2,0) = mEdq(0,0) + (mLq_t - mLq_s) * mIdq(1,0); - mEdq(3,0) = mEdq(1,0) - (mLd_t - mLd_s) * mIdq(0,0); + (**mEdq_t)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); + (**mEdq_t)(1,0) = - (mLd- mLd_t) * (**mIdq)(0,0) + mEf; + (**mEdq_t)(2,0) = (**mEdq_t)(0,0) + (mLq_t - mLq_s) * (**mIdq)(1,0); + (**mEdq_t)(3,0) = (**mEdq_t)(1,0) - (mLd_t - mLd_s) * (**mIdq)(0,0); // initialize transformation matrix dp->dq mDpToDq = Matrix::Zero(1,2); @@ -81,10 +77,10 @@ void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - mEdq(0,0), - mEdq(1,0), - mEdq(2,0), - mEdq(3,0), + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), + (**mEdq_t)(2,0), + (**mEdq_t)(3,0), mMaxIter, mTolerance ); @@ -120,97 +116,97 @@ void DP::Ph1::SynchronGenerator6OrderIter::calculateStateMatrix() { void DP::Ph1::SynchronGenerator6OrderIter::stepInPerUnit() { // set number of iteratios equal to zero - mNumIter = 0; + **mNumIter = 0; // Predictor step (backward euler) if (mSimTime>0.0) { // calculate electrical torque at t=k-1 - mElecTorque = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); + **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); // predict mechanical variables at t=k - mOmMech_pred = mOmMech + mTimeStep / (2 * mH) * (mMechTorque - mElecTorque); - mDelta_pred = mDelta + mTimeStep * mBase_OmMech * (mOmMech - 1); - mThetaMech_pred = mThetaMech + mTimeStep * mOmMech * mBase_OmMech; + mOmMech_pred = **mOmMech + mTimeStep / (2 * mH) * (**mMechTorque - **mElecTorque); + mDelta_pred = **mDelta + mTimeStep * mBase_OmMech * (**mOmMech - 1); + mThetaMech_pred = **mThetaMech + mTimeStep * **mOmMech * mBase_OmMech; } else { - mOmMech_pred = mOmMech; - mDelta_pred = mDelta; - mThetaMech_pred = mThetaMech; + mOmMech_pred = **mOmMech; + mDelta_pred = **mDelta; + mThetaMech_pred = **mThetaMech; } //predict voltage behind transient reactance - mEdq_pred = mA * mEdq + mB * mIdq + mC * mEf; + mEdq_pred = mA * (**mEdq_t) + mB * **mIdq + mC * mEf; // predict armature currents for at t=k+1 - mIdq_pred(0,0) = (mEdq_pred(3,0) - mVdq(1,0) ) / mLd_s; - mIdq_pred(1,0) = (mVdq(0,0) - mEdq_pred(2,0) ) / mLq_s; + mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; + mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(2,0) ) / mLq_s; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); - mIntfCurrent(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator6OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, matrixNodeIndex(0,0), mIntfCurrent(0, 0)); + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); } void DP::Ph1::SynchronGenerator6OrderIter::correctorStep() { // corrector step (trapezoidal rule) - mNumIter = mNumIter + 1; + **mNumIter = **mNumIter + 1; if (mSimTime>0.0) { // calculate electrical torque at t=k - mElecTorque_corr = mVdq(0,0) * mIdq(0,0) + mVdq(1,0) * mIdq(1,0); + mElecTorque_corr = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); // correct mechanical variables at t=k - mOmMech_corr = mOmMech + mTimeStep / (4. * mH) * (2 * mMechTorque - mElecTorque - mElecTorque_corr); - mDelta_corr = mDelta + mTimeStep / 2. * mBase_OmMech * (mOmMech + mOmMech_pred - 2); - mThetaMech_corr = mThetaMech + mTimeStep / 2. *(mOmMech + mOmMech_pred) * mBase_OmMech; + mOmMech_corr = **mOmMech + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorque_corr); + mDelta_corr = **mDelta + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMech_pred - 2); + mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_pred) * mBase_OmMech; } else { - mElecTorque_corr = mElecTorque; + mElecTorque_corr = **mElecTorque; mOmMech_corr = mOmMech_pred; mDelta_corr = mDelta_pred; mThetaMech_corr = mThetaMech_pred; } //predict voltage behind transient reactance - mEdq_corr = mA_prev * mEdq + mA_corr * mEdq_pred + mB_corr * (mIdq + mIdq_pred) + mC * mEf ; + mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC * mEf ; // armature currents for at t=k+1 - mIdq_corr(0,0) = (mEdq_corr(1,0) - mVdq(1,0) ) / mLd_t; - mIdq_corr(1,0) = (mVdq(0,0) - mEdq_corr(0,0) ) / mLq_t; + mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); - mIntfCurrent(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; // stamp currents - mnaApplyRightSideVectorStamp(mRightVector); + mnaApplyRightSideVectorStamp(**mRightVector); } void DP::Ph1::SynchronGenerator6OrderIter::updateVoltage(const Matrix& leftVector) { - mVdq_prev = mVdq; - mIntfVoltage(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + mVdq_prev = **mVdq; + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage into dq reference frame - MatrixComp Vabc_ = mIntfVoltage(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - if (mNumIter==0) { - mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; + if (**mNumIter == 0) { + **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; } else { - mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; } } bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { - if (mNumIter==0) { + if (**mNumIter == 0) { // if no corrector step has been performed yet return true; } - Matrix voltageDifference = mVdq - mVdq_prev; + Matrix voltageDifference = **mVdq - mVdq_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { - if (mNumIter == mMaxIter) { + if (**mNumIter == mMaxIter) { return false; } else { // set predicted values equal to corrected values for the next iteration @@ -229,11 +225,11 @@ bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { void DP::Ph1::SynchronGenerator6OrderIter::mnaPostStep(const Matrix& leftVector) { // update variables - mOmMech = mOmMech_corr; - mThetaMech = mThetaMech_corr; - mDelta = mDelta_corr; - mEdq = mEdq_corr; - mIdq = mIdq_corr; + **mOmMech = mOmMech_corr; + **mThetaMech = mThetaMech_corr; + **mDelta = mDelta_corr; + **mEdq_t = mEdq_corr; + **mIdq = mIdq_corr; } Matrix DP::Ph1::SynchronGenerator6OrderIter::parkTransform(Real theta, const Matrix& abcVector) { diff --git a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp index 67884c32d5..e8bdd684db 100644 --- a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp @@ -6,13 +6,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *********************************************************************************/ -#include +#include using namespace CPS; EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter - (String uid, String name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + (const String& uid, const String& name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq0_t(Attribute::create("Edq0_t", mAttributes)) { mPhaseType = PhaseType::ABC; setTerminalNumber(1); @@ -21,25 +22,22 @@ EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter mVoltageForm = false; // model variables - mIntfVoltage = Matrix::Zero(3,1); - mIntfCurrent = Matrix::Zero(3,1); - mEdq0_t = Matrix::Zero(3,1); + **mEdq0_t = Matrix::Zero(3,1); mEdq0_t_pred = Matrix::Zero(3,1); mEdq0_t_corr = Matrix::Zero(3,1); mdEdq0_t = Matrix::Zero(3,1); mdEdq0_t_corr = Matrix::Zero(3,1); - // Register attributes - addAttribute("NIterations", &mNumIterations2 , Flags::read); - addAttribute("Edq0_t", &mEdq0_t, Flags::read); + // Initialize attributes + mNumIter = Attribute::create("NIterations", mAttributes, 0); } EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter - (String name, Logger::Level logLevel) + (const String& name, Logger::Level logLevel) : SynchronGenerator4OrderIter(name, name, logLevel) { } -SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderIter::clone(String name) { +SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderIter::clone(const String& name) { auto copy = SynchronGenerator4OrderIter::make(name, mLogLevel); return copy; @@ -54,8 +52,8 @@ void EMT::Ph3::SynchronGenerator4OrderIter::specificInitialization() { calculateStateMatrix(); // 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)(0,0) = (**mVdq0)(0,0) - (**mIdq0)(1,0) * mLq_t; + (**mEdq0_t)(1,0) = (**mVdq0)(1,0) + (**mIdq0)(0,0) * mLd_t; mSLog->info( "\n--- Model specific initialization ---" @@ -65,8 +63,8 @@ void EMT::Ph3::SynchronGenerator4OrderIter::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - mEdq0_t(0,0), - mEdq0_t(1,0), + (**mEdq0_t)(0,0), + (**mEdq0_t)(1,0), mMaxIter, mTolerance ); @@ -102,120 +100,119 @@ void EMT::Ph3::SynchronGenerator4OrderIter::calculateStateMatrix() { void EMT::Ph3::SynchronGenerator4OrderIter::stepInPerUnit() { // set number of iteratios equal to zero - mNumIter = 0; + **mNumIter = **mNumIter + 1; // Predictor step (euler) //predict mechanical variables at t=k+1 if (mSimTime>0.0) { - mElecTorque = mVdq0(0,0) * mIdq0(0,0) + mVdq0(1,0) * mIdq0(1,0); - mdOmMech = 1 / (2.* mH) * (mMechTorque - mElecTorque); - mOmMech_pred = mOmMech + mTimeStep * mdOmMech; + **mElecTorque = (**mVdq0)(0,0) * (**mIdq0)(0,0) + (**mVdq0)(1,0) * (**mIdq0)(1,0); + mdOmMech = 1 / (2.* mH) * (**mMechTorque - **mElecTorque); + mOmMech_pred = **mOmMech + mTimeStep * mdOmMech; mdDelta = (mOmMech_pred - 1.) * mBase_OmMech; - mDelta_pred = mDelta + mTimeStep * mdDelta; - mThetaMech_pred = mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; + mDelta_pred = **mDelta + mTimeStep * mdDelta; + mThetaMech_pred = **mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; } else { mdOmMech = 0; - mOmMech_pred = mOmMech; + mOmMech_pred = **mOmMech; mdDelta = 0; - mDelta_pred = mDelta; - mThetaMech_pred = mThetaMech; + mDelta_pred = **mDelta; + mThetaMech_pred = **mThetaMech; } //predict voltage behind transient reactance if (mVoltageForm) - mdEdq0_t = mA * mEdq0_t + mB * mVdq0 + mC; + mdEdq0_t = mA * **mEdq0_t + mB * **mVdq0 + mC; else - mdEdq0_t = mA * mEdq0_t + mB * mIdq0 + mC; - mEdq0_t_pred = mEdq0_t + mTimeStep * mdEdq0_t; + mdEdq0_t = mA * **mEdq0_t + mB * **mIdq0 + mC; + mEdq0_t_pred = **mEdq0_t + mTimeStep * mdEdq0_t; // predict armature currents for at t=k+1 - mIdq0(0,0) = (mEdq0_t_pred(1,0) - mVdq0(1,0) ) / mLd_t; - mIdq0(1,0) = (mVdq0(0,0) - mEdq0_t_pred(0,0) ) / mLq_t; - mIdq0(2,0) = 0.0; + (**mIdq0)(0,0) = (mEdq0_t_pred(1,0) - (**mVdq0)(1,0) ) / mLd_t; + (**mIdq0)(1,0) = ((**mVdq0)(0,0) - mEdq0_t_pred(0,0) ) / mLq_t; + (**mIdq0)(2,0) = 0.0; // convert currents into the abc domain - mIntfCurrent = inverseParkTransform(mThetaMech_pred, mIdq0); - mIntfCurrent = mIntfCurrent * mBase_I; + **mIntfCurrent = inverseParkTransform(mThetaMech_pred, **mIdq0); + **mIntfCurrent = **mIntfCurrent * mBase_I; } void EMT::Ph3::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(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)); + 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::SynchronGenerator4OrderIter::correctorStep() { // corrector step (trapezoidal rule) - if (mNumIter==0) + if (**mNumIter == 1) return; //predict mechanical variables if (mSimTime>0.0) { - mElecTorque_corr = mVdq0(0,0) * mIdq0(0,0) + mVdq0(1,0) * mIdq0(1,0); - mdOmMech_corr = 1 / (2.* mH) * (mMechTorque - mElecTorque_corr); - mOmMech_corr = mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); + mElecTorque_corr = (**mVdq0)(0,0) * (**mIdq0)(0,0) + (**mVdq0)(1,0) * (**mIdq0)(1,0); + mdOmMech_corr = 1 / (2.* mH) * (**mMechTorque - mElecTorque_corr); + mOmMech_corr = **mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); mdDelta_corr = (mOmMech_corr - 1.) * mBase_OmMech; - mDelta_corr = mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); - mThetaMech_corr = mThetaMech + mTimeStep / 2. *(mOmMech + mOmMech_corr) * mBase_OmMech; + mDelta_corr = **mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); + mThetaMech_corr = **mThetaMech + mTimeStep / 2. * (**mOmMech + mOmMech_corr) * mBase_OmMech; } else { - mElecTorque_corr = mElecTorque; + mElecTorque_corr = **mElecTorque; mdOmMech_corr = 0; - mOmMech_corr = mOmMech; + mOmMech_corr = **mOmMech; mdDelta_corr = 0; - mDelta_corr = mDelta; - mThetaMech_corr = mThetaMech; + mDelta_corr = **mDelta; + mThetaMech_corr = **mThetaMech; } //predict voltage behind transient reactance if (mVoltageForm) - mdEdq0_t_corr = mA * mEdq0_t + mB * mVdq0 + mC; + mdEdq0_t_corr = mA * **mEdq0_t + mB * **mVdq0 + mC; else - mdEdq0_t_corr = mA * mEdq0_t + mB * mIdq0 + mC; - mEdq0_t_corr = mEdq0_t + mTimeStep / 2 * (mdEdq0_t + mdEdq0_t_corr); + mdEdq0_t_corr = mA * **mEdq0_t + mB * **mIdq0 + mC; + mEdq0_t_corr = **mEdq0_t + mTimeStep / 2 * (mdEdq0_t + mdEdq0_t_corr); // armature currents for at t=k+1 - mIdq0(0,0) = (mEdq0_t_corr(1,0) - mVdq0(1,0) ) / mLd_t; - mIdq0(1,0) = (mVdq0(0,0) - mEdq0_t_corr(0,0) ) / mLq_t; + (**mIdq0)(0,0) = (mEdq0_t_corr(1,0) - (**mVdq0)(1,0) ) / mLd_t; + (**mIdq0)(1,0) = ((**mVdq0)(0,0) - mEdq0_t_corr(0,0) ) / mLq_t; // convert currents into the abc domain - mIntfCurrent = inverseParkTransform(mThetaMech_corr, mIdq0); - mIntfCurrent = mIntfCurrent * mBase_I; + **mIntfCurrent = inverseParkTransform(mThetaMech_corr, **mIdq0); + **mIntfCurrent = **mIntfCurrent * mBase_I; // stamp currents - mnaApplyRightSideVectorStamp(mRightVector); + mnaApplyRightSideVectorStamp(**mRightVector); } void EMT::Ph3::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVector) { - mVdq0_prev = mVdq0; + mVdq0_prev = **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)); + (**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 - if (mNumIter==0) { - mVdq0 = parkTransform(mThetaMech_pred, mIntfVoltage); + if (**mNumIter == 0) { + **mVdq0 = parkTransform(mThetaMech_pred, **mIntfVoltage); } else { - mVdq0 = parkTransform(mThetaMech_corr, mIntfVoltage); + **mVdq0 = parkTransform(mThetaMech_corr, **mIntfVoltage); } - mVdq0 = mVdq0 / mBase_V; + **mVdq0 = **mVdq0 / mBase_V; } bool EMT::Ph3::SynchronGenerator4OrderIter::checkVoltageDifference() { - if (mNumIter==0) { + if (**mNumIter == 0) { // if no corrector step has been performed yet - mNumIter = 1; + **mNumIter = 1; return true; } - Matrix voltageDifference = mVdq0 - mVdq0_prev; + Matrix voltageDifference = **mVdq0 - mVdq0_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { - if (mNumIter > mMaxIter) { + if (**mNumIter == mMaxIter) { return false; } else { - mNumIter = mNumIter + 1; return true; } } else @@ -224,13 +221,10 @@ bool EMT::Ph3::SynchronGenerator4OrderIter::checkVoltageDifference() { void EMT::Ph3::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { // update variables - mEdq0_t = mEdq0_t_corr; - mOmMech = mOmMech_corr; - mThetaMech = mThetaMech_corr; - mDelta = mDelta_corr; - - // - mNumIterations2 = mNumIter; + **mEdq0_t = mEdq0_t_corr; + **mOmMech = mOmMech_corr; + **mThetaMech = mThetaMech_corr; + **mDelta = mDelta_corr; } Matrix EMT::Ph3::SynchronGenerator4OrderIter::parkTransform(Real theta, const Matrix& abcVector) { diff --git a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp index ae732762c1..9abd298ee2 100644 --- a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -33,6 +33,19 @@ void SP::Ph1::SynchronGenerator4OrderDCIM::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; + // + mStates = Matrix::Zero(4,1); + mStates << **mEdq_t, **mIdq; + mStates_prev = Matrix::Zero(6,1); + + // + mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); + mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); + + mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); + mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); + mCq = 2 * mTimeStep * mEf / (2 * mTd0_t + mTimeStep); + // Initialize matrix of state representation mA = Matrix::Zero(2,2); mB = Matrix::Zero(2,2); @@ -43,7 +56,7 @@ void SP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" "\nInitial Eq_t (per unit): {:f}" - "\n--- Model specific initialization finished ---", + "\n--- Model SPecific initialization finished ---", (**mEdq_t)(0,0), (**mEdq_t)(1,0) diff --git a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp index 37f9accc66..529bbdb3d1 100644 --- a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp +++ b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp @@ -149,11 +149,7 @@ int main(int argc, char *argv[]) { // log node voltage auto logger = DataLogger::make(simName, true, logDownSampling); for (auto node : sys.mNodes) -<<<<<<< HEAD logger->logAttribute(node->name() + ".V", node->attribute("v")); -======= - logger->addAttribute(node->name() + ".V", node->attribute("v")); ->>>>>>> 15f7252d (add wscc reduced order examples) // log generator vars for (auto comp : sys.mComponents) { diff --git a/dpsim/examples/cxx/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 09e55c107b..654e2f8911 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -75,6 +75,7 @@ set(CIRCUIT_SOURCES Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp + Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp # SMIB Reduced Order - Load step diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index f26d7e23f2..b3af1dc9f6 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -102,8 +102,8 @@ int main(int argc, char* argv[]) { // Logging auto loggerPF = DataLogger::make(simNamePF); - loggerPF->addAttribute("v1", n1PF->attribute("v")); - loggerPF->addAttribute("v2", n2PF->attribute("v")); + loggerPF->logAttribute("v1", n1PF->attribute("v")); + loggerPF->logAttribute("v2", n2PF->attribute("v")); // Simulation Simulation simPF(simNamePF, logLevel); @@ -169,11 +169,11 @@ int main(int argc, char* argv[]) { // log node voltage for (auto node : systemDP.mNodes) - logger->addAttribute(node->name() + ".V", node->attribute("v")); + logger->logAttribute(node->name() + ".V", node->attribute("v")); // log generator vars - logger->addAttribute(genDP->name() + ".Te", genDP->attribute("Te")); - logger->addAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); + logger->logAttribute(genDP->name() + ".Te", genDP->attribute("Te")); + logger->logAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); // load step event std::shared_ptr loadStepEvent = Examples::Events::createEventAddPowerConsumption("n1DP", std::round(loadStepEventTime/timeStep)*timeStep, gridParams.loadStepActivePower, systemDP, Domain::DP, logger); diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp index 82ea168219..b61be51826 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp @@ -158,7 +158,8 @@ int main(int argc, char* argv[]) { // Logging // log node voltage auto logger = DataLogger::make(simName, true, logDownSampling); - for (auto node : systemDP.mNodes) logger->logAttribute(node->name() + ".V", node->attribute("v")); + for (auto node : systemDP.mNodes) + logger->logAttribute(node->name() + ".V", node->attribute("v")); // log generator vars logger->logAttribute(genDP->name() + ".Tm", genDP->attribute("Tm")); diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp new file mode 100644 index 0000000000..ef242cb000 --- /dev/null +++ b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp @@ -0,0 +1,187 @@ +#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; + +void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, + Real startTimeFault, Real endTimeFault, Real logDownSampling, Real switchOpen, + Real switchClosed, 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(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 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), 0.0, 0.0}; + std::vector initialVoltage_n2{ n2PF->voltage()(0,0), 0.0, 0.0}; + auto n1DP = SimNode::make("n1DP", PhaseType::Single, initialVoltage_n1); + auto n2DP = SimNode::make("nDSP", PhaseType::Single, initialVoltage_n2); + + // Components + auto genDP = DP::Ph1::SynchronGenerator4OrderDCIM::make("SynGen", Logger::Level::debug); + genDP->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); + genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); + + //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); + + //Breaker + auto fault = DP::Ph1::Switch::make("Br_fault", logLevel); + fault->setParameters(switchOpen, switchClosed); + fault->open(); + + // Topology + genDP->connect({ n1DP }); + lineDP->connect({ n1DP, n2DP }); + extnetDP->connect({ n2DP }); + fault->connect({DP::SimNode::GND, n1DP}); + auto systemDP = SystemTopology(GridParams.nomFreq, + SystemNodeList{n1DP, n2DP}, + SystemComponentList{genDP, lineDP, extnetDP, fault}); + + // Logging + auto loggerDP = DataLogger::make(simNameDP, true, logDownSampling); + //loggerDP->logAttribute("v_slack", extnetDP->attribute("v_intf")); + //loggerDP->logAttribute("i_slack", extnetDP->attribute("i_intf")); + //loggerDP->logAttribute("v_gen", genDP->attribute("v_intf")); + //loggerDP->logAttribute("i_gen", genDP->attribute("i_intf")); + loggerDP->logAttribute("Te", genDP->attribute("Te")); + loggerDP->logAttribute("delta", genDP->attribute("delta")); + loggerDP->logAttribute("w_r", genDP->attribute("w_r")); + loggerDP->logAttribute("Edq0", genDP->attribute("Edq_t")); + loggerDP->logAttribute("Vdq0", genDP->attribute("Vdq0")); + loggerDP->logAttribute("Idq0", genDP->attribute("Idq0")); + + Simulation simDP(simNameDP, logLevel); + simDP.doInitFromNodesAndTerminals(true); + simDP.setSystem(systemDP); + simDP.setTimeStep(timeStep); + simDP.setFinalTime(finalTime); + simDP.setDomain(Domain::DP); + simDP.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simDP.addLogger(loggerDP); + //simDP.doSystemMatrixRecomputation(true); + + // Events + auto sw1 = SwitchEvent::make(startTimeFault, fault, true); + simDP.addEvent(sw1); + + auto sw2 = SwitchEvent::make(endTimeFault, fault, false); + simDP.addEvent(sw2); + + simDP.run(); +} + +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 = 10e-9; + Real H = syngenKundur.H; + Real finalTime = 3; + + // Command line args processing + CommandLineArgs args(argc, argv); + std::string stepSize_str = ""; + std::string inertia_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("Inertia") != args.options.end()) { + H = args.getOptionReal("Inertia"); + inertia_str = "_Inertia_" + std::to_string(H); + } + } + + Real logDownSampling; + if (timeStep<10e-6) + logDownSampling = floor((10e-6) / timeStep); + else + logDownSampling = 1.0; + Logger::Level logLevel = Logger::Level::off; + std::string simName = "DP_SynGen4OrderDCIM_SMIB_Fault" + stepSize_str + inertia_str; + DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, + logDownSampling, SwitchOpen, SwitchClosed, logLevel); +} \ 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 index 6349c714b0..154e3f0ede 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -89,8 +89,8 @@ int main(int argc, char* argv[]) { // Logging auto loggerPF = DataLogger::make(simNamePF); - loggerPF->addAttribute("v1", n1PF->attribute("v")); - loggerPF->addAttribute("v2", n2PF->attribute("v")); + loggerPF->logAttribute("v1", n1PF->attribute("v")); + loggerPF->logAttribute("v2", n2PF->attribute("v")); // Simulation Simulation simPF(simNamePF, logLevel); @@ -160,15 +160,15 @@ int main(int argc, char* argv[]) { // log node voltage auto logger = DataLogger::make(simName, true, logDownSampling); for (auto node : systemEMT.mNodes) - logger->addAttribute(node->name() + ".V", node->attribute("v")); + logger->logAttribute(node->name() + ".V", node->attribute("v")); // log generator vars - //logger->addAttribute(genEMT->name() + ".Tm", genEMT->attribute("Tm")); - logger->addAttribute(genEMT->name() + ".Te", genEMT->attribute("Te")); - logger->addAttribute(genEMT->name() + ".omega", genEMT->attribute("w_r")); - logger->addAttribute(genEMT->name() + ".delta", genEMT->attribute("delta")); - logger->addAttribute(genEMT->name() + ".NIterations", genEMT->attribute("NIterations")); - //logger->addAttribute(genEMT->name() + ".theta", genEMT->attribute("Theta")); + //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); diff --git a/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp index e076179e30..2113b3f54a 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp @@ -66,8 +66,8 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea // Logging auto loggerPF = DataLogger::make(simNamePF); - loggerPF->addAttribute("v1", n1PF->attribute("v")); - loggerPF->addAttribute("v2", n2PF->attribute("v")); + loggerPF->logAttribute("v1", n1PF->attribute("v")); + loggerPF->logAttribute("v2", n2PF->attribute("v")); // Simulation Simulation simPF(simNamePF, Logger::Level::debug); @@ -144,16 +144,16 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea // Logging auto loggerEMT = DataLogger::make(simNameEMT, true, logDownSampling); - //loggerEMT->addAttribute("v2", n2EMT->attribute("v")); - //loggerEMT->addAttribute("v_gen", genEMT->attribute("v_intf")); - //loggerEMT->addAttribute("i_gen", genEMT->attribute("i_intf")); - loggerEMT->addAttribute("Etorque", genEMT->attribute("Te")); - loggerEMT->addAttribute("delta", genEMT->attribute("delta")); - loggerEMT->addAttribute("w_r", genEMT->attribute("w_r")); - loggerEMT->addAttribute("Vdq0", genEMT->attribute("Vdq0")); - loggerEMT->addAttribute("Idq0", genEMT->attribute("Idq0")); - loggerEMT->addAttribute("Edq0", genEMT->attribute("Edq0_t")); - loggerEMT->addAttribute("NIterations", genEMT->attribute("NIterations")); + //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); diff --git a/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp index ba7f2b8695..50656cc91c 100644 --- a/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp @@ -30,8 +30,8 @@ int main(int argc, char* argv[]) { } Real logDownSampling; - if (timeStep<100e-6) - logDownSampling = floor(100e-6 / timeStep); + if (timeStep<10e-6) + logDownSampling = floor(10e-6 / timeStep); else logDownSampling = 1.0; Logger::Level logLevel = Logger::Level::off; @@ -142,11 +142,11 @@ int main(int argc, char* argv[]) { 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("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("Vdq0", genSP->attribute("Vdq0")); loggerSP->logAttribute("Idq0", genSP->attribute("Idq0")); Simulation simSP(simNameSP, logLevel); diff --git a/dpsim/include/dpsim/MNASolver.h b/dpsim/include/dpsim/MNASolver.h index 226387e1a0..8c748732fc 100644 --- a/dpsim/include/dpsim/MNASolver.h +++ b/dpsim/include/dpsim/MNASolver.h @@ -17,7 +17,6 @@ #include #include #include -<<<<<<< HEAD:dpsim/include/dpsim/MNASolver.h #include #include #include diff --git a/dpsim/src/MNASolverDirect.cpp b/dpsim/src/MNASolverDirect.cpp index 774cb1a87c..2c7fa5e04b 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -261,7 +261,7 @@ void MnaSolverDirect::solve(Real time, Int timeStepCount) { for (auto syncGen : mSyncGen) //update voltages - syncGen->updateVoltage(mLeftSideVector); + syncGen->updateVoltage(**mLeftSideVector); // check if there is sync generators that need iterate int count=0; From c8c9d847e50b474653eae1338c6ddbef21feef85 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Wed, 16 Nov 2022 16:11:31 +0100 Subject: [PATCH 08/68] refactor iterative SG 4Order Signed-off-by: Martin Moraga --- .../include/dpsim-models/Components.h | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderIter.h | 78 ++------ .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.h | 96 +++++++++ .../DP/DP_Ph1_SynchronGenerator6OrderIter.h | 76 +------ .../DP/DP_Ph1_SynchronGeneratorIter.h | 58 ++++++ dpsim-models/include/dpsim-models/Factory.h | 97 +++++++++ .../dpsim-models/Solver/MNASyncGenInterface.h | 76 +++++++ dpsim-models/src/CMakeLists.txt | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp | 8 +- .../DP/DP_Ph1_SynchronGenerator4OrderIter.cpp | 170 ++++++---------- .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp | 167 ++++++++++++++++ .../DP/DP_Ph1_SynchronGenerator6OrderIter.cpp | 71 +++---- .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp | 10 +- dpsim/examples/cxx/CMakeLists.txt | 1 + ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 24 ++- .../DP_SynGen4OrderDCIM_SMIB_Fault.cpp | 4 +- .../DP_SynGen6OrderDCIM_SMIB_Fault.cpp | 188 ++++++++++++++++++ 17 files changed, 828 insertions(+), 298 deletions(-) create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h create mode 100644 dpsim-models/include/dpsim-models/Factory.h create mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp create mode 100644 dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index f0fb42a090..45667f1fa6 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h index cba20e4327..74b11faa82 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h @@ -21,85 +21,35 @@ namespace Ph1 { public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, public SharedFactory { - protected: - using MNASyncGenInterface::mNumIter; - - /// sim flags - bool mVoltageForm; - - // #### Model specific variables #### - /// - Matrix mVdq_prev; - /// previous voltage behind the transient impedance (p.u.) - const Attribute::Ptr mEdq_t; - Matrix mEdq_t_pred; - Matrix mEdq_t_corr; - /// derivative voltage behind the transient impedance (p.u.) - Matrix mdEdq_t; - Matrix mdEdq_t_corr; - /// - Real mElecTorque_corr; - /// - Real mdOmMech = 0; - Real mdOmMech_corr = 0; - Real mOmMech_pred; - Real mOmMech_corr; - /// prediction of mechanical system angle - Real mThetaMech_pred; - Real mThetaMech_corr; - /// - Real mDelta_pred; - Real mDelta_corr; - Real mdDelta = 0; - Real mdDelta_corr = 0; - - /// State Matrix x(k+1) = Ax(k) + Bu(k) + C - /// A Matrix - Matrix mA; - /// B Matrix - Matrix mB; - /// Constant Matrix - Matrix mC; - - /// Transformation matrix dp->dq - MatrixComp mDpToDq; - - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - public: /// - SynchronGenerator4OrderIter(String uid, String name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator4OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); /// - SynchronGenerator4OrderIter(String name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator4OrderIter(const String& name, Logger::Level logLevel = Logger::Level::off); /// - SimPowerComp::Ptr clone(String name); + SimPowerComp::Ptr clone(const String& name); // #### General Functions #### /// - void specificInitialization(); + void specificInitialization() override; /// void calculateStateMatrix(); /// - void stepInPerUnit(); + void stepInPerUnit() override; // - void correctorStep(); + void correctorStep() override; /// - void updateVoltage(const Matrix& leftVector); + void updateVoltage(const Matrix& leftVector) override; /// - bool checkVoltageDifference(); - /// - Matrix parkTransform(Real theta, const Matrix& abcVector); - + bool checkVoltageDifference() override; - /// Setters + // #### MNA Functions #### /// - void useVoltageForm(bool state) {mVoltageForm = state;} - - // #### MNA Functions #### - void mnaApplyRightSideVectorStamp(Matrix& rightVector); - void mnaPostStep(const Matrix& leftVector); - void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; + void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; + /// + void mnaPostStep(const Matrix& leftVector) override; + /// + void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override {}; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h new file mode 100644 index 0000000000..88ae4f25ff --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.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 + +namespace CPS { +namespace DP { +namespace Ph1 { + /// @brief Delayed-Current-Injection (DCIM) implementation + /// of 4th order synchronous generator model + class SynchronGenerator6OrderDCIM : + public Base::ReducedOrderSynchronGenerator, + public SharedFactory { + protected: + // ### State variables [p.u.]### + /// voltage behing the transient reactance + const Attribute::Ptr mEdq_t; + /// voltage behind subtransient reactance + const Attribute::Ptr mEdq_s; + /// + Matrix mStates; + Matrix mStates_prev; + + /// Auxiliar VBR constants + /// + Real mAd_t; + /// + Real mBd_t; + /// + Real mAq_t; + /// + Real mBq_t; + /// + Real mDq_t; + /// + Real mAd_s; + /// + Real mAq_s; + /// + Real mBd_s; + /// + Real mBq_s; + /// + Real mCd_s; + /// + Real mCq_s; + + /// state representation matrix + /// + Matrix mA; + /// + Matrix mA_inv; + /// + Matrix mB; + /// + Matrix mC; + + /// Transformation matrix dp->dq + MatrixComp mDpToDq; + + /// Vector to create abc vector from a component + MatrixComp mShiftVector; + + /// Park Transformation + Matrix parkTransform(Real theta, const Matrix& abcVector); + + // #### General Functions #### + /// DPecific component initialization + void specificInitialization(); + /// + void stepInPerUnit(); + + // ### MNA Section ### + /// + void mnaApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaApplyRightSideVectorStamp(Matrix& rightVector); + void mnaPostStep(const Matrix& leftVector); + + public: + /// + SynchronGenerator6OrderDCIM(String uid, String name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator6OrderDCIM(String name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(String name); + }; +} +} +} \ No newline at end of file diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h index 371ec07a95..09b8b5d77c 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h @@ -21,56 +21,6 @@ namespace Ph1 { public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, public SharedFactory { - protected: - /// sim flags - bool mVoltageForm; - - // #### Model specific variables #### - /// generator terminal at time k-1 - Matrix mVdq_prev; - /// predicted generator current at time k - Matrix mIdq_pred; - /// corrected generator current at time k - Matrix mIdq_corr; - /// voltage behind the transient impedance at time k-1 - const Attribute::Ptr mEdq_t; - /// predicted voltage behind the transient and subtransient impedance at time k+1 - Matrix mEdq_pred; - /// corrected voltage behind the transient and subtransient impedance at time k+1 - Matrix mEdq_corr; - /// corrected electrical torque - Real mElecTorque_corr; - /// predicted mechanical omega at time k - Real mOmMech_pred; - /// corrected mechanical omega at time k - Real mOmMech_corr; - /// prediction mechanical system angle at time k - Real mThetaMech_pred; - /// corrected mechanical system angle at time k - Real mThetaMech_corr; - /// predicted delta at time k - Real mDelta_pred; - /// corrected delta at time k - Real mDelta_corr; - - /// State Matrix backward euler: Edq(k) = A * Edq(k) + B * Idq + C * Ef - /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + C * Ef - /// A Matrix - Matrix mA; - Matrix mA_prev; - Matrix mA_corr; - /// B Matrix - Matrix mB; - Matrix mB_corr; - /// Constant Matrix - Matrix mC; - - /// Transformation matrix dp->dq - MatrixComp mDpToDq; - - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - public: /// SynchronGenerator6OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); @@ -81,29 +31,25 @@ namespace Ph1 { // #### General Functions #### /// - void specificInitialization(); + void specificInitialization() override; /// void calculateStateMatrix(); /// - void stepInPerUnit(); + void stepInPerUnit() override; // - void correctorStep(); + void correctorStep() override; /// - void updateVoltage(const Matrix& leftVector); - /// - bool checkVoltageDifference(); + void updateVoltage(const Matrix& leftVector) override; /// - Matrix parkTransform(Real theta, const Matrix& abcVector); - - - /// Setters - /// - void useVoltageForm(bool state) {mVoltageForm = state;} + bool checkVoltageDifference() override; // #### MNA Functions #### - void mnaApplyRightSideVectorStamp(Matrix& rightVector); - void mnaPostStep(const Matrix& leftVector); - void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; + /// + void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; + /// + void mnaPostStep(const Matrix& leftVector) override; + /// + void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override {}; }; } } 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..49cad04799 --- /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 checkVoltageDifference(); + /// + Matrix parkTransform(Real theta, const Matrix& abcVector); + + // ### MNA Section ### + void mnaApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaApplyRightSideVectorStamp(Matrix& rightVector); + void mnaPostStep(const Matrix& leftVector); + void mnaInitialize(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; }; + + }; +} +} +} \ No newline at end of file diff --git a/dpsim-models/include/dpsim-models/Factory.h b/dpsim-models/include/dpsim-models/Factory.h new file mode 100644 index 0000000000..0dc8f86538 --- /dev/null +++ b/dpsim-models/include/dpsim-models/Factory.h @@ -0,0 +1,97 @@ +#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("4Iter", new DerivedCreator>); + FactoryRegistration> _6OrderDPIter("6Iter", new DerivedCreator>); + } +} +} +} \ No newline at end of file diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index cf66a7e68a..20a1aa1571 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -15,16 +15,92 @@ namespace CPS { /// Interface to be used by synchronous generators class MNASyncGenInterface { + protected: + // #### Model specific variables #### + /// generator terminal at time k-1 + Matrix mVdq_prev; + /// predicted generator current at time k + Matrix mIdq_pred; + /// corrected generator current at time k + Matrix mIdq_corr; + /// voltage behind the transient/subtransiet impedance at time k-1 + Attribute::Ptr mEdq; + /// predicted voltage behind the transient and subtransient impedance at time k+1 + Matrix mEdq_pred; + /// corrected voltage behind the transient and subtransient impedance at time k+1 + Matrix mEdq_corr; + /// corrected electrical torque + Real mElecTorque_corr; + /// predicted mechanical omega at time k + Real mOmMech_pred; + /// corrected mechanical omega at time k + Real mOmMech_corr; + /// prediction mechanical system angle at time k + Real mThetaMech_pred; + /// corrected mechanical system angle at time k + Real mThetaMech_corr; + /// predicted delta at time k + Real mDelta_pred; + /// corrected delta at time k + Real mDelta_corr; + + /// State Matrix backward euler: Edq(k) = A * Edq(k) + B * Idq + C * Ef + /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + C * Ef + /// A Matrix + Matrix mA; + Matrix mA_prev; + Matrix mA_corr; + /// B Matrix + Matrix mB; + Matrix mB_corr; + /// Constant Matrix + Matrix mC; + + /// Transformation matrix dp->dq + MatrixComp mDpToDq; + + /// Vector to create abc vector from a component + MatrixComp mShiftVector; + public: typedef std::shared_ptr Ptr; typedef std::vector List; + /// Constructor + MNASyncGenInterface() { + // inizialize matrix that are not model dependent + mIdq_pred = Matrix::Zero(2,1); + mIdq_corr = Matrix::Zero(2,1); + + // Vector to convert 1phase to 3phase + + mShiftVector = Matrix::Zero(3,1); + mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; + } + /// Returns true if it needs to iterate virtual void correctorStep()=0; /// virtual void updateVoltage(const Matrix& leftVector)=0; /// virtual bool checkVoltageDifference() {return false;} + /// + Matrix parkTransform(Real theta, const Matrix& abcVector) { + Matrix dq0Vector(3, 1); + Matrix dqVector(2, 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; + dqVector << dq0Vector(0,0), dq0Vector(1,0); + + return dqVector; + } /// Setters /// diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index e12aee3444..ae9be9ec20 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -39,6 +39,7 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator4OrderIter.cpp + DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator6OrderIter.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp index 8dee1234b7..df663ef47f 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -13,7 +13,7 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM (String uid, String name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(Attribute::create("Edq0_t", mAttributes)) { + mEdq_t(Attribute::create("Edq_t", mAttributes)) { setTerminalNumber(1); @@ -56,7 +56,7 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mCq = 2 * mTimeStep * mEf / (2 * mTd0_t + mTimeStep); + mCq = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); // Initialize matrix of state representation mA = Matrix::Zero(4,4); @@ -93,14 +93,14 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::mnaApplySystemMatrixStamp(Matrix& sys void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { if (mSimTime>0.0) { // calculate mechanical variables at t=k+1 with forward euler + **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); } // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - mStates_prev << mStates, mVdq; + mStates_prev << mStates, **mVdq; mStates = mA_inv * mB * mStates_prev + mA_inv * mC * mEf; **mEdq_t << mStates(0,0), mStates(1,0); **mIdq << mStates(2,0), mStates(3,0); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp index 22d68d8f54..ebc18b5456 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp @@ -11,37 +11,28 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter - (String uid, String name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(Attribute::create("Edq0_t", mAttributes)) { + (const String& uid, const String& name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { mPhaseType = PhaseType::Single; setTerminalNumber(1); - - // model flags - mVoltageForm = false; - // model variables - **mEdq_t = Matrix::Zero(2,1); - mEdq_t_pred = Matrix::Zero(2,1); - mEdq_t_corr = Matrix::Zero(2,1); - mdEdq_t = Matrix::Zero(2,1); - mdEdq_t_corr = Matrix::Zero(2,1); - - // - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - - // Initialize attributes + /// initialize attributes + mEdq = Attribute::create("Edq0_t", mAttributes); mNumIter = Attribute::create("NIterations", mAttributes, 0); + + // Initialize matrix + **mEdq = Matrix::Zero(2,1); + mEdq_pred = Matrix::Zero(2,1); + mEdq_corr = Matrix::Zero(2,1); } DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter - (String name, Logger::Level logLevel) + (const String& name, Logger::Level logLevel) : SynchronGenerator4OrderIter(name, name, logLevel) { } -SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderIter::clone(String name) { +SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderIter::clone(const String& name) { auto copy = SynchronGenerator4OrderIter::make(name, mLogLevel); return copy; @@ -51,13 +42,16 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { // Initialize matrix of state representation mA = Matrix::Zero(2,2); + mA_prev = Matrix::Zero(2,2); + mA_corr = Matrix::Zero(2,2); mB = Matrix::Zero(2,2); + mB_corr = Matrix::Zero(2,2); mC = Matrix::Zero(2,1); calculateStateMatrix(); // 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; + (**mEdq)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; + (**mEdq)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; // initialize transformation matrix dp->dq mDpToDq = Matrix::Zero(1,2); @@ -70,8 +64,8 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - (**mEdq_t)(0,0), - (**mEdq_t)(1,0), + (**mEdq)(0,0), + (**mEdq)(1,0), mMaxIter, mTolerance ); @@ -79,63 +73,51 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { } void DP::Ph1::SynchronGenerator4OrderIter::calculateStateMatrix() { - if (mVoltageForm) { - Real Td_t = mTd0_t * (mLd_t / mLd); - Real Tq_t = mTq0_t * (mLq_t / mLq); - mA << -1. / Tq_t , 0.0, - 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.0, - (1. / Td_t) * mEf * (mLd_t / mLd); - } - else { // Currents form - mA << -1./mTq0_t , 0.0, - 0.0 , -1/mTd0_t; - mB << 0.0 , (1. / mTq0_t) * (mLq-mLq_t), - (-1. / mTd0_t) * (mLd-mLd_t) , 0.0 ; - mC << 0.0, - (1./mTd0_t) * mEf; - } + mA << 1 - mTimeStep / mTq0_t, 0.0, + 0.0, 1 - mTimeStep / mTd0_t; + mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, + 0.0, 1 - mTimeStep / (2 * mTd0_t); + mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, + 0.0, - mTimeStep / (2 * mTd0_t); + mB << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, + - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0; + mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), + - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; + mC << 0.0, + (mTimeStep / mTd0_t); } void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { // set number of iteratios equal to zero - mNumIter = 0; + **mNumIter = 0; // Predictor step (euler) - //predict mechanical variables at t=k+1 + // Predictor step (backward euler) if (mSimTime>0.0) { + // calculate electrical torque at t=k-1 **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - mdOmMech = 1 / (2.* mH) * (**mMechTorque - **mElecTorque); - mOmMech_pred = **mOmMech + mTimeStep * mdOmMech; - mdDelta = (mOmMech_pred - 1.) * mBase_OmMech; - mDelta_pred = **mDelta + mTimeStep * mdDelta; - mThetaMech_pred = **mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; + // predict mechanical variables at t=k + mOmMech_pred = **mOmMech + mTimeStep / (2 * mH) * (**mMechTorque - **mElecTorque); + mDelta_pred = **mDelta + mTimeStep * mBase_OmMech * (**mOmMech - 1); + mThetaMech_pred = **mThetaMech + mTimeStep * **mOmMech * mBase_OmMech; } else { - mdOmMech = 0; mOmMech_pred = **mOmMech; - mdDelta = 0; mDelta_pred = **mDelta; mThetaMech_pred = **mThetaMech; } //predict voltage behind transient reactance - if (mVoltageForm) - mdEdq_t = mA * **mEdq_t + mB * **mVdq + mC; - else - mdEdq_t = mA * **mEdq_t + mB * **mIdq + mC; - mEdq_t_pred = **mEdq_t + mTimeStep * mdEdq_t; + mEdq_pred = mA * (**mEdq) + mB * **mIdq + mC * mEf; // predict armature currents for at t=k+1 - (**mIdq)(0,0) = (mEdq_t_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdq_t_pred(0,0) ) / mLq_t; + mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { @@ -144,43 +126,32 @@ void DP::Ph1::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { // corrector step (trapezoidal rule) - **mNumIter = **mNumIter + 1; - if (**mNumIter == 1) - return; - - //predict mechanical variables if (mSimTime>0.0) { + // calculate electrical torque at t=k mElecTorque_corr = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - mdOmMech_corr = 1 / (2.* mH) * (**mMechTorque - mElecTorque_corr); - mOmMech_corr = **mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); - mdDelta_corr = (mOmMech_corr - 1.) * mBase_OmMech; - mDelta_corr = **mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); - mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_corr) * mBase_OmMech; + // correct mechanical variables at t=k + mOmMech_corr = **mOmMech + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorque_corr); + mDelta_corr = **mDelta + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMech_pred - 2); + mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_pred) * mBase_OmMech; } else { mElecTorque_corr = **mElecTorque; - mdOmMech_corr = 0; - mOmMech_corr = **mOmMech; - mdDelta_corr = 0; - mDelta_corr = **mDelta; - mThetaMech_corr = **mThetaMech; + mOmMech_corr = mOmMech_pred; + mDelta_corr = mDelta_pred; + mThetaMech_corr = mThetaMech_pred; } //predict voltage behind transient reactance - if (mVoltageForm) - mdEdq_t_corr = mA * **mEdq_t + mB * **mVdq + mC; - else - mdEdq_t_corr = mA * **mEdq_t + mB * **mIdq + mC; - mEdq_t_corr = **mEdq_t + mTimeStep / 2 * (mdEdq_t + mdEdq_t_corr); + mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC * mEf ; // armature currents for at t=k+1 - (**mIdq)(0,0) = (mEdq_t_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdq_t_corr(0,0) ) / mLq_t; + mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; // stamp currents mnaApplyRightSideVectorStamp(**mRightVector); @@ -194,52 +165,41 @@ void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVecto MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - if (**mNumIter == 0) { + if (**mNumIter == 0) **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; - } else { + else **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; - } } bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { - if (**mNumIter == 0) { + if (**mNumIter == 0) // if no corrector step has been performed yet return true; - } Matrix voltageDifference = **mVdq - mVdq_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { if (**mNumIter == mMaxIter) { return false; } else { + // set predicted values equal to corrected values for the next iteration + mOmMech_pred = mOmMech_corr; + mDelta_pred = mDelta_corr; + mThetaMech_pred= mDelta_corr; + mIdq_pred = mIdq_corr; + mEdq_pred = mEdq_corr; + return true; } } else { return false; } - } void DP::Ph1::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { // update variables - **mEdq_t = mEdq_t_corr; **mOmMech = mOmMech_corr; **mThetaMech = mThetaMech_corr; **mDelta = mDelta_corr; -} - -Matrix DP::Ph1::SynchronGenerator4OrderIter::parkTransform(Real theta, const Matrix& abcVector) { - Matrix dq0Vector(3, 1); - Matrix dqVector(2, 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; - dqVector << dq0Vector(0,0), dq0Vector(1,0); - return dqVector; + **mEdq = mEdq_corr; + **mIdq = mIdq_corr; } \ No newline at end of file diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp new file mode 100644 index 0000000000..fc72774ad6 --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp @@ -0,0 +1,167 @@ +/* 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::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM + (String uid, String name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(Attribute::create("Edq_t", mAttributes)), + mEdq_s(Attribute::create("Edq_s", mAttributes)) { + + setTerminalNumber(1); + + // model variables + **mEdq_t = Matrix::Zero(2,1); + **mEdq_s = Matrix::Zero(2,1); + + // + mShiftVector = Matrix::Zero(3,1); + mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; + + // initialize transformation matrix dp->dq + mDpToDq = Matrix::Zero(1,2); +} + +DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM + (String name, Logger::Level logLevel) + : SynchronGenerator6OrderDCIM(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderDCIM::clone(String name) { + + auto copy = SynchronGenerator6OrderDCIM::make(name, mLogLevel); + return copy; +} + +void DP::Ph1::SynchronGenerator6OrderDCIM::specificInitialization() { + + // initial voltage behind the transient reactance in the dq reference frame + (**mEdq_t)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); + (**mEdq_t)(1,0) = mEf - (mLd - mLd_t) * (**mIdq)(0,0); + + // initial dq behind the subtransient reactance in the dq reference frame + (**mEdq_s)(0,0) = (**mVdq)(0,0) - mLq_s * (**mIdq)(1,0); + (**mEdq_s)(1,0) = (**mVdq)(1,0) + mLd_s * (**mIdq)(0,0); + + // + mStates = Matrix::Zero(6,1); + mStates << **mEdq_s, **mEdq_t, **mIdq; + mStates_prev = Matrix::Zero(8,1); + + // + mAd_t = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); + mBd_t = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); + mAq_t = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); + mBq_t = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); + mDq_t = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); + + // + mAd_s = mTimeStep * (mLq_t - mLq_s) / (2 * mTq0_s + mTimeStep); + mBd_s = mTimeStep / (2 * mTq0_s + mTimeStep); + mCd_s = (2 * mTq0_s - mTimeStep) / (2 * mTq0_s + mTimeStep); + mAq_s = mTimeStep * (mLd_t - mLd_s) / (2 * mTd0_s + mTimeStep); + mBq_s = mTimeStep / (2 * mTd0_s + mTimeStep); + mCq_s = (2 * mTd0_s - mTimeStep) / (2 * mTd0_s + mTimeStep); + + // Initialize matrix of state representation + mA = Matrix::Zero(6,6); + mA << 1, 0, -mBd_s, 0, 0, -mAd_s, + 0, 1, 0, -mBq_s, mAq_s, 0, + 0, 0, 1, 0, 0, -mAd_t, + 0, 0, 0, 1, -mAq_t, 0, + 0, -1, 0, 0, mLd_s, 0, + 1, 0, 0, 0, 0, mLq_s; + mA_inv = mA.inverse(); + + mB = Matrix::Zero(6,8); + mB << mCd_s, 0, mBd_s, 0, 0, mAd_s, 0, 0, + 0, mCq_s, 0, mBq_s, -mAq_s, 0, 0, 0, + 0, 0, mBd_t, 0, 0, mAd_t, 0, 0, + 0, 0, 0, mBq_t, mAq_t, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 0, 0, 1, 0; + + mC = Matrix::Zero(6,1); + mC << 0, 0, 0, mDq_t, 0, 0; + + 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}" + "\n--- Model specific initialization finished ---", + + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), + (**mEdq_s)(0,0), + (**mEdq_s)(1,0) + ); + mSLog->flush(); +} + +void DP::Ph1::SynchronGenerator6OrderDCIM::mnaApplySystemMatrixStamp(Matrix& systemMatrix) { +} + +void DP::Ph1::SynchronGenerator6OrderDCIM::stepInPerUnit() { + if (mSimTime>0.0) { + // calculate mechanical variables at t=k+1 with forward euler + **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); + **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); + **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); + **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; + } + + // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) + mStates_prev << mStates, **mVdq; + mStates = mA_inv * mB * mStates_prev + mA_inv * mC * mEf; + **mEdq_s << mStates(0,0), mStates(1,0); + **mEdq_t << mStates(2,0), mStates(3,0); + **mIdq << mStates(4,0), mStates(5,0); + + // convert currents into the abc reference frame + mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; +} + +void DP::Ph1::SynchronGenerator6OrderDCIM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); +} + +void DP::Ph1::SynchronGenerator6OrderDCIM::mnaPostStep(const Matrix& leftVector) { + + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage into dq reference frame + MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix Vabc = Matrix(3,1); + Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); + **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; + + mSimTime = mSimTime + mTimeStep; +} + +Matrix DP::Ph1::SynchronGenerator6OrderDCIM::parkTransform(Real theta, const Matrix& abcVector) { + Matrix dq0Vector(3, 1); + Matrix dqVector(2, 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; + dqVector << dq0Vector(0,0), dq0Vector(1,0); + return dqVector; +} \ No newline at end of file diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp index a452068cf5..191bfc6197 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp @@ -12,28 +12,19 @@ using namespace CPS; DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter (const String& uid, const String& name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(Attribute::create("Edq0_t", mAttributes)) { + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { mPhaseType = PhaseType::Single; setTerminalNumber(1); - // model flags (deprecated) - mVoltageForm = false; - + /// initialize attributes + mEdq = Attribute::create("Edq", mAttributes); + mNumIter = Attribute::create("NIterations", mAttributes, 0); + // Initialize matrix - mIdq_pred = Matrix::Zero(2,1); - mIdq_corr = Matrix::Zero(2,1); - **mEdq_t = Matrix::Zero(4,1); + **mEdq = Matrix::Zero(4,1); mEdq_pred = Matrix::Zero(4,1); mEdq_corr = Matrix::Zero(4,1); - - // - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - - // Initialize attributes - mNumIter = Attribute::create("NIterations", mAttributes, 0); } DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter @@ -59,10 +50,10 @@ void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { calculateStateMatrix(); // 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_t)(2,0) = (**mEdq_t)(0,0) + (mLq_t - mLq_s) * (**mIdq)(1,0); - (**mEdq_t)(3,0) = (**mEdq_t)(1,0) - (mLd_t - mLd_s) * (**mIdq)(0,0); + (**mEdq)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); + (**mEdq)(1,0) = - (mLd- mLd_t) * (**mIdq)(0,0) + mEf; + (**mEdq)(2,0) = (**mEdq)(0,0) + (mLq_t - mLq_s) * (**mIdq)(1,0); + (**mEdq)(3,0) = (**mEdq)(1,0) - (mLd_t - mLd_s) * (**mIdq)(0,0); // initialize transformation matrix dp->dq mDpToDq = Matrix::Zero(1,2); @@ -77,10 +68,10 @@ void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - (**mEdq_t)(0,0), - (**mEdq_t)(1,0), - (**mEdq_t)(2,0), - (**mEdq_t)(3,0), + (**mEdq)(0,0), + (**mEdq)(1,0), + (**mEdq)(2,0), + (**mEdq)(3,0), mMaxIter, mTolerance ); @@ -133,7 +124,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::stepInPerUnit() { } //predict voltage behind transient reactance - mEdq_pred = mA * (**mEdq_t) + mB * **mIdq + mC * mEf; + mEdq_pred = mA * (**mEdq) + mB * **mIdq + mC * mEf; // predict armature currents for at t=k+1 mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; @@ -168,11 +159,12 @@ void DP::Ph1::SynchronGenerator6OrderIter::correctorStep() { } //predict voltage behind transient reactance - mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC * mEf ; + mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC * mEf; // armature currents for at t=k+1 - mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; + mIdq_corr(0,0) = (mEdq_corr(3,0) - (**mVdq)(1,0) ) / mLd_s; + mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(2,0) ) / mLq_s; + // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); @@ -191,18 +183,16 @@ void DP::Ph1::SynchronGenerator6OrderIter::updateVoltage(const Matrix& leftVecto MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - if (**mNumIter == 0) { + if (**mNumIter == 0) **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; - } else { + else **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; - } } bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { - if (**mNumIter == 0) { + if (**mNumIter == 0) // if no corrector step has been performed yet return true; - } Matrix voltageDifference = **mVdq - mVdq_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { @@ -228,22 +218,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::mnaPostStep(const Matrix& leftVector) **mOmMech = mOmMech_corr; **mThetaMech = mThetaMech_corr; **mDelta = mDelta_corr; - **mEdq_t = mEdq_corr; + **mEdq = mEdq_corr; **mIdq = mIdq_corr; } -Matrix DP::Ph1::SynchronGenerator6OrderIter::parkTransform(Real theta, const Matrix& abcVector) { - Matrix dq0Vector(3, 1); - Matrix dqVector(2, 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; - dqVector << dq0Vector(0,0), dq0Vector(1,0); - return dqVector; -} \ No newline at end of file diff --git a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp index 9abd298ee2..e2da9e9ace 100644 --- a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -44,7 +44,7 @@ void SP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mCq = 2 * mTimeStep * mEf / (2 * mTd0_t + mTimeStep); + mCq = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); // Initialize matrix of state representation mA = Matrix::Zero(2,2); @@ -79,6 +79,14 @@ void SP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplySystemMatrixStamp(SparseM } void SP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { + if (mSimTime>0.0) { + // calculate mechanical variables at t=k+1 with forward euler + **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); + **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); + **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); + **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; + } + // get transformation matrix mDqToComplexA = get_DqToComplexATransformMatrix(); mComplexAToDq = mDqToComplexA.transpose(); diff --git a/dpsim/examples/cxx/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 654e2f8911..16aa7e4832 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -76,6 +76,7 @@ set(CIRCUIT_SOURCES Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp + Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp # SMIB Reduced Order - Load step diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index b3af1dc9f6..154019c3d2 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -1,4 +1,5 @@ #include +#include #include "../Examples.h" using namespace DPsim; @@ -17,17 +18,21 @@ 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 = 10e-6; + Real timeStep = 32e-6; Real finalTime = 35; // Default configuration - String sgType = defaultConfig.sgType; Real loadStepEventTime = defaultConfig.loadStepEventTime; Real H = syngenKundur.H; Real tolerance = defaultConfig.tolerance; int maxIter = defaultConfig.maxIter; + String SGModel = defaultConfig.sgType + "Iter"; + SGModel = "4Iter"; // Command line args processing CommandLineArgs args(argc, argv); @@ -40,12 +45,12 @@ int main(int argc, char* argv[]) { tolerance = args.getOptionReal("Tolerance"); if (args.options.find("MaxIter") != args.options.end()) maxIter = int(args.getOptionReal("MaxIter")); - if (args.options.find("sgType") != args.options.end()) - sgType = args.getOptionString("sgType"); 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; @@ -53,7 +58,7 @@ int main(int argc, char* argv[]) { 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: " << sgType << std::endl; + std::cout << "SG: " << SGModel << std::endl; // Configure logging Logger::Level logLevel = Logger::Level::info; @@ -135,15 +140,16 @@ int main(int argc, char* argv[]) { auto n2DP = SimNode::make("n2DP", PhaseType::Single, initialVoltage_n2); // Synchronous generator - auto genDP = DP::Ph1::SynchronGenerator4OrderIter::make("SynGen", logLevel); + 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_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->setMaxIterations(maxIter); - genDP->setTolerance(tolerance); + 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); diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp index ef242cb000..c3043d419d 100644 --- a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp @@ -156,9 +156,9 @@ int main(int argc, char* argv[]) { Real SwitchOpen = GridParams.SwitchOpen; Real startTimeFault = 1.0; Real endTimeFault = 1.1; - Real timeStep = 10e-9; + Real timeStep = 1e-9; Real H = syngenKundur.H; - Real finalTime = 3; + Real finalTime = 2; // Command line args processing CommandLineArgs args(argc, argv); diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp new file mode 100644 index 0000000000..26706a90fd --- /dev/null +++ b/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp @@ -0,0 +1,188 @@ +#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; + +void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, + Real startTimeFault, Real endTimeFault, Real logDownSampling, Real switchOpen, + Real switchClosed, 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(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 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), 0.0, 0.0}; + std::vector initialVoltage_n2{ n2PF->voltage()(0,0), 0.0, 0.0}; + auto n1DP = SimNode::make("n1DP", PhaseType::Single, initialVoltage_n1); + auto n2DP = SimNode::make("nDSP", PhaseType::Single, initialVoltage_n2); + + // Components + auto genDP = DP::Ph1::SynchronGenerator6OrderDCIM::make("SynGen", Logger::Level::debug); + genDP->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); + genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); + + //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); + + //Breaker + auto fault = DP::Ph1::Switch::make("Br_fault", logLevel); + fault->setParameters(switchOpen, switchClosed); + fault->open(); + + // Topology + genDP->connect({ n1DP }); + lineDP->connect({ n1DP, n2DP }); + extnetDP->connect({ n2DP }); + fault->connect({DP::SimNode::GND, n1DP}); + auto systemDP = SystemTopology(GridParams.nomFreq, + SystemNodeList{n1DP, n2DP}, + SystemComponentList{genDP, lineDP, extnetDP, fault}); + + // Logging + auto loggerDP = DataLogger::make(simNameDP, true, logDownSampling); + //loggerDP->logAttribute("v_slack", extnetDP->attribute("v_intf")); + //loggerDP->logAttribute("i_slack", extnetDP->attribute("i_intf")); + //loggerDP->logAttribute("v_gen", genDP->attribute("v_intf")); + //loggerDP->logAttribute("i_gen", genDP->attribute("i_intf")); + loggerDP->logAttribute("Te", genDP->attribute("Te")); + loggerDP->logAttribute("delta", genDP->attribute("delta")); + loggerDP->logAttribute("w_r", genDP->attribute("w_r")); + loggerDP->logAttribute("Edq_t", genDP->attribute("Edq_t")); + loggerDP->logAttribute("Edq_s", genDP->attribute("Edq_s")); + loggerDP->logAttribute("Vdq", genDP->attribute("Vdq0")); + loggerDP->logAttribute("Idq", genDP->attribute("Idq0")); + + Simulation simDP(simNameDP, logLevel); + simDP.doInitFromNodesAndTerminals(true); + simDP.setSystem(systemDP); + simDP.setTimeStep(timeStep); + simDP.setFinalTime(finalTime); + simDP.setDomain(Domain::DP); + simDP.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simDP.addLogger(loggerDP); + //simDP.doSystemMatrixRecomputation(true); + + // Events + auto sw1 = SwitchEvent::make(startTimeFault, fault, true); + simDP.addEvent(sw1); + + auto sw2 = SwitchEvent::make(endTimeFault, fault, false); + simDP.addEvent(sw2); + + simDP.run(); +} + +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-9; + Real H = syngenKundur.H; + Real finalTime = 3; + + // Command line args processing + CommandLineArgs args(argc, argv); + std::string stepSize_str = ""; + std::string inertia_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("Inertia") != args.options.end()) { + H = args.getOptionReal("Inertia"); + inertia_str = "_Inertia_" + std::to_string(H); + } + } + + Real logDownSampling; + if (timeStep<10e-6) + logDownSampling = floor((10e-6) / timeStep); + else + logDownSampling = 1.0; + Logger::Level logLevel = Logger::Level::off; + std::string simName = "DP_SynGen6OrderDCIM_SMIB_Fault" + stepSize_str + inertia_str; + DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, + logDownSampling, SwitchOpen, SwitchClosed, logLevel); +} \ No newline at end of file From 469867e25306890b0d8741499b6e1dc773c23c2a Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 17 Nov 2022 15:54:51 +0100 Subject: [PATCH 09/68] add trapezoidal method for predictor step Signed-off-by: Martin Moraga --- .../dpsim-models/Solver/MNASyncGenInterface.h | 72 ++++++---- .../DP/DP_Ph1_SynchronGenerator4OrderIter.cpp | 117 ++++++++++++----- .../DP/DP_Ph1_SynchronGenerator6OrderIter.cpp | 124 +++++++++++++----- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 8 +- dpsim/include/dpsim/MNASolverDirect.h | 4 + dpsim/include/dpsim/Simulation.h | 5 + dpsim/include/dpsim/Solver.h | 4 + dpsim/src/MNASolverDirect.cpp | 5 + dpsim/src/Simulation.cpp | 1 + 9 files changed, 245 insertions(+), 95 deletions(-) diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 20a1aa1571..1a586d78be 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -44,28 +44,66 @@ namespace CPS { /// corrected delta at time k Real mDelta_corr; - /// State Matrix backward euler: Edq(k) = A * Edq(k) + B * Idq + C * Ef - /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + C * Ef - /// A Matrix - Matrix mA; + /// Matrix used when numerical method of predictor step = Euler + /// State Matrix backward euler: Edq(k) = mA_euler * Edq(k) + mB_euler * Idq + mC_euler * Ef + Matrix mA_euler; + Matrix mB_euler; + Matrix mC_euler; + + /// Matrixes used when numerical method of predictor step = trapezoidal rule + /// State Matrix trapezoidal rule: mStates = mA_trap_inv * mB_trap * mStates_prev + mA_trap_inv * mC_trap * Ef + /// where mStates_prev << Ed(k-1), Ed(k-1), Id(k-1), Iq(k-1), Vd(k-1), Vq(k-1) + /// and mStates << Ed(k), Ed(k), Id(k), Iq(k) + Matrix mA_trap; + Matrix mA_trap_inv; + Matrix mB_trap; + Matrix mC_trap; + Matrix mStates_trap_prev; + Matrix mStates_trap; + + /// Matrixes used when numerical method of corrector step = trapezoidal rule + /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + mC_corr * Ef Matrix mA_prev; Matrix mA_corr; - /// B Matrix - Matrix mB; Matrix mB_corr; - /// Constant Matrix - Matrix mC; + Matrix mC_corr; /// Transformation matrix dp->dq MatrixComp mDpToDq; /// Vector to create abc vector from a component MatrixComp mShiftVector; + + /// + Attribute::Ptr mNumIter; + /// + Int mMaxIter = 25; + /// + Real mTolerance = 1e-6; + /// + CPS::NumericalMethod mNumericalMethod = CPS::NumericalMethod::Trapezoidal; 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 checkVoltageDifference() {return false;} + + /// Setters + /// + void setMaxIterations(Int maxIterations) {mMaxIter = maxIterations;} + /// + void setTolerance(Real Tolerance) {mTolerance = Tolerance;} + /// + void setNumericalMethod(CPS::NumericalMethod numericalMethod) {mNumericalMethod = numericalMethod;} + + protected: /// Constructor MNASyncGenInterface() { // inizialize matrix that are not model dependent @@ -78,12 +116,6 @@ namespace CPS { mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; } - /// Returns true if it needs to iterate - virtual void correctorStep()=0; - /// - virtual void updateVoltage(const Matrix& leftVector)=0; - /// - virtual bool checkVoltageDifference() {return false;} /// Matrix parkTransform(Real theta, const Matrix& abcVector) { Matrix dq0Vector(3, 1); @@ -102,17 +134,5 @@ namespace CPS { return dqVector; } - /// Setters - /// - void setMaxIterations(Int maxIterations) {mMaxIter = maxIterations;} - /// - void setTolerance(Real Tolerance) {mTolerance = Tolerance;} - - /// - Attribute::Ptr mNumIter; - /// - Int mMaxIter = 25; - /// - Real mTolerance = 1e-6; }; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp index ebc18b5456..efdf8dd6f7 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp @@ -18,7 +18,7 @@ DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter setTerminalNumber(1); /// initialize attributes - mEdq = Attribute::create("Edq0_t", mAttributes); + mEdq = Attribute::create("Edq", mAttributes); mNumIter = Attribute::create("NIterations", mAttributes, 0); // Initialize matrix @@ -40,13 +40,7 @@ SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderIter::clone(const Str void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { - // Initialize matrix of state representation - mA = Matrix::Zero(2,2); - mA_prev = Matrix::Zero(2,2); - mA_corr = Matrix::Zero(2,2); - mB = Matrix::Zero(2,2); - mB_corr = Matrix::Zero(2,2); - mC = Matrix::Zero(2,1); + // calculate state representation matrix calculateStateMatrix(); // initial voltage behind the transient reactance in the dq0 reference frame @@ -73,18 +67,61 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { } void DP::Ph1::SynchronGenerator4OrderIter::calculateStateMatrix() { - mA << 1 - mTimeStep / mTq0_t, 0.0, - 0.0, 1 - mTimeStep / mTd0_t; + // Initialize matrix of state representation of predictor step + if (mNumericalMethod == CPS::NumericalMethod::Euler) { + mA_euler = Matrix::Zero(2,2); + mA_euler << 1 - mTimeStep / mTq0_t, 0.0, + 0.0, 1 - mTimeStep / mTd0_t; + mB_euler = Matrix::Zero(2,2); + mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, + - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0; + mC_euler = Matrix::Zero(2,1); + mC_euler << 0.0, + (mTimeStep / mTd0_t); + + } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + mC_trap = Matrix::Zero(4,1); + mStates_trap_prev = Matrix::Zero(6,1); + mStates_trap = Matrix::Zero(4,1); + + Real Ad = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); + Real Bd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); + + Real Aq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); + Real Bq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); + Real Cq = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); + + mA_trap = Matrix::Zero(4,4); + mA_trap << 1, 0, 0, -Ad, + 0, 1, -Aq, 0, + 0, -1, mLd_t, 0, + 1, 0, 0, mLq_t; + mA_trap_inv = mA_trap.inverse(); + + mB_trap = Matrix::Zero(4,6); + mB_trap << Bd, 0, 0, Ad, 0, 0, + 0, Bq, Aq, 0, 0, 0, + 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 1, 0; + + mC_trap = Matrix::Zero(4,1); + mC_trap << 0, Cq, 0, 0; + } + + // Initialize matrix of state representation of corrector step + mA_prev = Matrix::Zero(2,2); mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, 1 - mTimeStep / (2 * mTd0_t); - mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, - mTimeStep / (2 * mTd0_t); - mB << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, - - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0; - mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; - mC << 0.0, - (mTimeStep / mTd0_t); + 0.0, 1 - mTimeStep / (2 * mTd0_t); + mA_corr = Matrix::Zero(2,2); + mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, + 0.0, - mTimeStep / (2 * mTd0_t); + mB_corr = Matrix::Zero(2,2); + mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), + - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; + + mC_corr = Matrix::Zero(2,1); + mC_corr << 0.0, + (mTimeStep / mTd0_t); } void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { @@ -107,12 +144,22 @@ void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { mThetaMech_pred = **mThetaMech; } - //predict voltage behind transient reactance - mEdq_pred = mA * (**mEdq) + mB * **mIdq + mC * mEf; + // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) + if (mNumericalMethod == CPS::NumericalMethod::Euler) { + //predict voltage behind transient reactance + mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; - // predict armature currents for at t=k+1 - mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; + // predict armature currents for at t=k+1 + mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; + + } + else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + mStates_trap_prev << **mEdq, **mIdq, **mVdq; + mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * mEf; + mEdq_pred << mStates_trap(0,0), mStates_trap(1,0); + mIdq_pred << mStates_trap(2,0), mStates_trap(3,0); + } // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); @@ -129,11 +176,12 @@ void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { **mNumIter = **mNumIter + 1; if (mSimTime>0.0) { // calculate electrical torque at t=k - mElecTorque_corr = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); + mElecTorque_corr = (**mVdq)(0,0) * (mIdq_pred)(0,0) + (**mVdq)(1,0) * (mIdq_pred)(1,0); // correct mechanical variables at t=k mOmMech_corr = **mOmMech + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorque_corr); mDelta_corr = **mDelta + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMech_pred - 2); mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_pred) * mBase_OmMech; + } else { mElecTorque_corr = **mElecTorque; mOmMech_corr = mOmMech_pred; @@ -142,7 +190,7 @@ void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { } //predict voltage behind transient reactance - mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC * mEf ; + mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; // armature currents for at t=k+1 mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; @@ -169,6 +217,12 @@ void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVecto **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; else **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; + + mOmMech_pred = mOmMech_corr; + mDelta_pred = mDelta_corr; + mThetaMech_pred = mThetaMech_corr; + mIdq_pred = mIdq_corr; + mEdq_pred = mEdq_corr; } bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { @@ -178,16 +232,9 @@ bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { Matrix voltageDifference = **mVdq - mVdq_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { - if (**mNumIter == mMaxIter) { + if (**mNumIter >= mMaxIter) { return false; - } else { - // set predicted values equal to corrected values for the next iteration - mOmMech_pred = mOmMech_corr; - mDelta_pred = mDelta_corr; - mThetaMech_pred= mDelta_corr; - mIdq_pred = mIdq_corr; - mEdq_pred = mEdq_corr; - + } else { return true; } } else { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp index 191bfc6197..1968d282fd 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp @@ -40,13 +40,7 @@ SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderIter::clone(const Str void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { - // Initialize matrix of state representation - mA = Matrix::Zero(4,4); - mA_prev = Matrix::Zero(4,4); - mA_corr = Matrix::Zero(4,4); - mB = Matrix::Zero(4,2); - mB_corr = Matrix::Zero(4,2); - mC = Matrix::Zero(4,1); + // calculate state representation matrix calculateStateMatrix(); // initial voltage behind the transient reactance in the dq0 reference frame @@ -78,28 +72,86 @@ void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { mSLog->flush(); } -void DP::Ph1::SynchronGenerator6OrderIter::calculateStateMatrix() { - mA << 1 - mTimeStep / mTq0_t, 0.0, 0.0, 0.0, - 0.0, 1 - mTimeStep / mTd0_t, 0.0, 0.0, - mTimeStep / mTq0_s, 0.0, 1 - mTimeStep / mTq0_s, 0.0, - 0.0, mTimeStep / mTd0_s, 0.0, 1 - mTimeStep / mTd0_s; - mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, - 0.0, 1 - mTimeStep / (2 * mTd0_t), 0.0, 0.0, - mTimeStep / (2 * mTq0_s), 0.0, 1 - mTimeStep / (2 * mTq0_s), 0.0, - 0.0, mTimeStep / (2 * mTd0_s), 0.0, 1 - mTimeStep / (2 * mTd0_s); - mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, - 0.0, - mTimeStep / (2 * mTd0_t), 0.0, 0.0, - mTimeStep / (2 * mTq0_s), 0.0, - mTimeStep / (2 * mTq0_s), 0.0, - 0.0, mTimeStep / (2 * mTd0_s), 0.0, - mTimeStep / (2 * mTd0_s); - mB << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, - - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0, - 0.0, (mLq_t - mLq_s) * mTimeStep / mTq0_s, - - (mLd_t - mLd_s) * mTimeStep / mTd0_s, 0.0; +void DP::Ph1::SynchronGenerator6OrderIter::calculateStateMatrix() { + // Initialize matrix of state representation of predictor step + if (mNumericalMethod == CPS::NumericalMethod::Euler) { + mA_euler = Matrix::Zero(4,4); + mA_euler << 1 - mTimeStep / mTq0_t, 0.0, 0.0, 0.0, + 0.0, 1 - mTimeStep / mTd0_t, 0.0, 0.0, + mTimeStep / mTq0_s, 0.0, 1 - mTimeStep / mTq0_s, 0.0, + 0.0, mTimeStep / mTd0_s, 0.0, 1 - mTimeStep / mTd0_s; + mB_euler = Matrix::Zero(4,2); + mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, + - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0, + 0.0, (mLq_t - mLq_s) * mTimeStep / mTq0_s, + - (mLd_t - mLd_s) * mTimeStep / mTd0_s, 0.0; + mC_euler = Matrix::Zero(4,1); + mC_euler << 0.0, + (mTimeStep / mTd0_t), + 0.0, + 0.0; + + } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + // + mStates_trap = Matrix::Zero(6,1); + mStates_trap << **mEdq, **mIdq; + mStates_trap_prev = Matrix::Zero(8,1); + + // + Real Ad_t = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); + Real Bd_t = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); + Real Aq_t = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); + Real Bq_t = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); + Real Dq_t = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); + + // + Real Ad_s = mTimeStep * (mLq_t - mLq_s) / (2 * mTq0_s + mTimeStep); + Real Bd_s = mTimeStep / (2 * mTq0_s + mTimeStep); + Real Cd_s = (2 * mTq0_s - mTimeStep) / (2 * mTq0_s + mTimeStep); + Real Aq_s = mTimeStep * (mLd_t - mLd_s) / (2 * mTd0_s + mTimeStep); + Real Bq_s = mTimeStep / (2 * mTd0_s + mTimeStep); + Real Cq_s = (2 * mTd0_s - mTimeStep) / (2 * mTd0_s + mTimeStep); + + // Initialize matrix of state representation + mA_trap = Matrix::Zero(6,6); + mA_trap << 1, 0, -Bd_s, 0, 0, -Ad_s, + 0, 1, 0, -Bq_s, Aq_s, 0, + 0, 0, 1, 0, 0, -Ad_t, + 0, 0, 0, 1, -Aq_t, 0, + 0, -1, 0, 0, mLd_s, 0, + 1, 0, 0, 0, 0, mLq_s; + mA_trap_inv = mA_trap.inverse(); + + mB_trap = Matrix::Zero(6,8); + mB_trap << Cd_s, 0, Bd_s, 0, 0, Ad_s, 0, 0, + 0, Cq_s, 0, Bq_s, -Aq_s, 0, 0, 0, + 0, 0, Bd_t, 0, 0, Ad_t, 0, 0, + 0, 0, 0, Bq_t, Aq_t, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -1, + 0, 0, 0, 0, 0, 0, 1, 0; + + mC_trap = Matrix::Zero(6,1); + mC_trap << 0, 0, 0, Dq_t, 0, 0; + } + + mA_prev = Matrix::Zero(4, 4); + mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, + 0.0, 1 - mTimeStep / (2 * mTd0_t), 0.0, 0.0, + mTimeStep / (2 * mTq0_s), 0.0, 1 - mTimeStep / (2 * mTq0_s), 0.0, + 0.0, mTimeStep / (2 * mTd0_s), 0.0, 1 - mTimeStep / (2 * mTd0_s); + mA_corr = Matrix::Zero(4,4); + mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, + 0.0, - mTimeStep / (2 * mTd0_t), 0.0, 0.0, + mTimeStep / (2 * mTq0_s), 0.0, - mTimeStep / (2 * mTq0_s), 0.0, + 0.0, mTimeStep / (2 * mTd0_s), 0.0, - mTimeStep / (2 * mTd0_s); + + mB_corr = Matrix::Zero(4,2); mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0, 0.0, (mLq_t - mLq_s) * mTimeStep / (2 * mTq0_s), - (mLd_t - mLd_s) * mTimeStep / (2 * mTd0_s), 0.0; - mC << 0.0, + mC_corr = Matrix::Zero(4,1); + mC_corr << 0.0, (mTimeStep / mTd0_t), 0.0, 0.0; @@ -123,12 +175,20 @@ void DP::Ph1::SynchronGenerator6OrderIter::stepInPerUnit() { mThetaMech_pred = **mThetaMech; } - //predict voltage behind transient reactance - mEdq_pred = mA * (**mEdq) + mB * **mIdq + mC * mEf; + // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) + if (mNumericalMethod == CPS::NumericalMethod::Euler) { + mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; - // predict armature currents for at t=k+1 - mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; - mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(2,0) ) / mLq_s; + // predict armature currents for at t=k+1 + mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; + mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(2,0) ) / mLq_s; + + } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + mStates_trap_prev << **mEdq, **mIdq, **mVdq; + mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * mEf; + mEdq_pred << mStates_trap(0,0), mStates_trap(1,0), mStates_trap(2,0), mStates_trap(3,0); + mIdq_pred << mStates_trap(4,0), mStates_trap(5,0); + } // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); @@ -159,7 +219,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::correctorStep() { } //predict voltage behind transient reactance - mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC * mEf; + mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; // armature currents for at t=k+1 mIdq_corr(0,0) = (mEdq_corr(3,0) - (**mVdq)(1,0) ) / mLd_s; @@ -202,7 +262,7 @@ bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { // set predicted values equal to corrected values for the next iteration mOmMech_pred = mOmMech_corr; mDelta_pred = mDelta_corr; - mThetaMech_pred= mDelta_corr; + mThetaMech_pred= mThetaMech_corr; mIdq_pred = mIdq_corr; mEdq_pred = mEdq_corr; diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index 154019c3d2..cc8f9766fa 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -33,6 +33,7 @@ int main(int argc, char* argv[]) { int maxIter = defaultConfig.maxIter; String SGModel = defaultConfig.sgType + "Iter"; SGModel = "4Iter"; + NumericalMethod numericalMethod = NumericalMethod::Euler; // Command line args processing CommandLineArgs args(argc, argv); @@ -61,7 +62,7 @@ int main(int argc, char* argv[]) { std::cout << "SG: " << SGModel << std::endl; // Configure logging - Logger::Level logLevel = Logger::Level::info; + Logger::Level logLevel = Logger::Level::off; // apply downsampling for simulation step sizes lower than 10us Real logDownSampling; @@ -150,7 +151,7 @@ int main(int argc, char* argv[]) { genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); - + std::dynamic_pointer_cast(genDP)->setNumericalMethod(numericalMethod); //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); extnetDP->setParameters(gridParams.VnomMV); @@ -180,6 +181,9 @@ int main(int argc, char* argv[]) { // log generator vars logger->logAttribute(genDP->name() + ".Te", genDP->attribute("Te")); logger->logAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); + logger->logAttribute(genDP->name() + ".Edq", genDP->attribute("Edq")); + logger->logAttribute(genDP->name() + ".Vdq0", genDP->attribute("Vdq0")); + logger->logAttribute(genDP->name() + ".Idq0", genDP->attribute("Idq0")); // load step event std::shared_ptr loadStepEvent = Examples::Events::createEventAddPowerConsumption("n1DP", std::round(loadStepEventTime/timeStep)*timeStep, gridParams.loadStepActivePower, systemDP, Domain::DP, logger); diff --git a/dpsim/include/dpsim/MNASolverDirect.h b/dpsim/include/dpsim/MNASolverDirect.h index c4414b26f8..8188577845 100644 --- a/dpsim/include/dpsim/MNASolverDirect.h +++ b/dpsim/include/dpsim/MNASolverDirect.h @@ -102,6 +102,7 @@ namespace DPsim { using MnaSolver::mSolveTimes; using MnaSolver::mRecomputationTimes; using MnaSolver::mListVariableSystemMatrixEntries; + using Solver::mMaxIterations; // #### General /// Create system matrix @@ -168,6 +169,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/MNASolverDirect.cpp b/dpsim/src/MNASolverDirect.cpp index 2c7fa5e04b..60df21c74a 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -215,6 +215,7 @@ std::shared_ptr MnaSolverDirect::createLogTask() template void MnaSolverDirect::solve(Real time, Int timeStepCount) { + mIter = 0; if (mSyncGen.size()==0) { // Reset source vector mRightSideVector.setZero(); @@ -237,6 +238,9 @@ void MnaSolverDirect::solve(Real time, Int timeStepCount) { // if there is iterative syncGens, then it is necessary to iterate bool iterate = true; while (iterate) { + // + mIter = mIter + 1; + // Reset source vector mRightSideVector.setZero(); @@ -271,6 +275,7 @@ void MnaSolverDirect::solve(Real time, Int timeStepCount) { } if (count==0) iterate=false; + } } } diff --git a/dpsim/src/Simulation.cpp b/dpsim/src/Simulation.cpp index 15e1c9034e..1cfa159032 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); } From 65a9def2d77933d0211a369ccf3224c8e7b761c2 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Tue, 10 Jan 2023 11:29:14 +0100 Subject: [PATCH 10/68] add SG 4 Order TPM Signed-off-by: Martin Moraga --- .../include/dpsim-models/Components.h | 1 + .../DP_Ph1_ReducedOrderSynchronGeneratorVBR.h | 2 +- .../DP/DP_Ph1_SynchronGenerator4OrderIter.h | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 125 +++++++ dpsim-models/include/dpsim-models/Factory.h | 2 + dpsim-models/src/CMakeLists.txt | 1 + .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 349 ++++++++++++++++++ ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 5 +- 8 files changed, 483 insertions(+), 3 deletions(-) create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h create mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 45667f1fa6..3a46258a26 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include 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..18fa16aa05 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.h @@ -64,7 +64,7 @@ namespace Ph1 { /// virtual void stepInPerUnit() override =0; /// - void calculateConductanceMatrix(); + virtual void calculateConductanceMatrix(); /// Calculate Ka, Kb and Kvbr void calculateAuxiliarVariables(); /// diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h index 74b11faa82..7a30cb0ccf 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h @@ -17,6 +17,7 @@ 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 SynchronGenerator4OrderIter : public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, 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..8727ae6fa9 --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -0,0 +1,125 @@ +/* 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 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 { + public: + // Common elements of all VBR models + /// voltage behind reactance phase a + const Attribute::Ptr mEvbr; + + protected: + /// Resistance matrix in dq reference frame + Matrix mResistanceMatrixDq; + /// Conductance matrix phase A + Matrix 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; + + // #### Model specific variables #### + /// voltage behind transient reactance + const Attribute::Ptr mEdq_t; + /// history term of voltage behind the transient reactance + Matrix mEh_vbr; + /// + MatrixComp mIdq_2prev; + + /// Auxiliar VBR constants + /// + Real mA; + /// + Real mB; + /// + Real mAd; + /// + Real mAq; + /// + Real mBd; + /// + Real mBq; + /// + Real mCq; + + 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 calculateAuxiliarConstants(); + /// + void calculateConductanceMatrix(); + /// + void stepInPerUnit() override; + /// + void correctorStep() override; + /// + void updateVoltage(const Matrix& leftVector) override; + /// + bool checkVoltageDifference() override; + /// Calculate Ka, Kb and Kvbr + void calculateAuxiliarVariables(); + /// + Matrix get_parkTransformMatrix(); + + // #### MNA Functions #### + /// + void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; + /// + void mnaPostStep(const Matrix& leftVector) override; + /// + void mnaApplySystemMatrixStamp(Matrix& 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/Factory.h b/dpsim-models/include/dpsim-models/Factory.h index 0dc8f86538..8a04094431 100644 --- a/dpsim-models/include/dpsim-models/Factory.h +++ b/dpsim-models/include/dpsim-models/Factory.h @@ -4,6 +4,7 @@ #include #include +#include #include #pragma once @@ -90,6 +91,7 @@ namespace DP { namespace Ph1 { void registerSynchronGenerators() { FactoryRegistration> _4OrderDPIter("4Iter", new DerivedCreator>); + FactoryRegistration> _4OrderDPTPM("4TPM", new DerivedCreator>); FactoryRegistration> _6OrderDPIter("6Iter", new DerivedCreator>); } } diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index ae9be9ec20..546634f7a9 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -39,6 +39,7 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator4OrderIter.cpp + DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator6OrderIter.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp 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..75ec8db02d --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -0,0 +1,349 @@ +/* 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), + mEvbr(Attribute::create("Evbr", mAttributes)), + mEdq_t(Attribute::create("Edq", mAttributes)) { + + mPhaseType = PhaseType::Single; + setVirtualNodeNumber(2); + setTerminalNumber(1); + + /// initialize attributes + mNumIter = Attribute::create("NIterations", mAttributes, 0); + + // model variables + **mIntfVoltage = MatrixComp::Zero(1, 1); + **mIntfCurrent = MatrixComp::Zero(1, 1); + + // 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); + + // model variables + mEh_vbr = Matrix::Zero(2,1); + **mEdq_t = Matrix::Zero(2,1); + mEdq_corr = Matrix::Zero(2,1); + mEdq_pred = 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::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::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { + mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); + mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); + + mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); + mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); + mCq = 2 * mTimeStep * mEf / (2 * mTd0_t + mTimeStep); + + mB = mLd_t - mAq; + mA = -mLq_t - mAd; + + 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; + + // Initialize matrix of state representation of corrector step + mA_prev = Matrix::Zero(2,2); + mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, + 0.0, 1 - mTimeStep / (2 * mTd0_t); + mA_corr = Matrix::Zero(2,2); + mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, + 0.0, - mTimeStep / (2 * mTd0_t); + mB_corr = Matrix::Zero(2,2); + mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), + - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; + + mC_corr = Matrix::Zero(2,1); + mC_corr << 0.0, + (mTimeStep / mTd0_t); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::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; + calculateAuxiliarConstants(); + + // 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); + + mSLog->info( + "\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 DP::Ph1::SynchronGenerator4OrderTPM::calculateConductanceMatrix() { + Matrix resistanceMatrix = Matrix::Zero(2,2); + + resistanceMatrix(0,0) = mR_const_1ph.real(); + resistanceMatrix(0,1) = -mR_const_1ph.imag(); + resistanceMatrix(1,0) = mR_const_1ph.imag(); + resistanceMatrix(1,1) = mR_const_1ph.real(); + + resistanceMatrix = resistanceMatrix * mBase_Z; + mConductanceMatrix = resistanceMatrix.inverse(); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::mnaApplySystemMatrixStamp(Matrix& systemMatrix) { + updateMatrixNodeIndices(); + calculateConductanceMatrix(); + // 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(), mConductanceMatrix); + + // set buttom right block + Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrix); + + // Set off diagonal blocks + Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), matrixNodeIndex(0, 0), -mConductanceMatrix); + Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), mVirtualNodes[0]->matrixNodeIndex(), -mConductanceMatrix); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { + // set number of iteratios equal to zero + **mNumIter = 0; + + // 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; + + if (mSimTime>0.0){ + // calculate mechanical variables at t=k+1 using forward euler + **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); + **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); + **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); + **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; + } + + // VBR history voltage + calculateAuxiliarVariables(); + //mEh_vbr(0,0) = mAd * (**mIdq)(1,0) + mBd * (**mEdq_t)(0,0); + //mEh_vbr(1,0) = mAq * (**mIdq)(0,0) + mBq * (**mEdq_t)(1,0) + mCq; + + // predict current + if (mSimTime==0.0) { + (**mIntfCurrent)(0,0) = std::conj(mInitElecPower / (mInitVoltage * mBase_V_RMS)); + mIdq_2prev = **mIntfCurrent; + } + + Matrix Idq_pred = Matrix::Zero(2,1); + Idq_pred(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdq_2prev(0,0).real(); + Idq_pred(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdq_2prev(0,0).imag(); + + // + Matrix resistanceMatrix = Matrix::Zero(2,2); + resistanceMatrix(0,0) = mKa_1ph.real() + mKb_1ph.real(); + resistanceMatrix(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrix(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrix(1,1) = mKa_1ph.real() - mKb_1ph.real(); + + mEh_vbr(0,0) = mAd * (**mIdq)(1,0) + mBd * (**mEdq_t)(0,0); + mEh_vbr(1,0) = mAq * (**mIdq)(0,0) + mBq * (**mEdq_t)(1,0) + mCq; + + // convert Edq_t into the abc reference frame + **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); + + // + **mEvbr += - Complex(mBase_Z * (resistanceMatrix * Idq_pred)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (resistanceMatrix * Idq_pred)(1,0)); + + // + mIdq_2prev = **mIntfCurrent; +} + +void DP::Ph1::SynchronGenerator4OrderTPM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), **mEvbr); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { + // corrector step (trapezoidal rule) + **mNumIter = **mNumIter + 1; + + /* + // calculate Edq_t corrected + mEdq_corr(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; + mEdq_corr(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; + + // corrected currents at t=k+1 + mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; + */ + + //predict voltage behind transient reactance (with trapezoidal rule) + mEdq_pred(0,0) = (**mVdq)(0,0) - mIdq_pred(1,0) * mLq_t; + mEdq_pred(1,0) = (**mVdq)(1,0) + mIdq_pred(0,0) * mLd_t; + mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; + + // armature currents for at t=k+1 + mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; + + // convert currents into the abc reference frame + Complex Idqpred = (mKvbr * mIdq_corr)(0,0) * mBase_I_RMS; + Matrix Idq_pred = Matrix::Zero(2,1); + Idq_pred(0,0) = Idqpred.real(); + Idq_pred(1,0) = Idqpred.imag(); + + // + Matrix resistanceMatrix = Matrix::Zero(2,2); + resistanceMatrix(0,0) = mKa_1ph.real() + mKb_1ph.real(); + resistanceMatrix(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrix(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrix(1,1) = mKa_1ph.real() - mKb_1ph.real(); + + // convert Edq_t into the abc reference frame + **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); + + // + **mEvbr += - Complex(mBase_Z * (resistanceMatrix * Idq_pred)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (resistanceMatrix * Idq_pred)(1,0)); + + // stamp currents + (**mRightVector).setZero(); + mnaApplyRightSideVectorStamp(**mRightVector); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector) { + mVdq_prev = **mVdq; + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); + + // + Matrix parkTransform = get_parkTransformMatrix(); + + // convert armature voltage into dq reference frame + MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix 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 current into dq reference frame + MatrixComp Iabc_ = (**mIntfCurrent)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix Iabc = Matrix(3,1); + Iabc << Iabc_(0,0).real(), Iabc_(1,0).real(), Iabc_(2,0).real(); + mIdq_pred = parkTransform * Iabc / mBase_I_RMS; +} + +bool DP::Ph1::SynchronGenerator4OrderTPM::checkVoltageDifference() { + if (**mNumIter == 0) + // if no corrector step has been performed yet + return true; + + Matrix voltageDifference = **mVdq - mVdq_prev; + 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::SynchronGenerator4OrderTPM::mnaPostStep(const Matrix& leftVector) { + // update armature voltage and current + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); + + // convert armature voltage into dq reference frame + Matrix parkTransform = get_parkTransformMatrix(); + MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix 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 current into dq reference frame + MatrixComp Iabc_ = (**mIntfCurrent)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); + Matrix 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::SynchronGenerator4OrderTPM::get_parkTransformMatrix() { + 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; +} diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index cc8f9766fa..5f0c2110ae 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -32,8 +32,8 @@ int main(int argc, char* argv[]) { Real tolerance = defaultConfig.tolerance; int maxIter = defaultConfig.maxIter; String SGModel = defaultConfig.sgType + "Iter"; - SGModel = "4Iter"; - NumericalMethod numericalMethod = NumericalMethod::Euler; + SGModel = "4Iter"; // options: "4Iter", "4TPM", "6Iter" + NumericalMethod numericalMethod = NumericalMethod::Euler; // only for "4Iter" or "6Iter" // Command line args processing CommandLineArgs args(argc, argv); @@ -152,6 +152,7 @@ int main(int argc, char* argv[]) { std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); std::dynamic_pointer_cast(genDP)->setNumericalMethod(numericalMethod); + //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); extnetDP->setParameters(gridParams.VnomMV); From 9e26dc9f1d30bb05423664b512c96296351cc2fe Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Tue, 10 Jan 2023 12:00:26 +0100 Subject: [PATCH 11/68] rename SGIter -> SGPCM Signed-off-by: Martin Moraga --- .../include/dpsim-models/Components.h | 8 ++--- ....h => DP_Ph1_SynchronGenerator4OrderPCM.h} | 8 ++--- ....h => DP_Ph1_SynchronGenerator6OrderPCM.h} | 8 ++--- .../include/dpsim-models/Definitions.h | 2 +- ...h => EMT_Ph3_SynchronGenerator4OrderPCM.h} | 8 ++--- dpsim-models/include/dpsim-models/Factory.h | 8 ++--- dpsim-models/src/CIM/Reader.cpp | 16 +++++----- dpsim-models/src/CMakeLists.txt | 6 ++-- ... => DP_Ph1_SynchronGenerator4OrderPCM.cpp} | 28 ++++++++-------- ... => DP_Ph1_SynchronGenerator6OrderPCM.cpp} | 28 ++++++++-------- ...=> EMT_Ph3_SynchronGenerator4OrderPCM.cpp} | 32 +++++++++---------- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 4 +-- 12 files changed, 78 insertions(+), 78 deletions(-) rename dpsim-models/include/dpsim-models/DP/{DP_Ph1_SynchronGenerator4OrderIter.h => DP_Ph1_SynchronGenerator4OrderPCM.h} (83%) rename dpsim-models/include/dpsim-models/DP/{DP_Ph1_SynchronGenerator6OrderIter.h => DP_Ph1_SynchronGenerator6OrderPCM.h} (82%) rename dpsim-models/include/dpsim-models/EMT/{EMT_Ph3_SynchronGenerator4OrderIter.h => EMT_Ph3_SynchronGenerator4OrderPCM.h} (88%) rename dpsim-models/src/DP/{DP_Ph1_SynchronGenerator4OrderIter.cpp => DP_Ph1_SynchronGenerator4OrderPCM.cpp} (88%) rename dpsim-models/src/DP/{DP_Ph1_SynchronGenerator6OrderIter.cpp => DP_Ph1_SynchronGenerator6OrderPCM.cpp} (90%) rename dpsim-models/src/EMT/{EMT_Ph3_SynchronGenerator4OrderIter.cpp => EMT_Ph3_SynchronGenerator4OrderPCM.cpp} (85%) diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 3a46258a26..db4ad58173 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -58,13 +58,13 @@ #include #include #include -#include -#include #include +#include +#include #include #include #include -#include +#include #include #include #include @@ -102,7 +102,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h similarity index 83% rename from dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h rename to dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 7a30cb0ccf..4341db039c 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -18,15 +18,15 @@ namespace Ph1 { /// /// This model is based on Eremia section 2.1.6. /// Modeling approach: delayed current injection + predictor corrector method - class SynchronGenerator4OrderIter : + class SynchronGenerator4OrderPCM : public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, - public SharedFactory { + public SharedFactory { public: /// - SynchronGenerator4OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator4OrderPCM(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); /// - SynchronGenerator4OrderIter(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); diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h similarity index 82% rename from dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h rename to dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 09b8b5d77c..502901f70a 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -17,15 +17,15 @@ namespace Ph1 { /// @brief 6 Order Synchronous generator model for transient stability analysis /// /// This model is based on Eremia section 2.1.6. - class SynchronGenerator6OrderIter : + class SynchronGenerator6OrderPCM : public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, - public SharedFactory { + public SharedFactory { public: /// - SynchronGenerator6OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator6OrderPCM(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); /// - SynchronGenerator6OrderIter(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); diff --git a/dpsim-models/include/dpsim-models/Definitions.h b/dpsim-models/include/dpsim-models/Definitions.h index 22605dd87f..cab5728583 100644 --- a/dpsim-models/include/dpsim-models/Definitions.h +++ b/dpsim-models/include/dpsim-models/Definitions.h @@ -108,7 +108,7 @@ 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, SG4OrderIter, SG6OrderIter, None}; + enum class GeneratorType {PVNode, IdealVoltageSource, IdealCurrentSource, TransientStability, FullOrder, FullOrderVBR, SG6aOrderVBR, SG6bOrderVBR, SG4OrderVBR, SG3OrderVBR, SG4OrderPCM, SG6OrderPCM, None}; enum class SGOrder {SG3Order, SG4Order, SG6aOrder, SG6bOrder}; // ### Exceptions ### diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h similarity index 88% rename from dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h rename to dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h index a1400afd96..d79b650f05 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderIter.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -17,10 +17,10 @@ namespace Ph3 { /// @brief 4 Order Synchronous generator model for transient stability analysis /// /// This model is based on Eremia section 2.1.6. - class SynchronGenerator4OrderIter : + class SynchronGenerator4OrderPCM : public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, - public SharedFactory { + public SharedFactory { protected: @@ -63,9 +63,9 @@ namespace Ph3 { public: /// - SynchronGenerator4OrderIter(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); + SynchronGenerator4OrderPCM(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); /// - SynchronGenerator4OrderIter(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); diff --git a/dpsim-models/include/dpsim-models/Factory.h b/dpsim-models/include/dpsim-models/Factory.h index 8a04094431..7b35a2b52f 100644 --- a/dpsim-models/include/dpsim-models/Factory.h +++ b/dpsim-models/include/dpsim-models/Factory.h @@ -3,9 +3,9 @@ #include #include -#include +#include #include -#include +#include #pragma once @@ -90,9 +90,9 @@ namespace SynchronGeneratorFactory { namespace DP { namespace Ph1 { void registerSynchronGenerators() { - FactoryRegistration> _4OrderDPIter("4Iter", new DerivedCreator>); + FactoryRegistration> _4OrderDPIter("4PCM", new DerivedCreator>); FactoryRegistration> _4OrderDPTPM("4TPM", new DerivedCreator>); - FactoryRegistration> _6OrderDPIter("6Iter", new DerivedCreator>); + FactoryRegistration> _6OrderDPIter("6PCM", new DerivedCreator>); } } } diff --git a/dpsim-models/src/CIM/Reader.cpp b/dpsim-models/src/CIM/Reader.cpp index b8350fe703..cb32594b24 100644 --- a/dpsim-models/src/CIM/Reader.cpp +++ b/dpsim-models/src/CIM/Reader.cpp @@ -525,8 +525,8 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin || mGeneratorType == GeneratorType::SG6bOrderVBR || mGeneratorType == GeneratorType::SG4OrderVBR || mGeneratorType == GeneratorType::SG3OrderVBR - || mGeneratorType == GeneratorType::SG4OrderIter - || mGeneratorType == GeneratorType::SG6OrderIter) { + || mGeneratorType == GeneratorType::SG4OrderPCM + || mGeneratorType == GeneratorType::SG6OrderPCM) { Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M); Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k); @@ -596,15 +596,15 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Td0_t); return gen; - } else if (mGeneratorType == GeneratorType::SG4OrderIter) { - mSLog->info(" GeneratorType is SynchronGenerator4OrderIter."); - auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); + } 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::SG6OrderIter) { - mSLog->info(" GeneratorType is SynchronGenerator6OrderIter."); - auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); + } 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; diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 546634f7a9..bd30bb4ff8 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -38,10 +38,10 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp - DP/DP_Ph1_SynchronGenerator4OrderIter.cpp + DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp - DP/DP_Ph1_SynchronGenerator6OrderIter.cpp + DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp DP/DP_Ph1_Inverter.cpp @@ -91,7 +91,7 @@ list(APPEND MODELS_SOURCES EMT/EMT_Ph3_SynchronGenerator6aOrderVBR.cpp EMT/EMT_Ph3_SynchronGenerator6bOrderVBR.cpp EMT/EMT_Ph3_SynchronGeneratorDQ.cpp - EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp + EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp EMT/EMT_Ph3_SynchronGeneratorDQTrapez.cpp # EMT/EMT_Ph3_SynchronGeneratorDQSmpl.cpp # EMT/EMT_Ph3_SynchronGeneratorDQSmplCompSource.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp similarity index 88% rename from dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp rename to dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index efdf8dd6f7..1e21d0dd08 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -6,11 +6,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *********************************************************************************/ -#include +#include using namespace CPS; -DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter +DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& uid, const String& name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { @@ -27,18 +27,18 @@ DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter mEdq_corr = Matrix::Zero(2,1); } -DP::Ph1::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter +DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& name, Logger::Level logLevel) - : SynchronGenerator4OrderIter(name, name, logLevel) { + : SynchronGenerator4OrderPCM(name, name, logLevel) { } -SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderIter::clone(const String& name) { - auto copy = SynchronGenerator4OrderIter::make(name, mLogLevel); +SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderPCM::clone(const String& name) { + auto copy = SynchronGenerator4OrderPCM::make(name, mLogLevel); return copy; } -void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { +void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { // calculate state representation matrix calculateStateMatrix(); @@ -66,7 +66,7 @@ void DP::Ph1::SynchronGenerator4OrderIter::specificInitialization() { mSLog->flush(); } -void DP::Ph1::SynchronGenerator4OrderIter::calculateStateMatrix() { +void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { // Initialize matrix of state representation of predictor step if (mNumericalMethod == CPS::NumericalMethod::Euler) { mA_euler = Matrix::Zero(2,2); @@ -124,7 +124,7 @@ void DP::Ph1::SynchronGenerator4OrderIter::calculateStateMatrix() { (mTimeStep / mTd0_t); } -void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { +void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; @@ -167,11 +167,11 @@ void DP::Ph1::SynchronGenerator4OrderIter::stepInPerUnit() { (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; } -void DP::Ph1::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator4OrderPCM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); } -void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { +void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; if (mSimTime>0.0) { @@ -205,7 +205,7 @@ void DP::Ph1::SynchronGenerator4OrderIter::correctorStep() { mnaApplyRightSideVectorStamp(**mRightVector); } -void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { mVdq_prev = **mVdq; (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); @@ -225,7 +225,7 @@ void DP::Ph1::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVecto mEdq_pred = mEdq_corr; } -bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { +bool DP::Ph1::SynchronGenerator4OrderPCM::checkVoltageDifference() { if (**mNumIter == 0) // if no corrector step has been performed yet return true; @@ -242,7 +242,7 @@ bool DP::Ph1::SynchronGenerator4OrderIter::checkVoltageDifference() { } } -void DP::Ph1::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator4OrderPCM::mnaPostStep(const Matrix& leftVector) { // update variables **mOmMech = mOmMech_corr; **mThetaMech = mThetaMech_corr; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp similarity index 90% rename from dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp rename to dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 1968d282fd..85c4a47684 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderIter.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -6,11 +6,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *********************************************************************************/ -#include +#include using namespace CPS; -DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter +DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM (const String& uid, const String& name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { @@ -27,18 +27,18 @@ DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter mEdq_corr = Matrix::Zero(4,1); } -DP::Ph1::SynchronGenerator6OrderIter::SynchronGenerator6OrderIter +DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM (const String& name, Logger::Level logLevel) - : SynchronGenerator6OrderIter(name, name, logLevel) { + : SynchronGenerator6OrderPCM(name, name, logLevel) { } -SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderIter::clone(const String& name) { - auto copy = SynchronGenerator6OrderIter::make(name, mLogLevel); +SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderPCM::clone(const String& name) { + auto copy = SynchronGenerator6OrderPCM::make(name, mLogLevel); return copy; } -void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { +void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { // calculate state representation matrix calculateStateMatrix(); @@ -72,7 +72,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::specificInitialization() { mSLog->flush(); } -void DP::Ph1::SynchronGenerator6OrderIter::calculateStateMatrix() { +void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { // Initialize matrix of state representation of predictor step if (mNumericalMethod == CPS::NumericalMethod::Euler) { mA_euler = Matrix::Zero(4,4); @@ -157,7 +157,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::calculateStateMatrix() { 0.0; } -void DP::Ph1::SynchronGenerator6OrderIter::stepInPerUnit() { +void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; @@ -196,11 +196,11 @@ void DP::Ph1::SynchronGenerator6OrderIter::stepInPerUnit() { (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; } -void DP::Ph1::SynchronGenerator6OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator6OrderPCM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); } -void DP::Ph1::SynchronGenerator6OrderIter::correctorStep() { +void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; @@ -235,7 +235,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::correctorStep() { mnaApplyRightSideVectorStamp(**mRightVector); } -void DP::Ph1::SynchronGenerator6OrderIter::updateVoltage(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector) { mVdq_prev = **mVdq; (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); @@ -249,7 +249,7 @@ void DP::Ph1::SynchronGenerator6OrderIter::updateVoltage(const Matrix& leftVecto **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; } -bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { +bool DP::Ph1::SynchronGenerator6OrderPCM::checkVoltageDifference() { if (**mNumIter == 0) // if no corrector step has been performed yet return true; @@ -273,7 +273,7 @@ bool DP::Ph1::SynchronGenerator6OrderIter::checkVoltageDifference() { } } -void DP::Ph1::SynchronGenerator6OrderIter::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator6OrderPCM::mnaPostStep(const Matrix& leftVector) { // update variables **mOmMech = mOmMech_corr; **mThetaMech = mThetaMech_corr; diff --git a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp similarity index 85% rename from dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp rename to dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp index e8bdd684db..be1568e071 100644 --- a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderIter.cpp +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp @@ -6,11 +6,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *********************************************************************************/ -#include +#include using namespace CPS; -EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter +EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& uid, const String& name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), mEdq0_t(Attribute::create("Edq0_t", mAttributes)) { @@ -32,18 +32,18 @@ EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter mNumIter = Attribute::create("NIterations", mAttributes, 0); } -EMT::Ph3::SynchronGenerator4OrderIter::SynchronGenerator4OrderIter +EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& name, Logger::Level logLevel) - : SynchronGenerator4OrderIter(name, name, logLevel) { + : SynchronGenerator4OrderPCM(name, name, logLevel) { } -SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderIter::clone(const String& name) { - auto copy = SynchronGenerator4OrderIter::make(name, mLogLevel); +SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderPCM::clone(const String& name) { + auto copy = SynchronGenerator4OrderPCM::make(name, mLogLevel); return copy; } -void EMT::Ph3::SynchronGenerator4OrderIter::specificInitialization() { +void EMT::Ph3::SynchronGenerator4OrderPCM::specificInitialization() { // Initialize matrix of state representation mA = Matrix::Zero(3,3); @@ -71,7 +71,7 @@ void EMT::Ph3::SynchronGenerator4OrderIter::specificInitialization() { mSLog->flush(); } -void EMT::Ph3::SynchronGenerator4OrderIter::calculateStateMatrix() { +void EMT::Ph3::SynchronGenerator4OrderPCM::calculateStateMatrix() { if (mVoltageForm) { Real Td_t = mTd0_t * (mLd_t / mLd); Real Tq_t = mTq0_t * (mLq_t / mLq); @@ -98,7 +98,7 @@ void EMT::Ph3::SynchronGenerator4OrderIter::calculateStateMatrix() { } } -void EMT::Ph3::SynchronGenerator4OrderIter::stepInPerUnit() { +void EMT::Ph3::SynchronGenerator4OrderPCM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = **mNumIter + 1; @@ -137,13 +137,13 @@ void EMT::Ph3::SynchronGenerator4OrderIter::stepInPerUnit() { **mIntfCurrent = **mIntfCurrent * mBase_I; } -void EMT::Ph3::SynchronGenerator4OrderIter::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void EMT::Ph3::SynchronGenerator4OrderPCM::mnaApplyRightSideVectorStamp(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::SynchronGenerator4OrderIter::correctorStep() { +void EMT::Ph3::SynchronGenerator4OrderPCM::correctorStep() { // corrector step (trapezoidal rule) if (**mNumIter == 1) @@ -185,7 +185,7 @@ void EMT::Ph3::SynchronGenerator4OrderIter::correctorStep() { mnaApplyRightSideVectorStamp(**mRightVector); } -void EMT::Ph3::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVector) { +void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { mVdq0_prev = **mVdq0; (**mIntfVoltage)(0, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0)); @@ -201,7 +201,7 @@ void EMT::Ph3::SynchronGenerator4OrderIter::updateVoltage(const Matrix& leftVect **mVdq0 = **mVdq0 / mBase_V; } -bool EMT::Ph3::SynchronGenerator4OrderIter::checkVoltageDifference() { +bool EMT::Ph3::SynchronGenerator4OrderPCM::checkVoltageDifference() { if (**mNumIter == 0) { // if no corrector step has been performed yet **mNumIter = 1; @@ -219,7 +219,7 @@ bool EMT::Ph3::SynchronGenerator4OrderIter::checkVoltageDifference() { return false; } -void EMT::Ph3::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector) { +void EMT::Ph3::SynchronGenerator4OrderPCM::mnaPostStep(const Matrix& leftVector) { // update variables **mEdq0_t = mEdq0_t_corr; **mOmMech = mOmMech_corr; @@ -227,7 +227,7 @@ void EMT::Ph3::SynchronGenerator4OrderIter::mnaPostStep(const Matrix& leftVector **mDelta = mDelta_corr; } -Matrix EMT::Ph3::SynchronGenerator4OrderIter::parkTransform(Real theta, const Matrix& abcVector) { +Matrix EMT::Ph3::SynchronGenerator4OrderPCM::parkTransform(Real theta, const Matrix& abcVector) { Matrix dq0Vector(3, 1); Matrix abcToDq0(3, 3); @@ -242,7 +242,7 @@ Matrix EMT::Ph3::SynchronGenerator4OrderIter::parkTransform(Real theta, const Ma return dq0Vector; } -Matrix EMT::Ph3::SynchronGenerator4OrderIter::inverseParkTransform(Real theta, const Matrix& dq0Vector) { +Matrix EMT::Ph3::SynchronGenerator4OrderPCM::inverseParkTransform(Real theta, const Matrix& dq0Vector) { Matrix abcVector(3, 1); Matrix dq0ToAbc(3, 3); diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index 5f0c2110ae..e42fea9a04 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -32,8 +32,8 @@ int main(int argc, char* argv[]) { Real tolerance = defaultConfig.tolerance; int maxIter = defaultConfig.maxIter; String SGModel = defaultConfig.sgType + "Iter"; - SGModel = "4Iter"; // options: "4Iter", "4TPM", "6Iter" - NumericalMethod numericalMethod = NumericalMethod::Euler; // only for "4Iter" or "6Iter" + SGModel = "4PCM"; // options: "4PCM", "4TPM", "6PCM" + NumericalMethod numericalMethod = NumericalMethod::Euler; // only for "4PCM" or "6PCM" // Command line args processing CommandLineArgs args(argc, argv); From 79bbd40f473516180d4f20aad2fac43aa3abc4dd Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Tue, 10 Jan 2023 13:10:46 +0100 Subject: [PATCH 12/68] add SG TPM to CIM reader Signed-off-by: Martin Moraga --- dpsim-models/include/dpsim-models/Definitions.h | 2 +- dpsim-models/src/CIM/Reader.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dpsim-models/include/dpsim-models/Definitions.h b/dpsim-models/include/dpsim-models/Definitions.h index cab5728583..5d133673d9 100644 --- a/dpsim-models/include/dpsim-models/Definitions.h +++ b/dpsim-models/include/dpsim-models/Definitions.h @@ -108,7 +108,7 @@ 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, SG4OrderPCM, SG6OrderPCM, 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 ### diff --git a/dpsim-models/src/CIM/Reader.cpp b/dpsim-models/src/CIM/Reader.cpp index cb32594b24..1af5994962 100644 --- a/dpsim-models/src/CIM/Reader.cpp +++ b/dpsim-models/src/CIM/Reader.cpp @@ -526,6 +526,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin || mGeneratorType == GeneratorType::SG4OrderVBR || mGeneratorType == GeneratorType::SG3OrderVBR || mGeneratorType == GeneratorType::SG4OrderPCM + || mGeneratorType == GeneratorType::SG4OrderTPM || mGeneratorType == GeneratorType::SG6OrderPCM) { Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M); @@ -602,6 +603,12 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin 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); From cab75ab5844f71cd579a93bd80e4bb893e97ae19 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Tue, 10 Jan 2023 13:11:40 +0100 Subject: [PATCH 13/68] add WSCC9bus example using iterative SG models Signed-off-by: Martin Moraga --- .../CIM/DP_WSCC9bus_SGReducedOrderIter.cpp | 188 ++++++++++++++++++ dpsim/examples/cxx/CMakeLists.txt | 1 + ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 2 +- .../EMT_SynGen4OrderIter_SMIB_Fault.cpp | 2 +- 4 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp 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..123cc3b8f3 --- /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 sgType = "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("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"); + + if (args.options.find("inertiaScalingFactor") != args.options.end()) + inertiaScalingFactor = args.getOptionReal("inertiaScalingFactor"); + + if (args.options.find("logDirectory") != args.options.end()) + logDirectory = args.getOptionString("logDirectory"); + } + + // 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 (sgType=="4PCM") + sys = reader2.loadCIM(60, filenames, Domain::DP, PhaseType::Single, CPS::GeneratorType::SG4OrderPCM); + else if (sgType=="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")); + } + } + + Simulation sim(simName, logLevel); + sim.setSystem(sys); + sim.setDomain(Domain::DP); + sim.setSolverType(Solver::Type::MNA); + sim.setTimeStep(timeStep); + sim.setFinalTime(finalTime); + sim.setMnaSolverImplementation(MnaSolverFactory::MnaSolverImpl::EigenSparse); + 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/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 16aa7e4832..1a75147435 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -175,6 +175,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 diff --git a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp index 154e3f0ede..91068a4373 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -128,7 +128,7 @@ int main(int argc, char* argv[]) { auto n2EMT = SimNode::make("n2EMT", PhaseType::ABC, initialVoltage_n2); // Synchronous generator - auto genEMT = EMT::Ph3::SynchronGenerator4OrderIter::make("SynGen", logLevel); + auto genEMT = EMT::Ph3::SynchronGenerator4OrderPCM::make("SynGen", logLevel); genEMT->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, diff --git a/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp index 2113b3f54a..f32699be68 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp @@ -105,7 +105,7 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea auto n2EMT = SimNode::make("n2EMT", PhaseType::ABC, initialVoltage_n2); // Components - auto genEMT = EMT::Ph3::SynchronGenerator4OrderIter::make("SynGen", logLevel); + auto genEMT = EMT::Ph3::SynchronGenerator4OrderPCM::make("SynGen", logLevel); genEMT->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, From 25cd911b8f9f641edb6453c26526f2ac427df270 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Tue, 7 Feb 2023 15:43:17 +0100 Subject: [PATCH 14/68] fix downsampling configuration in dp smib load step example with pcm model Signed-off-by: Jan Dinkelbach --- .../cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index e42fea9a04..c9bea78203 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -66,7 +66,7 @@ int main(int argc, char* argv[]) { // apply downsampling for simulation step sizes lower than 10us Real logDownSampling; - if (timeStep < 1e-6) + if (timeStep < 10e-6) logDownSampling = floor((10e-6) / timeStep); else logDownSampling = 1.0; From ea3319dd91a9eb43d16f250541ccffa923c55408 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Wed, 8 Feb 2023 18:37:57 +0100 Subject: [PATCH 15/68] enable zero iterations with tpm syngen Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 6 +- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 12 +- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 12 +- .../DP/DP_Ph1_SynchronGeneratorIter.h | 14 +-- .../EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h | 12 +- .../dpsim-models/Solver/MNASyncGenInterface.h | 12 +- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 22 ++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 55 ++++----- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 16 +-- .../EMT_Ph3_SynchronGenerator4OrderPCM.cpp | 18 +-- dpsim/src/MNASolverDirect.cpp | 109 +++++++++--------- 11 files changed, 144 insertions(+), 144 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 4341db039c..4f22d55669 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -37,12 +37,12 @@ namespace Ph1 { void calculateStateMatrix(); /// void stepInPerUnit() override; - // + // void correctorStep() override; - /// + /// void updateVoltage(const Matrix& leftVector) override; /// - bool checkVoltageDifference() override; + bool requiresIteration() override; // #### MNA Functions #### /// diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index 8727ae6fa9..d7ca459577 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -70,7 +70,7 @@ namespace Ph1 { Real mAd; /// Real mAq; - /// + /// Real mBd; /// Real mBq; @@ -96,14 +96,14 @@ namespace Ph1 { void stepInPerUnit() override; /// void correctorStep() override; - /// + /// void updateVoltage(const Matrix& leftVector) override; /// - bool checkVoltageDifference() override; + bool requiresIteration() override; /// Calculate Ka, Kb and Kvbr void calculateAuxiliarVariables(); /// - Matrix get_parkTransformMatrix(); + Matrix get_parkTransformMatrix(); // #### MNA Functions #### /// @@ -113,12 +113,12 @@ namespace Ph1 { /// void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override; - void setOperationalParametersPerUnit(Real nomPower, + 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 index 502901f70a..14c93e2b86 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -36,19 +36,19 @@ namespace Ph1 { void calculateStateMatrix(); /// void stepInPerUnit() override; - // + // void correctorStep() override; - /// + /// void updateVoltage(const Matrix& leftVector) override; /// - bool checkVoltageDifference() override; + bool requiresIteration() override; - // #### MNA Functions #### + // #### MNA Functions #### /// void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; - /// + /// void mnaPostStep(const Matrix& leftVector) override; - /// + /// void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override {}; }; } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h index 49cad04799..4db6c64552 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h @@ -20,23 +20,23 @@ namespace Ph1 { public MNASyncGenInterface { public: private: - + protected: - /// Constructor + /// 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 checkVoltageDifference(); + bool requiresIteration(); /// Matrix parkTransform(Real theta, const Matrix& abcVector); @@ -55,4 +55,4 @@ namespace Ph1 { }; } } -} \ No newline at end of file +} diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h index d79b650f05..bd36ae3c9c 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -40,7 +40,7 @@ namespace Ph3 { /// Real mElecTorque_corr; /// - Real mdOmMech = 0; + Real mdOmMech = 0; Real mdOmMech_corr = 0; Real mOmMech_pred; Real mOmMech_corr; @@ -76,12 +76,12 @@ namespace Ph3 { void calculateStateMatrix(); /// void stepInPerUnit(); - // + // void correctorStep(); - /// + /// void updateVoltage(const Matrix& leftVector); /// - bool checkVoltageDifference(); + bool requiresIteration(); /// Matrix parkTransform(Real theta, const Matrix& abcVector); /// @@ -90,9 +90,9 @@ namespace Ph3 { /// Setters /// - void useVoltageForm(bool state) {mVoltageForm = state;} + void useVoltageForm(bool state) {mVoltageForm = state;} - // #### MNA Functions #### + // #### MNA Functions #### void mnaApplyRightSideVectorStamp(Matrix& rightVector); void mnaPostStep(const Matrix& leftVector); void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 1a586d78be..8eabe245f4 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -73,7 +73,7 @@ namespace CPS { /// Vector to create abc vector from a component MatrixComp mShiftVector; - + /// Attribute::Ptr mNumIter; /// @@ -88,18 +88,18 @@ namespace CPS { typedef std::vector List; // Solver functions - /// + /// virtual void correctorStep()=0; /// virtual void updateVoltage(const Matrix& leftVector)=0; /// - virtual bool checkVoltageDifference() {return false;} + virtual bool requiresIteration() {return false;} /// Setters - /// + /// void setMaxIterations(Int maxIterations) {mMaxIter = maxIterations;} - /// - void setTolerance(Real Tolerance) {mTolerance = Tolerance;} + /// + void setTolerance(Real Tolerance) {mTolerance = Tolerance;} /// void setNumericalMethod(CPS::NumericalMethod numericalMethod) {mNumericalMethod = numericalMethod;} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 1e21d0dd08..1f83a0b491 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -16,7 +16,7 @@ DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM mPhaseType = PhaseType::Single; setTerminalNumber(1); - + /// initialize attributes mEdq = Attribute::create("Edq", mAttributes); mNumIter = Attribute::create("NIterations", mAttributes, 0); @@ -34,7 +34,7 @@ DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderPCM::clone(const String& name) { auto copy = SynchronGenerator4OrderPCM::make(name, mLogLevel); - + return copy; } @@ -83,7 +83,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { mC_trap = Matrix::Zero(4,1); mStates_trap_prev = Matrix::Zero(6,1); mStates_trap = Matrix::Zero(4,1); - + Real Ad = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); Real Bd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); @@ -118,7 +118,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { mB_corr = Matrix::Zero(2,2); mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; - + mC_corr = Matrix::Zero(2,1); mC_corr << 0.0, (mTimeStep / mTd0_t); @@ -146,13 +146,13 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) if (mNumericalMethod == CPS::NumericalMethod::Euler) { - //predict voltage behind transient reactance + //predict voltage behind transient reactance mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; // predict armature currents for at t=k+1 mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; - + } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { mStates_trap_prev << **mEdq, **mIdq, **mVdq; @@ -208,14 +208,14 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { mVdq_prev = **mVdq; (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - + // convert armature voltage into dq reference frame MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); if (**mNumIter == 0) **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; - else + else **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; mOmMech_pred = mOmMech_corr; @@ -225,7 +225,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector mEdq_pred = mEdq_corr; } -bool DP::Ph1::SynchronGenerator4OrderPCM::checkVoltageDifference() { +bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { if (**mNumIter == 0) // if no corrector step has been performed yet return true; @@ -234,7 +234,7 @@ bool DP::Ph1::SynchronGenerator4OrderPCM::checkVoltageDifference() { if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { if (**mNumIter >= mMaxIter) { return false; - } else { + } else { return true; } } else { @@ -249,4 +249,4 @@ void DP::Ph1::SynchronGenerator4OrderPCM::mnaPostStep(const Matrix& leftVector) **mDelta = mDelta_corr; **mEdq = mEdq_corr; **mIdq = mIdq_corr; -} \ No newline at end of file +} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 75ec8db02d..5c74d56798 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -15,7 +15,7 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), mEvbr(Attribute::create("Evbr", mAttributes)), mEdq_t(Attribute::create("Edq", mAttributes)) { - + mPhaseType = PhaseType::Single; setVirtualNodeNumber(2); setTerminalNumber(1); @@ -50,18 +50,18 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderTPM::clone(String name) { auto copy = SynchronGenerator4OrderTPM::make(name, mLogLevel); - + return copy; } -void DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit(Real nomPower, +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, + 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); @@ -71,17 +71,17 @@ void DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit(Real 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, + H, Ld, Lq, L0, Ld_t, Lq_t, Td0_t, Tq0_t); }; -void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarVariables() { - mKa = Matrix::Zero(1,3); +void DP::Ph1::SynchronGenerator4OrderTPM::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); + 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); @@ -116,7 +116,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { mB_corr = Matrix::Zero(2,2); mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; - + mC_corr = Matrix::Zero(2,1); mC_corr << 0.0, (mTimeStep / mTd0_t); @@ -203,12 +203,12 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { (**mIntfCurrent)(0,0) = std::conj(mInitElecPower / (mInitVoltage * mBase_V_RMS)); mIdq_2prev = **mIntfCurrent; } - + Matrix Idq_pred = Matrix::Zero(2,1); Idq_pred(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdq_2prev(0,0).real(); Idq_pred(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdq_2prev(0,0).imag(); - // + // Matrix resistanceMatrix = Matrix::Zero(2,2); resistanceMatrix(0,0) = mKa_1ph.real() + mKb_1ph.real(); resistanceMatrix(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); @@ -261,8 +261,8 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { Matrix Idq_pred = Matrix::Zero(2,1); Idq_pred(0,0) = Idqpred.real(); Idq_pred(1,0) = Idqpred.imag(); - - // + + // Matrix resistanceMatrix = Matrix::Zero(2,2); resistanceMatrix(0,0) = mKa_1ph.real() + mKb_1ph.real(); resistanceMatrix(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); @@ -285,7 +285,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector mVdq_prev = **mVdq; (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); - + // Matrix parkTransform = get_parkTransformMatrix(); @@ -302,20 +302,21 @@ void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector mIdq_pred = parkTransform * Iabc / mBase_I_RMS; } -bool DP::Ph1::SynchronGenerator4OrderTPM::checkVoltageDifference() { - if (**mNumIter == 0) - // if no corrector step has been performed yet +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; - - Matrix voltageDifference = **mVdq - mVdq_prev; - if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { - if (**mNumIter >= mMaxIter) { - return false; - } else { - return true; - } } else { - return false; + // check voltage convergence according to tolerance + Matrix voltageDifference = **mVdq - mVdq_prev; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) + return true; + else + return false; } } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 85c4a47684..0e791dff03 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -34,7 +34,7 @@ DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderPCM::clone(const String& name) { auto copy = SynchronGenerator6OrderPCM::make(name, mLogLevel); - + return copy; } @@ -121,7 +121,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { 0, -1, 0, 0, mLd_s, 0, 1, 0, 0, 0, 0, mLq_s; mA_trap_inv = mA_trap.inverse(); - + mB_trap = Matrix::Zero(6,8); mB_trap << Cd_s, 0, Bd_s, 0, 0, Ad_s, 0, 0, 0, Cq_s, 0, Bq_s, -Aq_s, 0, 0, 0, @@ -144,7 +144,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { 0.0, - mTimeStep / (2 * mTd0_t), 0.0, 0.0, mTimeStep / (2 * mTq0_s), 0.0, - mTimeStep / (2 * mTq0_s), 0.0, 0.0, mTimeStep / (2 * mTd0_s), 0.0, - mTimeStep / (2 * mTd0_s); - + mB_corr = Matrix::Zero(4,2); mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0, @@ -176,7 +176,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { } // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - if (mNumericalMethod == CPS::NumericalMethod::Euler) { + if (mNumericalMethod == CPS::NumericalMethod::Euler) { mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; // predict armature currents for at t=k+1 @@ -238,18 +238,18 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector) { mVdq_prev = **mVdq; (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - + // convert armature voltage into dq reference frame MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); if (**mNumIter == 0) **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; - else + else **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; } -bool DP::Ph1::SynchronGenerator6OrderPCM::checkVoltageDifference() { +bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { if (**mNumIter == 0) // if no corrector step has been performed yet return true; @@ -265,7 +265,7 @@ bool DP::Ph1::SynchronGenerator6OrderPCM::checkVoltageDifference() { mThetaMech_pred= mThetaMech_corr; mIdq_pred = mIdq_corr; mEdq_pred = mEdq_corr; - + return true; } } else { diff --git a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp index be1568e071..77e4ed4872 100644 --- a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp @@ -20,7 +20,7 @@ EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM // model flags mVoltageForm = false; - + // model variables **mEdq0_t = Matrix::Zero(3,1); mEdq0_t_pred = Matrix::Zero(3,1); @@ -39,7 +39,7 @@ EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderPCM::clone(const String& name) { auto copy = SynchronGenerator4OrderPCM::make(name, mLogLevel); - + return copy; } @@ -86,12 +86,12 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::calculateStateMatrix() { 0.0; } else { // Currents form - mA << -1./mTq0_t , 0.0, 0.0, + mA << -1./mTq0_t , 0.0, 0.0, 0.0 , -1/mTd0_t, 0.0, 0.0 , 0.0, 0.0; mB << 0.0 , (1. / mTq0_t) * (mLq-mLq_t), 0.0, (-1. / mTd0_t) * (mLd-mLd_t) , 0.0 , 0.0, - 0.0 , 0.0 , 0.0; + 0.0 , 0.0 , 0.0; mC << 0.0, (1./mTd0_t) * mEf, 0.0; @@ -120,7 +120,7 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::stepInPerUnit() { mThetaMech_pred = **mThetaMech; } - //predict voltage behind transient reactance + //predict voltage behind transient reactance if (mVoltageForm) mdEdq0_t = mA * **mEdq0_t + mB * **mVdq0 + mC; else @@ -185,9 +185,9 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::correctorStep() { mnaApplyRightSideVectorStamp(**mRightVector); } -void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { +void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { mVdq0_prev = **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)); @@ -201,13 +201,13 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVecto **mVdq0 = **mVdq0 / mBase_V; } -bool EMT::Ph3::SynchronGenerator4OrderPCM::checkVoltageDifference() { +bool EMT::Ph3::SynchronGenerator4OrderPCM::requiresIteration() { if (**mNumIter == 0) { // if no corrector step has been performed yet **mNumIter = 1; return true; } - + Matrix voltageDifference = **mVdq0 - mVdq0_prev; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { if (**mNumIter == mMaxIter) { diff --git a/dpsim/src/MNASolverDirect.cpp b/dpsim/src/MNASolverDirect.cpp index 60df21c74a..c9879df2d3 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -215,68 +215,67 @@ std::shared_ptr MnaSolverDirect::createLogTask() template void MnaSolverDirect::solve(Real time, Int timeStepCount) { - mIter = 0; - if (mSyncGen.size()==0) { - // Reset source vector - mRightSideVector.setZero(); - - // 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){ - 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()); - } else { - // if there is iterative syncGens, then it is necessary to iterate - bool iterate = true; - while (iterate) { - // - mIter = mIter + 1; + // Reset source vector + mRightSideVector.setZero(); - // Reset source vector - mRightSideVector.setZero(); + // Add together the right side vector (computed by the components' pre-step tasks) + for (auto stamp : mRightVectorStamps) + mRightSideVector += *stamp; - if (!mIsInInitialization) - MnaSolver::updateSwitchStatus(); + 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()); - } + 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()); + } + // 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) - //update voltages - syncGen->updateVoltage(**mLeftSideVector); - - // check if there is sync generators that need iterate - int count=0; - for (auto syncGen : mSyncGen) { - if (syncGen->checkVoltageDifference()) - count = count+1; - } - if (count==0) - iterate=false; + 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()); + } + + for (auto syncGen : mSyncGen) + syncGen->updateVoltage(**mLeftSideVector); } } + while (numCompsRequireIter > 0); } // TODO split into separate task? (dependent on x, updating all v attributes) From 2e7354072678db952cb2207795262237d01717f3 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Feb 2023 18:28:55 +0100 Subject: [PATCH 16/68] refactor tpm syngen model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 77 +++++++++---------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 5c74d56798..f310b20731 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -193,39 +193,39 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; } - // VBR history voltage + // Auxiliar variables for time-varying VBR history voltage and matrix calculateAuxiliarVariables(); - //mEh_vbr(0,0) = mAd * (**mIdq)(1,0) + mBd * (**mEdq_t)(0,0); - //mEh_vbr(1,0) = mAq * (**mIdq)(0,0) + mBq * (**mEdq_t)(1,0) + mCq; - // predict current + // set previous values of current at simulation start if (mSimTime==0.0) { (**mIntfCurrent)(0,0) = std::conj(mInitElecPower / (mInitVoltage * mBase_V_RMS)); mIdq_2prev = **mIntfCurrent; } - Matrix Idq_pred = Matrix::Zero(2,1); - Idq_pred(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdq_2prev(0,0).real(); - Idq_pred(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdq_2prev(0,0).imag(); + // predict current + Matrix IdpPrediction = Matrix::Zero(2,1); + IdpPrediction(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdq_2prev(0,0).real(); + IdpPrediction(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdq_2prev(0,0).imag(); - // - Matrix resistanceMatrix = Matrix::Zero(2,2); - resistanceMatrix(0,0) = mKa_1ph.real() + mKb_1ph.real(); - resistanceMatrix(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrix(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrix(1,1) = mKa_1ph.real() - mKb_1ph.real(); + // Determine time-varying part of resistance matrix + Matrix resistanceMatrixVarying = Matrix::Zero(2,2); + resistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); + resistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); mEh_vbr(0,0) = mAd * (**mIdq)(1,0) + mBd * (**mEdq_t)(0,0); mEh_vbr(1,0) = mAq * (**mIdq)(0,0) + mBq * (**mEdq_t)(1,0) + mCq; - // convert Edq_t into the abc reference frame + // convert Edq_t to dp domain **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); - // - **mEvbr += - Complex(mBase_Z * (resistanceMatrix * Idq_pred)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (resistanceMatrix * Idq_pred)(1,0)); + // Add current prediction based component to voltage behind reactance + **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpPrediction)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpPrediction)(1,0)); - // + // Store previous current for later use + // FIXME: Rename to dp mIdq_2prev = **mIntfCurrent; } @@ -237,17 +237,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; - /* - // calculate Edq_t corrected - mEdq_corr(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; - mEdq_corr(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - - // corrected currents at t=k+1 - mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; - */ - - //predict voltage behind transient reactance (with trapezoidal rule) + // predict voltage behind transient reactance (with trapezoidal rule) mEdq_pred(0,0) = (**mVdq)(0,0) - mIdq_pred(1,0) * mLq_t; mEdq_pred(1,0) = (**mVdq)(1,0) + mIdq_pred(0,0) * mLd_t; mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; @@ -257,24 +247,27 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; // convert currents into the abc reference frame - Complex Idqpred = (mKvbr * mIdq_corr)(0,0) * mBase_I_RMS; - Matrix Idq_pred = Matrix::Zero(2,1); - Idq_pred(0,0) = Idqpred.real(); - Idq_pred(1,0) = Idqpred.imag(); + Complex IdpPredictionComplex = (mKvbr * mIdq_corr)(0,0) * mBase_I_RMS; - // - Matrix resistanceMatrix = Matrix::Zero(2,2); - resistanceMatrix(0,0) = mKa_1ph.real() + mKb_1ph.real(); - resistanceMatrix(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrix(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrix(1,1) = mKa_1ph.real() - mKb_1ph.real(); + Matrix IdpPrediction = Matrix::Zero(2,1); + IdpPrediction(0,0) = IdpPredictionComplex.real(); + IdpPrediction(1,0) = IdpPredictionComplex.imag(); + + // Determine time-varying part of resistance matrix + // FIXME: No recomputation of mechanical vars in corrector step, + // so reuse of this save computation time + Matrix resistanceMatrixVarying = Matrix::Zero(2,2); + resistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); + resistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); // convert Edq_t into the abc reference frame **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); - // - **mEvbr += - Complex(mBase_Z * (resistanceMatrix * Idq_pred)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (resistanceMatrix * Idq_pred)(1,0)); + // Add current prediction based component to voltage behind reactance + **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpPrediction)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpPrediction)(1,0)); // stamp currents (**mRightVector).setZero(); From 3747a4eb598087bc732aee9ab9ade39dacd042b5 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Tue, 14 Feb 2023 10:32:29 +0100 Subject: [PATCH 17/68] revise args and logging of dp wscc reduced order iter Signed-off-by: Jan Dinkelbach --- .../CIM/DP_WSCC9bus_SGReducedOrderIter.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp index 123cc3b8f3..51a0b4af51 100644 --- a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp +++ b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) { String simName = "DP_WSCC9bus_SGReducedOrderIter"; Real timeStep = 1e-9; Real finalTime = 0.01; - String sgType = "4PCM"; // 4PCM or 4TPM + String SGModel = "4PCM"; // 4PCM or 4TPM Bool withFault = true; Real startTimeFault = 0.2; Real endTimeFault = 0.3; @@ -52,27 +52,26 @@ int main(int argc, char *argv[]) { if (args.name != "dpsim") simName = args.name; + if (args.options.find("logDirectory") != args.options.end()) + logDirectory = args.getOptionString("logDirectory"); - 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"); + 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("logDirectory") != args.options.end()) - logDirectory = args.getOptionString("logDirectory"); + 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 @@ -115,9 +114,9 @@ int main(int argc, char *argv[]) { CPS::CIM::Reader reader2(simName, logLevel, logLevel); SystemTopology sys; - if (sgType=="4PCM") + if (SGModel=="4PCM") sys = reader2.loadCIM(60, filenames, Domain::DP, PhaseType::Single, CPS::GeneratorType::SG4OrderPCM); - else if (sgType=="4TPM") + 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!"); @@ -126,7 +125,7 @@ int main(int argc, char *argv[]) { for (auto comp : sys.mComponents) { if (std::dynamic_pointer_cast>(comp)) { - std::dynamic_pointer_cast(comp)->setMaxIterations(maxIter); + std::dynamic_pointer_cast(comp)->setMaxIterations(maxIter); std::dynamic_pointer_cast(comp)->setTolerance(tolerance); } } @@ -162,6 +161,7 @@ int main(int argc, char *argv[]) { 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")); } } From bd0317db2de485f474c0eca448de1961400eb63c Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Tue, 7 Mar 2023 14:24:16 +0100 Subject: [PATCH 18/68] improve comments pcm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 62 ++++++++++++------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 1f83a0b491..0e17bba425 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -39,7 +39,6 @@ SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderPCM::clone(const Stri } void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { - // calculate state representation matrix calculateStateMatrix(); @@ -67,7 +66,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { } void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { - // Initialize matrix of state representation of predictor step + // Initialize matrices of state representation of predictor step if (mNumericalMethod == CPS::NumericalMethod::Euler) { mA_euler = Matrix::Zero(2,2); mA_euler << 1 - mTimeStep / mTq0_t, 0.0, @@ -78,8 +77,9 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { mC_euler = Matrix::Zero(2,1); mC_euler << 0.0, (mTimeStep / mTd0_t); - } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + // TODO: State space model applied here is different than the Euler representation. + // This needs harmonisation or further documentation. mC_trap = Matrix::Zero(4,1); mStates_trap_prev = Matrix::Zero(6,1); mStates_trap = Matrix::Zero(4,1); @@ -125,43 +125,43 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { } void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { - // set number of iteratios equal to zero + // set number of iterations equal to zero **mNumIter = 0; - // Predictor step (euler) - - // Predictor step (backward euler) - if (mSimTime>0.0) { - // calculate electrical torque at t=k-1 + // prediction of mechanical vars + if (mSimTime > 0.0) { + // calculate electrical torque at t=k **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - // predict mechanical variables at t=k + + // predict mechanical variables at t=k+1 (forward euler) mOmMech_pred = **mOmMech + mTimeStep / (2 * mH) * (**mMechTorque - **mElecTorque); mDelta_pred = **mDelta + mTimeStep * mBase_OmMech * (**mOmMech - 1); mThetaMech_pred = **mThetaMech + mTimeStep * **mOmMech * mBase_OmMech; } else { + // prediction by assuming constant mechanical vars at t=0 + // TODO: add further explanatory comment why this is required mOmMech_pred = **mOmMech; mDelta_pred = **mDelta; mThetaMech_pred = **mThetaMech; } - // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) + // prediction of electrical vars if (mNumericalMethod == CPS::NumericalMethod::Euler) { - //predict voltage behind transient reactance + // predict emf at t=k+1 (forward euler) mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; - // predict armature currents for at t=k+1 + // calculate stator currents at t=k+1 (assuming Vdq(k+1)=Vdq(k)) mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; - - } - else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { + // predict emfs and stator currents at t=k+1 (trapezoidal rule) mStates_trap_prev << **mEdq, **mIdq, **mVdq; mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * mEf; mEdq_pred << mStates_trap(0,0), mStates_trap(1,0); mIdq_pred << mStates_trap(2,0), mStates_trap(3,0); } - // convert currents into the abc reference frame + // convert currents to dp domain mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; @@ -172,31 +172,41 @@ void DP::Ph1::SynchronGenerator4OrderPCM::mnaApplyRightSideVectorStamp(Matrix& r } void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { - // corrector step (trapezoidal rule) + // TODO: The naming of variables becomes confusing. + // Probably better to use `PrevIter` and `CurrIter` + // variables instead of misusing `_pred` variables or `mVdq`. + + // increase number of iterations **mNumIter = **mNumIter + 1; + + // correction of mechanical vars if (mSimTime>0.0) { - // calculate electrical torque at t=k + // calculate electrical torque at t=k+1 mElecTorque_corr = (**mVdq)(0,0) * (mIdq_pred)(0,0) + (**mVdq)(1,0) * (mIdq_pred)(1,0); - // correct mechanical variables at t=k + + // correct mechanical variables at t=k+1 (trapezoidal rule) mOmMech_corr = **mOmMech + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorque_corr); mDelta_corr = **mDelta + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMech_pred - 2); + // CHECK: For mThetaMech_corr use mOmMech_corr already? mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_pred) * mBase_OmMech; - } else { + // correction by assuming constant mechanical vars at t=0 + // TODO: add further explanatory comment why this is required mElecTorque_corr = **mElecTorque; mOmMech_corr = mOmMech_pred; mDelta_corr = mDelta_pred; mThetaMech_corr = mThetaMech_pred; } - //predict voltage behind transient reactance + // correction of electrical vars + // correct emf at t=k+1 (trapezoidal rule) mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; - // armature currents for at t=k+1 + // calculate stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; - // convert currents into the abc reference frame + // convert currents to dp domain mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; @@ -206,6 +216,10 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { } void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { + // TODO: The naming of variables becomes confusing. + // Probably better to use `PrevIter` and `CurrIter` + // variables instead of misusing `_pred` variables or `mVdq`. + mVdq_prev = **mVdq; (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); From 446730b296e4fc79637ad3abb80d4b5ce72c5b21 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Tue, 7 Mar 2023 14:46:45 +0100 Subject: [PATCH 19/68] improve notebook name for sp vbr validation against psat Signed-off-by: Jan Dinkelbach --- ...=> SP_Validation_ReducedOrderSG_VBR_SMIB_Fault_withPSAT.ipynb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/Notebooks/Circuits/{SP_Validation_ReducedOrderSG_VBR_SMIB_Fault.ipynb => SP_Validation_ReducedOrderSG_VBR_SMIB_Fault_withPSAT.ipynb} (100%) 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 From 7103c11fe3e3cdf7cd9d8795321db38c25bcfb01 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Wed, 8 Mar 2023 10:30:22 +0100 Subject: [PATCH 20/68] improve comments tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 4 +- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 71 +++++++++++-------- dpsim/src/MNASolverDirect.cpp | 2 + 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 0e17bba425..62c7c97839 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -202,11 +202,11 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // correct emf at t=k+1 (trapezoidal rule) mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; - // calculate stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) + // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; - // convert currents to dp domain + // convert corrected currents to dp domain mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index f310b20731..6912b8c9a2 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -123,7 +123,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { } void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { - // initial voltage behind the transient reactance in the dq reference frame + // 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; calculateAuxiliarConstants(); @@ -181,46 +181,51 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // 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; - - if (mSimTime>0.0){ - // calculate mechanical variables at t=k+1 using forward euler + // predict mechanical vars + 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 - **mElecTorque)); + + // predict theta and delta at t=k+1 (backward euler) **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; } - // Auxiliar variables for time-varying VBR history voltage and matrix + // update auxiliar variables for time-varying VBR voltage and matrix depending on mThetaMech calculateAuxiliarVariables(); - // set previous values of current at simulation start - if (mSimTime==0.0) { + // determine time-varying part of resistance matrix + Matrix resistanceMatrixVarying = Matrix::Zero(2,2); + resistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); + resistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); + resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); + + // 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)); mIdq_2prev = **mIntfCurrent; } - // predict current + // predict stator current (linear extrapolation) Matrix IdpPrediction = Matrix::Zero(2,1); IdpPrediction(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdq_2prev(0,0).real(); IdpPrediction(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdq_2prev(0,0).imag(); - // Determine time-varying part of resistance matrix - Matrix resistanceMatrixVarying = Matrix::Zero(2,2); - resistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); - resistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); + // 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 VBR voltage (trapezoidal rule) mEh_vbr(0,0) = mAd * (**mIdq)(1,0) + mBd * (**mEdq_t)(0,0); mEh_vbr(1,0) = mAq * (**mIdq)(0,0) + mBq * (**mEdq_t)(1,0) + mCq; - // convert Edq_t to dp domain + // convert original VBR voltage to dp domain **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); - // Add current prediction based component to voltage behind reactance + // add current prediction based component to VBR voltage in dp domain **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpPrediction)(0,0), 0); **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpPrediction)(1,0)); @@ -234,24 +239,28 @@ void DP::Ph1::SynchronGenerator4OrderTPM::mnaApplyRightSideVectorStamp(Matrix& r } void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { - // corrector step (trapezoidal rule) + // increase number of iterations **mNumIter = **mNumIter + 1; - // predict voltage behind transient reactance (with trapezoidal rule) + // correct electrical vars + // CHECK: Is this computation really required? PCM models does + // simply mEdq_pred = mEdq_corr at the end of each iteration mEdq_pred(0,0) = (**mVdq)(0,0) - mIdq_pred(1,0) * mLq_t; mEdq_pred(1,0) = (**mVdq)(1,0) + mIdq_pred(0,0) * mLd_t; + + // correct emf at t=k+1 (trapezoidal rule) mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; - // armature currents for at t=k+1 + // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; - // convert currents into the abc reference frame - Complex IdpPredictionComplex = (mKvbr * mIdq_corr)(0,0) * mBase_I_RMS; + // convert corrected currents to dp domain + Complex IdpCorrectionComplex = (mKvbr * mIdq_corr)(0,0) * mBase_I_RMS; - Matrix IdpPrediction = Matrix::Zero(2,1); - IdpPrediction(0,0) = IdpPredictionComplex.real(); - IdpPrediction(1,0) = IdpPredictionComplex.imag(); + Matrix IdpCorrection = Matrix::Zero(2,1); + IdpCorrection(0,0) = IdpCorrectionComplex.real(); + IdpCorrection(1,0) = IdpCorrectionComplex.imag(); // Determine time-varying part of resistance matrix // FIXME: No recomputation of mechanical vars in corrector step, @@ -262,12 +271,12 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); - // convert Edq_t into the abc reference frame + // reset original VBR voltage in dp domain **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); - // Add current prediction based component to voltage behind reactance - **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpPrediction)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpPrediction)(1,0)); + // add current correction based component to VBR voltage in dp domain + **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpCorrection)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpCorrection)(1,0)); // stamp currents (**mRightVector).setZero(); diff --git a/dpsim/src/MNASolverDirect.cpp b/dpsim/src/MNASolverDirect.cpp index c9879df2d3..aaa9e9c1d4 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -271,6 +271,8 @@ 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); } From c9ba5979741a70ac07ff966ee8d32d47699b74e9 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Wed, 8 Mar 2023 14:41:24 +0100 Subject: [PATCH 21/68] fixes after rebase related to dcim/pcm/tpm models Signed-off-by: Jan Dinkelbach --- .../Base/Base_ReducedOrderSynchronGenerator.h | 16 --------- .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.h | 16 +++++---- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 8 +++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 10 +++--- .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.h | 20 ++++++----- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 8 +++-- .../DP/DP_Ph1_SynchronGeneratorIter.h | 8 ++--- .../EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h | 8 +++-- .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.h | 2 ++ .../Base_ReducedOrderSynchronGenerator.cpp | 6 ++-- .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp | 20 +++++------ .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 16 ++++----- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 18 +++++----- .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp | 24 ++++++------- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 18 +++++----- .../EMT_Ph3_SynchronGenerator4OrderPCM.cpp | 14 ++++---- .../CIM/DP_WSCC9bus_SGReducedOrderIter.cpp | 2 +- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 34 +++++++++---------- .../DP_SynGen4OrderDCIM_SMIB_Fault.cpp | 26 +++++++------- .../DP_SynGen6OrderDCIM_SMIB_Fault.cpp | 26 +++++++------- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 32 ++++++++--------- .../EMT_SynGen4OrderIter_SMIB_Fault.cpp | 28 +++++++-------- 22 files changed, 179 insertions(+), 181 deletions(-) diff --git a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h index ca38149c4f..b070f562eb 100644 --- a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h +++ b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h @@ -255,22 +255,6 @@ namespace Base { /// Signal component modelling voltage regulator and exciter std::shared_ptr mExciter; - /// - void setBaseParameters(Real nomPower, Real nomVolt, Real nomFreq); - /// Initialization for 3 Order SynGen - void setOperationalParametersPerUnit(Real nomPower, - Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, - Real Ld_t, Real Td0_t); - /// Initialization for 4 Order SynGen - 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); - /// Initialization for 6 Order SynGen - 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, - Real Ld_s, Real Lq_s, Real Td0_s, Real Tq0_s, - Real Taa=0); /// Real mTimeStep; Real mSimTime; diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h index a35354d159..f5ae43b308 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h @@ -22,7 +22,7 @@ namespace Ph1 { // ### State variables [p.u.]### /// voltage behing the transient reactance const Attribute::Ptr mEdq_t; - /// + /// Matrix mStates; Matrix mStates_prev; @@ -43,7 +43,7 @@ namespace Ph1 { Matrix mA_inv; /// Matrix mB; - /// + /// Matrix mC; /// Transformation matrix dp->dq @@ -57,15 +57,17 @@ namespace Ph1 { // #### General Functions #### /// DPecific component initialization - void specificInitialization(); + void specificInitialization(); /// void stepInPerUnit(); + /// + void initializeResistanceMatrix() final {}; // ### MNA Section ### /// - void mnaApplySystemMatrixStamp(Matrix& systemMatrix); - void mnaApplyRightSideVectorStamp(Matrix& rightVector); - void mnaPostStep(const Matrix& leftVector); + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); + void mnaCompPostStep(const Matrix& leftVector); public: /// @@ -77,4 +79,4 @@ namespace Ph1 { }; } } -} \ No newline at end of file +} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 4f22d55669..4b95018007 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -43,14 +43,16 @@ namespace Ph1 { void updateVoltage(const Matrix& leftVector) override; /// bool requiresIteration() override; + /// + void initializeResistanceMatrix() final {}; // #### MNA Functions #### /// - void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; /// - void mnaPostStep(const Matrix& leftVector) override; + void mnaCompPostStep(const Matrix& leftVector) override; /// - void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override {}; + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) override {}; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index d7ca459577..c96af4a6e7 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include namespace CPS { namespace DP { @@ -104,14 +104,16 @@ namespace Ph1 { void calculateAuxiliarVariables(); /// Matrix get_parkTransformMatrix(); + /// + void initializeResistanceMatrix() final {}; // #### MNA Functions #### /// - void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; /// - void mnaPostStep(const Matrix& leftVector) override; + void mnaCompPostStep(const Matrix& leftVector) override; /// - void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override; + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) override; void setOperationalParametersPerUnit(Real nomPower, Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h index 88ae4f25ff..f25edad1f1 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h @@ -24,7 +24,7 @@ namespace Ph1 { const Attribute::Ptr mEdq_t; /// voltage behind subtransient reactance const Attribute::Ptr mEdq_s; - /// + /// Matrix mStates; Matrix mStates_prev; @@ -43,9 +43,9 @@ namespace Ph1 { Real mAd_s; /// Real mAq_s; - /// + /// Real mBd_s; - /// + /// Real mBq_s; /// Real mCd_s; @@ -59,7 +59,7 @@ namespace Ph1 { Matrix mA_inv; /// Matrix mB; - /// + /// Matrix mC; /// Transformation matrix dp->dq @@ -73,15 +73,17 @@ namespace Ph1 { // #### General Functions #### /// DPecific component initialization - void specificInitialization(); + void specificInitialization(); /// void stepInPerUnit(); + /// + void initializeResistanceMatrix() final {}; // ### MNA Section ### /// - void mnaApplySystemMatrixStamp(Matrix& systemMatrix); - void mnaApplyRightSideVectorStamp(Matrix& rightVector); - void mnaPostStep(const Matrix& leftVector); + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); + void mnaCompPostStep(const Matrix& leftVector); public: /// @@ -93,4 +95,4 @@ namespace Ph1 { }; } } -} \ No newline at end of file +} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 14c93e2b86..cce907540f 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -42,14 +42,16 @@ namespace Ph1 { void updateVoltage(const Matrix& leftVector) override; /// bool requiresIteration() override; + /// + void initializeResistanceMatrix() final {}; // #### MNA Functions #### /// - void mnaApplyRightSideVectorStamp(Matrix& rightVector) override; + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; /// - void mnaPostStep(const Matrix& leftVector) override; + void mnaCompPostStep(const Matrix& leftVector) override; /// - void mnaApplySystemMatrixStamp(Matrix& systemMatrix) override {}; + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) override {}; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h index 4db6c64552..e3924e9c28 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h @@ -41,10 +41,10 @@ namespace Ph1 { Matrix parkTransform(Real theta, const Matrix& abcVector); // ### MNA Section ### - void mnaApplySystemMatrixStamp(Matrix& systemMatrix); - void mnaApplyRightSideVectorStamp(Matrix& rightVector); - void mnaPostStep(const Matrix& leftVector); - void mnaInitialize(Real omega, Real timeStep, Attribute::Ptr leftVector); + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); + void mnaCompPostStep(const Matrix& leftVector); + void mnaCompInitialize(Real omega, Real timeStep, Attribute::Ptr leftVector); public: virtual ~SynchronGeneratorIter(); diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h index bd36ae3c9c..5aa813f781 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -86,6 +86,8 @@ namespace Ph3 { Matrix parkTransform(Real theta, const Matrix& abcVector); /// Matrix inverseParkTransform(Real theta, const Matrix& dq0Vector); + /// + void initializeResistanceMatrix() final {}; /// Setters @@ -93,9 +95,9 @@ namespace Ph3 { void useVoltageForm(bool state) {mVoltageForm = state;} // #### MNA Functions #### - void mnaApplyRightSideVectorStamp(Matrix& rightVector); - void mnaPostStep(const Matrix& leftVector); - void mnaApplySystemMatrixStamp(Matrix& systemMatrix){}; + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); + void mnaCompPostStep(const Matrix& leftVector); + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix){}; }; } } diff --git a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h index b17cfbe2dc..3930ccd8cf 100644 --- a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h @@ -64,6 +64,8 @@ namespace Ph1 { void initializeResistanceMatrix() final {}; /// void stepInPerUnit() final; + /// + void calculateStateMatrix(); // ### MNA Section ### /// diff --git a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp index 9c8925cfb6..59f8a469f9 100644 --- a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp +++ b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp @@ -447,7 +447,7 @@ void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int stepInPerUnit(); (**mRightVector).setZero(); - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } template <> @@ -472,7 +472,7 @@ void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int ti stepInPerUnit(); (**mRightVector).setZero(); - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } template @@ -486,7 +486,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/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp index df663ef47f..eabcf2ad6d 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -13,7 +13,7 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM (String uid, String name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(Attribute::create("Edq_t", mAttributes)) { + mEdq_t(mAttributes->create("Edq_t")) { setTerminalNumber(1); @@ -34,7 +34,7 @@ DP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM } SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderDCIM::clone(String name) { - + auto copy = SynchronGenerator4OrderDCIM::make(name, mLogLevel); return copy; } @@ -65,7 +65,7 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { 0, -1, mLd_t, 0, 1, 0, 0, mLq_t; mA_inv = mA.inverse(); - + mB = Matrix::Zero(4,6); mB << mBd, 0, 0, mAd, 0, 0, 0, mBq, mAq, 0, 0, 0, @@ -74,7 +74,7 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { mC = Matrix::Zero(4,1); mC << 0, mCq, 0, 0; - + mSLog->info( "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" @@ -87,7 +87,7 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { mSLog->flush(); } -void DP::Ph1::SynchronGenerator4OrderDCIM::mnaApplySystemMatrixStamp(Matrix& systemMatrix) { +void DP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { } void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { @@ -101,7 +101,7 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) mStates_prev << mStates, **mVdq; - mStates = mA_inv * mB * mStates_prev + mA_inv * mC * mEf; + mStates = mA_inv * mB * mStates_prev + mA_inv * mC * (**mEf); **mEdq_t << mStates(0,0), mStates(1,0); **mIdq << mStates(2,0), mStates(3,0); @@ -111,14 +111,14 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; } -void DP::Ph1::SynchronGenerator4OrderDCIM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); } -void DP::Ph1::SynchronGenerator4OrderDCIM::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator4OrderDCIM::mnaCompPostStep(const Matrix& leftVector) { (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - + // convert armature voltage into dq reference frame MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); @@ -142,4 +142,4 @@ Matrix DP::Ph1::SynchronGenerator4OrderDCIM::parkTransform(Real theta, const Mat dq0Vector = abcToDq0 * abcVector; dqVector << dq0Vector(0,0), dq0Vector(1,0); return dqVector; -} \ No newline at end of file +} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 62c7c97839..61e314d898 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -18,8 +18,8 @@ DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM setTerminalNumber(1); /// initialize attributes - mEdq = Attribute::create("Edq", mAttributes); - mNumIter = Attribute::create("NIterations", mAttributes, 0); + mEdq = mAttributes->create("Edq"); + mNumIter = mAttributes->create("NIterations", 0); // Initialize matrix **mEdq = Matrix::Zero(2,1); @@ -148,7 +148,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // prediction of electrical vars if (mNumericalMethod == CPS::NumericalMethod::Euler) { // predict emf at t=k+1 (forward euler) - mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; + mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); // calculate stator currents at t=k+1 (assuming Vdq(k+1)=Vdq(k)) mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; @@ -156,7 +156,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { // predict emfs and stator currents at t=k+1 (trapezoidal rule) mStates_trap_prev << **mEdq, **mIdq, **mVdq; - mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * mEf; + mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * (**mEf); mEdq_pred << mStates_trap(0,0), mStates_trap(1,0); mIdq_pred << mStates_trap(2,0), mStates_trap(3,0); } @@ -167,7 +167,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; } -void DP::Ph1::SynchronGenerator4OrderPCM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); } @@ -200,7 +200,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) - mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; + mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * (**mEf); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; @@ -212,7 +212,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; // stamp currents - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { @@ -256,7 +256,7 @@ bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { } } -void DP::Ph1::SynchronGenerator4OrderPCM::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompPostStep(const Matrix& leftVector) { // update variables **mOmMech = mOmMech_corr; **mThetaMech = mThetaMech_corr; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 6912b8c9a2..dfec74798d 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -13,15 +13,15 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM (String uid, String name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEvbr(Attribute::create("Evbr", mAttributes)), - mEdq_t(Attribute::create("Edq", mAttributes)) { + mEvbr(mAttributes->create("Evbr")), + mEdq_t(mAttributes->create("Edq")) { mPhaseType = PhaseType::Single; setVirtualNodeNumber(2); setTerminalNumber(1); /// initialize attributes - mNumIter = Attribute::create("NIterations", mAttributes, 0); + mNumIter = mAttributes->create("NIterations", 0); // model variables **mIntfVoltage = MatrixComp::Zero(1, 1); @@ -97,7 +97,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mCq = 2 * mTimeStep * mEf / (2 * mTd0_t + mTimeStep); + mCq = 2 * mTimeStep * (**mEf) / (2 * mTd0_t + mTimeStep); mB = mLd_t - mAq; mA = -mLq_t - mAd; @@ -157,7 +157,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateConductanceMatrix() { mConductanceMatrix = resistanceMatrix.inverse(); } -void DP::Ph1::SynchronGenerator4OrderTPM::mnaApplySystemMatrixStamp(Matrix& systemMatrix) { +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { updateMatrixNodeIndices(); calculateConductanceMatrix(); // Stamp voltage source @@ -234,7 +234,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { mIdq_2prev = **mIntfCurrent; } -void DP::Ph1::SynchronGenerator4OrderTPM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), **mEvbr); } @@ -249,7 +249,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { mEdq_pred(1,0) = (**mVdq)(1,0) + mIdq_pred(0,0) * mLd_t; // correct emf at t=k+1 (trapezoidal rule) - mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; + mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * (**mEf); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; @@ -280,7 +280,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // stamp currents (**mRightVector).setZero(); - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector) { @@ -322,7 +322,7 @@ bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { } } -void DP::Ph1::SynchronGenerator4OrderTPM::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompPostStep(const Matrix& leftVector) { // update armature voltage and current (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp index fc72774ad6..efdf2a445f 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp @@ -13,8 +13,8 @@ using namespace CPS; DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM (String uid, String name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(Attribute::create("Edq_t", mAttributes)), - mEdq_s(Attribute::create("Edq_s", mAttributes)) { + mEdq_t(mAttributes->create("Edq_t")), + mEdq_s(mAttributes->create("Edq_s")) { setTerminalNumber(1); @@ -36,7 +36,7 @@ DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM } SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderDCIM::clone(String name) { - + auto copy = SynchronGenerator6OrderDCIM::make(name, mLogLevel); return copy; } @@ -45,7 +45,7 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::specificInitialization() { // initial voltage behind the transient reactance in the dq reference frame (**mEdq_t)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); - (**mEdq_t)(1,0) = mEf - (mLd - mLd_t) * (**mIdq)(0,0); + (**mEdq_t)(1,0) = (**mEf) - (mLd - mLd_t) * (**mIdq)(0,0); // initial dq behind the subtransient reactance in the dq reference frame (**mEdq_s)(0,0) = (**mVdq)(0,0) - mLq_s * (**mIdq)(1,0); @@ -80,7 +80,7 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::specificInitialization() { 0, -1, 0, 0, mLd_s, 0, 1, 0, 0, 0, 0, mLq_s; mA_inv = mA.inverse(); - + mB = Matrix::Zero(6,8); mB << mCd_s, 0, mBd_s, 0, 0, mAd_s, 0, 0, 0, mCq_s, 0, mBq_s, -mAq_s, 0, 0, 0, @@ -91,7 +91,7 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::specificInitialization() { mC = Matrix::Zero(6,1); mC << 0, 0, 0, mDq_t, 0, 0; - + mSLog->info( "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" @@ -108,7 +108,7 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::specificInitialization() { mSLog->flush(); } -void DP::Ph1::SynchronGenerator6OrderDCIM::mnaApplySystemMatrixStamp(Matrix& systemMatrix) { +void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { } void DP::Ph1::SynchronGenerator6OrderDCIM::stepInPerUnit() { @@ -122,7 +122,7 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::stepInPerUnit() { // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) mStates_prev << mStates, **mVdq; - mStates = mA_inv * mB * mStates_prev + mA_inv * mC * mEf; + mStates = mA_inv * mB * mStates_prev + mA_inv * mC * (**mEf); **mEdq_s << mStates(0,0), mStates(1,0); **mEdq_t << mStates(2,0), mStates(3,0); **mIdq << mStates(4,0), mStates(5,0); @@ -133,14 +133,14 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::stepInPerUnit() { (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; } -void DP::Ph1::SynchronGenerator6OrderDCIM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); } -void DP::Ph1::SynchronGenerator6OrderDCIM::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompPostStep(const Matrix& leftVector) { (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - + // convert armature voltage into dq reference frame MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); @@ -164,4 +164,4 @@ Matrix DP::Ph1::SynchronGenerator6OrderDCIM::parkTransform(Real theta, const Mat dq0Vector = abcToDq0 * abcVector; dqVector << dq0Vector(0,0), dq0Vector(1,0); return dqVector; -} \ No newline at end of file +} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 0e791dff03..495645e06a 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -18,8 +18,8 @@ DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM setTerminalNumber(1); /// initialize attributes - mEdq = Attribute::create("Edq", mAttributes); - mNumIter = Attribute::create("NIterations", mAttributes, 0); + mEdq = mAttributes->create("Edq"); + mNumIter = mAttributes->create("NIterations", 0); // Initialize matrix **mEdq = Matrix::Zero(4,1); @@ -45,7 +45,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { // initial voltage behind the transient reactance in the dq0 reference frame (**mEdq)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); - (**mEdq)(1,0) = - (mLd- mLd_t) * (**mIdq)(0,0) + mEf; + (**mEdq)(1,0) = - (mLd- mLd_t) * (**mIdq)(0,0) + (**mEf); (**mEdq)(2,0) = (**mEdq)(0,0) + (mLq_t - mLq_s) * (**mIdq)(1,0); (**mEdq)(3,0) = (**mEdq)(1,0) - (mLd_t - mLd_s) * (**mIdq)(0,0); @@ -177,7 +177,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) if (mNumericalMethod == CPS::NumericalMethod::Euler) { - mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * mEf; + mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); // predict armature currents for at t=k+1 mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; @@ -185,7 +185,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { mStates_trap_prev << **mEdq, **mIdq, **mVdq; - mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * mEf; + mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * (**mEf); mEdq_pred << mStates_trap(0,0), mStates_trap(1,0), mStates_trap(2,0), mStates_trap(3,0); mIdq_pred << mStates_trap(4,0), mStates_trap(5,0); } @@ -196,7 +196,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; } -void DP::Ph1::SynchronGenerator6OrderPCM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); } @@ -219,7 +219,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { } //predict voltage behind transient reactance - mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * mEf; + mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * (**mEf); // armature currents for at t=k+1 mIdq_corr(0,0) = (mEdq_corr(3,0) - (**mVdq)(1,0) ) / mLd_s; @@ -232,7 +232,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; // stamp currents - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector) { @@ -273,7 +273,7 @@ bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { } } -void DP::Ph1::SynchronGenerator6OrderPCM::mnaPostStep(const Matrix& leftVector) { +void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompPostStep(const Matrix& leftVector) { // update variables **mOmMech = mOmMech_corr; **mThetaMech = mThetaMech_corr; diff --git a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp index 77e4ed4872..4f1b612fd7 100644 --- a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp @@ -13,7 +13,7 @@ using namespace CPS; EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& uid, const String& name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq0_t(Attribute::create("Edq0_t", mAttributes)) { + mEdq0_t(mAttributes->create("Edq0_t")) { mPhaseType = PhaseType::ABC; setTerminalNumber(1); @@ -29,7 +29,7 @@ EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM mdEdq0_t_corr = Matrix::Zero(3,1); // Initialize attributes - mNumIter = Attribute::create("NIterations", mAttributes, 0); + mNumIter = mAttributes->create("NIterations", 0); } EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM @@ -82,7 +82,7 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::calculateStateMatrix() { 0.0, (1. / Td_t) * (mLd-mLd_t) / mLd, 0.0, 0.0, 0.0, 0.0; mC << 0.0, - (1. / Td_t) * mEf * (mLd_t / mLd), + (1. / Td_t) * (**mEf) * (mLd_t / mLd), 0.0; } else { // Currents form @@ -93,7 +93,7 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::calculateStateMatrix() { (-1. / mTd0_t) * (mLd-mLd_t) , 0.0 , 0.0, 0.0 , 0.0 , 0.0; mC << 0.0, - (1./mTd0_t) * mEf, + (1./mTd0_t) * (**mEf), 0.0; } } @@ -137,7 +137,7 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::stepInPerUnit() { **mIntfCurrent = **mIntfCurrent * mBase_I; } -void EMT::Ph3::SynchronGenerator4OrderPCM::mnaApplyRightSideVectorStamp(Matrix& rightVector) { +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)); @@ -182,7 +182,7 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::correctorStep() { **mIntfCurrent = **mIntfCurrent * mBase_I; // stamp currents - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { @@ -219,7 +219,7 @@ bool EMT::Ph3::SynchronGenerator4OrderPCM::requiresIteration() { return false; } -void EMT::Ph3::SynchronGenerator4OrderPCM::mnaPostStep(const Matrix& leftVector) { +void EMT::Ph3::SynchronGenerator4OrderPCM::mnaCompPostStep(const Matrix& leftVector) { // update variables **mEdq0_t = mEdq0_t_corr; **mOmMech = mOmMech_corr; diff --git a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp index 51a0b4af51..d7e049571b 100644 --- a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp +++ b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp @@ -171,7 +171,7 @@ int main(int argc, char *argv[]) { sim.setSolverType(Solver::Type::MNA); sim.setTimeStep(timeStep); sim.setFinalTime(finalTime); - sim.setMnaSolverImplementation(MnaSolverFactory::MnaSolverImpl::EigenSparse); + sim.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); sim.addLogger(logger); // Optionally add switch event diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index c9bea78203..5d5015a55b 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_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[]) { // initiaize gen factory SynchronGeneratorFactory::DP::Ph1::registerSynchronGenerators(); @@ -60,7 +60,7 @@ int main(int argc, char* argv[]) { 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::off; @@ -81,7 +81,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); @@ -91,10 +91,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); @@ -127,7 +127,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(); @@ -143,27 +143,27 @@ int main(int argc, char* argv[]) { // Synchronous generator auto genDP = Factory>::get().create(SGModel, "SynGen", logLevel); genDP->setOperationalParametersPerUnit( - syngenKundur.nomPower, syngenKundur.nomVoltage, - syngenKundur.nomFreq, H, + 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); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); + std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); std::dynamic_pointer_cast(genDP)->setNumericalMethod(numericalMethod); - + //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, + lineDP->setParameters(gridParams.lineResistance, + gridParams.lineInductance, gridParams.lineCapacitance, gridParams.lineConductance); - + // Topology genDP->connect({ n1DP }); lineDP->connect({ n1DP, n2DP }); @@ -195,11 +195,11 @@ int main(int argc, char* argv[]) { simDP.setTimeStep(timeStep); simDP.setFinalTime(finalTime); simDP.setDomain(Domain::DP); - simDP.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simDP.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); simDP.addLogger(logger); // Events simDP.addEvent(loadStepEvent); - + simDP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp index c3043d419d..e64051c60c 100644 --- a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp @@ -25,7 +25,7 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, //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); @@ -35,10 +35,10 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, 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); @@ -70,7 +70,7 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, // ----- Dynamic simulation ------ String simNameDP = simName; Logger::setLogDir("logs/" + simNameDP); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -88,9 +88,9 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, genDP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, syngenKundur.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)); //Grid bus as Slack @@ -99,9 +99,9 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, // 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 = DP::Ph1::Switch::make("Br_fault", logLevel); fault->setParameters(switchOpen, switchClosed); @@ -135,7 +135,7 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, simDP.setTimeStep(timeStep); simDP.setFinalTime(finalTime); simDP.setDomain(Domain::DP); - simDP.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simDP.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); simDP.addLogger(loggerDP); //simDP.doSystemMatrixRecomputation(true); @@ -145,11 +145,11 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, auto sw2 = SwitchEvent::make(endTimeFault, fault, false); simDP.addEvent(sw2); - + simDP.run(); } -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters Real SwitchClosed = GridParams.SwitchClosed; @@ -182,6 +182,6 @@ int main(int argc, char* argv[]) { logDownSampling = 1.0; Logger::Level logLevel = Logger::Level::off; std::string simName = "DP_SynGen4OrderDCIM_SMIB_Fault" + stepSize_str + inertia_str; - DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, + DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, logDownSampling, SwitchOpen, SwitchClosed, logLevel); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp index 26706a90fd..bd2db7a019 100644 --- a/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp @@ -25,7 +25,7 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, //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); @@ -35,10 +35,10 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, 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); @@ -70,7 +70,7 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, // ----- Dynamic simulation ------ String simNameDP = simName; Logger::setLogDir("logs/" + simNameDP); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -88,9 +88,9 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, genDP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, syngenKundur.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)); //Grid bus as Slack @@ -99,9 +99,9 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, // 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 = DP::Ph1::Switch::make("Br_fault", logLevel); fault->setParameters(switchOpen, switchClosed); @@ -136,7 +136,7 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, simDP.setTimeStep(timeStep); simDP.setFinalTime(finalTime); simDP.setDomain(Domain::DP); - simDP.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simDP.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); simDP.addLogger(loggerDP); //simDP.doSystemMatrixRecomputation(true); @@ -146,11 +146,11 @@ void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, auto sw2 = SwitchEvent::make(endTimeFault, fault, false); simDP.addEvent(sw2); - + simDP.run(); } -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters Real SwitchClosed = GridParams.SwitchClosed; @@ -183,6 +183,6 @@ int main(int argc, char* argv[]) { logDownSampling = 1.0; Logger::Level logLevel = Logger::Level::off; std::string simName = "DP_SynGen6OrderDCIM_SMIB_Fault" + stepSize_str + inertia_str; - DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, + DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, logDownSampling, SwitchOpen, SwitchClosed, logLevel); -} \ 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 index 91068a4373..abd63f67e0 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -15,7 +15,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 = "EMT_SMIB_ReducedOrderSGIterative_LoadStep"; @@ -62,7 +62,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); @@ -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", logLevel); - 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,12 +116,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 }; @@ -130,10 +130,10 @@ int main(int argc, char* argv[]) { // Synchronous generator auto genEMT = EMT::Ph3::SynchronGenerator4OrderPCM::make("SynGen", logLevel); genEMT->setOperationalParametersPerUnit( - syngenKundur.nomPower, syngenKundur.nomVoltage, - syngenKundur.nomFreq, H, + 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_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t); genEMT->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); genEMT->setMaxIterations(defaultConfig.maxIter); genEMT->setTolerance(defaultConfig.tolerance); @@ -143,11 +143,11 @@ int main(int argc, char* argv[]) { // 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 }); @@ -179,12 +179,12 @@ int main(int argc, char* argv[]) { simEMT.setTimeStep(timeStep); simEMT.setFinalTime(finalTime); simEMT.setDomain(Domain::EMT); - simEMT.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simEMT.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); simEMT.addLogger(logger); //simEMT.doSystemMatrixRecomputation(true); // 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 index f32699be68..609741218e 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp @@ -50,7 +50,7 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea 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); @@ -84,7 +84,7 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea // ----- Dynamic simulation ------ String simNameEMT = simName; Logger::setLogDir("logs/"+simNameEMT); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -92,13 +92,13 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea 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 }; @@ -109,27 +109,27 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea 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.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), + 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), + fault->setParameters(Math::singlePhaseParameterToThreePhase(switchOpen), Math::singlePhaseParameterToThreePhase(switchClosed)); fault->openSwitch(); @@ -162,7 +162,7 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea simEMT.setFinalTime(finalTime); simEMT.setDomain(Domain::EMT); simEMT.addLogger(loggerEMT); - simEMT.setMnaSolverImplementation(DPsim::MnaSolverFactory::EigenSparse); + simEMT.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); // Events auto sw1 = SwitchEvent3Ph::make(startTimeFault, fault, true); @@ -170,12 +170,12 @@ void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Rea auto sw2 = SwitchEvent3Ph::make(endTimeFault, fault, false); simEMT.addEvent(sw2); - + simEMT.run(); simEMT.logStepTimes(simNameEMT + "_step_times"); } -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Command line args processing CommandLineArgs args(argc, argv); @@ -220,4 +220,4 @@ int main(int argc, char* argv[]) { EMT_3ph_4OrderSynGenIter(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, switchClosed, logDownSampling, iteration, tolerance, logLevel); -} \ No newline at end of file +} From 61c1dba2e078097aaa89c33b5b678f0003a62a8d Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Wed, 8 Mar 2023 14:59:24 +0100 Subject: [PATCH 22/68] cleanup in dcim models Signed-off-by: Jan Dinkelbach --- .../dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h | 2 +- .../dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h | 3 --- dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp | 3 --- dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp | 5 ----- 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h index f5ae43b308..878fbcfcc6 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h @@ -65,7 +65,7 @@ namespace Ph1 { // ### MNA Section ### /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) {}; void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); void mnaCompPostStep(const Matrix& leftVector); diff --git a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h index 3930ccd8cf..fd1b8e8d6a 100644 --- a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h @@ -25,9 +25,6 @@ namespace Ph1 { const Attribute::Ptr mEdq_t; protected: - Matrix mStates; - Matrix mStates_prev; - /// state representation matrix /// Real mAd; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp index eabcf2ad6d..3d297c8303 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -87,9 +87,6 @@ void DP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { mSLog->flush(); } -void DP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { -} - void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { if (mSimTime>0.0) { // calculate mechanical variables at t=k+1 with forward euler diff --git a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp index e2da9e9ace..88d35ea120 100644 --- a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp @@ -33,11 +33,6 @@ void SP::Ph1::SynchronGenerator4OrderDCIM::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; - // - mStates = Matrix::Zero(4,1); - mStates << **mEdq_t, **mIdq; - mStates_prev = Matrix::Zero(6,1); - // mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); From 56fac7ee6152d443e9c114be4af9b6aad80f6544 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 11:01:09 +0100 Subject: [PATCH 23/68] fix mechanical calculation of tpm after rebase Signed-off-by: Jan Dinkelbach --- .../Base/Base_ReducedOrderSynchronGenerator.h | 2 +- .../Base_ReducedOrderSynchronGenerator.cpp | 26 ++++++++++++++----- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 11 -------- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 9 ++++--- .../DP_SMIB_ReducedOrderSG_LoadStep.cpp | 25 +++++++++--------- 5 files changed, 40 insertions(+), 33 deletions(-) diff --git a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h index b070f562eb..e89ca27b4d 100644 --- a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h +++ b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h @@ -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 ### diff --git a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp index 59f8a469f9..dd5bcb1caa 100644 --- a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp +++ b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp @@ -437,15 +437,21 @@ 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(); mnaCompApplyRightSideVectorStamp(**mRightVector); } @@ -453,6 +459,8 @@ void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int 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,15 +470,21 @@ 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(); mnaCompApplyRightSideVectorStamp(**mRightVector); } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index dfec74798d..2dbd8e4898 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -181,17 +181,6 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // predict mechanical vars - 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 - **mElecTorque)); - - // predict theta and delta at t=k+1 (backward euler) - **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); - **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - } - // update auxiliar variables for time-varying VBR voltage and matrix depending on mThetaMech calculateAuxiliarVariables(); diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index 5d5015a55b..226b9e0ab3 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { // Simulation parameters String simName = "DP_SMIB_ReducedOrderSGIterative_LoadStep"; - Real timeStep = 32e-6; + Real timeStep = 1e-3; Real finalTime = 35; // Default configuration @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) { Real tolerance = defaultConfig.tolerance; int maxIter = defaultConfig.maxIter; String SGModel = defaultConfig.sgType + "Iter"; - SGModel = "4PCM"; // options: "4PCM", "4TPM", "6PCM" + SGModel = "4TPM"; // options: "4PCM", "4TPM", "6PCM" NumericalMethod numericalMethod = NumericalMethod::Euler; // only for "4PCM" or "6PCM" // Command line args processing @@ -62,7 +62,7 @@ int main(int argc, char* argv[]) { std::cout << "SG: " << SGModel << std::endl; // Configure logging - Logger::Level logLevel = Logger::Level::off; + Logger::Level logLevel = Logger::Level::info; // apply downsampling for simulation step sizes lower than 10us Real logDownSampling; @@ -185,6 +185,9 @@ int main(int argc, char* argv[]) { logger->logAttribute(genDP->name() + ".Edq", genDP->attribute("Edq")); 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); diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp index b61be51826..47047226a2 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,9 +132,9 @@ 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); @@ -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 }); @@ -158,7 +158,7 @@ int main(int argc, char* argv[]) { // Logging // log node voltage auto logger = DataLogger::make(simName, true, logDownSampling); - for (auto node : systemDP.mNodes) + for (auto node : systemDP.mNodes) logger->logAttribute(node->name() + ".V", node->attribute("v")); // log generator vars @@ -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 +} From d376e3da171690f1a8afdca14568af8409be3d96 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 11:02:18 +0100 Subject: [PATCH 24/68] add validation notebook tpm versus vbr Signed-off-by: Jan Dinkelbach --- ...SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb 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..05fe1d0bf6 --- /dev/null +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb @@ -0,0 +1,258 @@ +{ + "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]\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 Plot" + ] + }, + { + "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]<3.23)\n", + "assert(rmse_list[2]<4.42)\n", + "assert(rmse_list[3]<0.55)\n", + "assert(rmse_list[4]<0.03)" + ] + } + ], + "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 +} From 2bf7b35ae4a5038a83444abe123536475afb7800 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 12:17:11 +0100 Subject: [PATCH 25/68] remove redundant declaration of constants from tpm Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 17 ----------------- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 15 +++------------ 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index c96af4a6e7..e136f2e1d3 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -60,23 +60,6 @@ namespace Ph1 { Matrix mEh_vbr; /// MatrixComp mIdq_2prev; - - /// Auxiliar VBR constants - /// - Real mA; - /// - Real mB; - /// - Real mAd; - /// - Real mAq; - /// - Real mBd; - /// - Real mBq; - /// - Real mCq; - public: /// SynchronGenerator4OrderTPM(String uid, String name, Logger::Level logLevel = Logger::Level::off); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 2dbd8e4898..7ed59694b4 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -16,6 +16,7 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM mEvbr(mAttributes->create("Evbr")), mEdq_t(mAttributes->create("Edq")) { + mSGOrder = SGOrder::SG4Order; mPhaseType = PhaseType::Single; setVirtualNodeNumber(2); setTerminalNumber(1); @@ -92,16 +93,6 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarVariables() { } void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { - mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); - mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); - - mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); - mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mCq = 2 * mTimeStep * (**mEf) / (2 * mTd0_t + mTimeStep); - - mB = mLd_t - mAq; - mA = -mLq_t - mAd; - 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; @@ -208,8 +199,8 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; // calculate original VBR voltage (trapezoidal rule) - mEh_vbr(0,0) = mAd * (**mIdq)(1,0) + mBd * (**mEdq_t)(0,0); - mEh_vbr(1,0) = mAq * (**mIdq)(0,0) + mBq * (**mEdq_t)(1,0) + mCq; + 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 original VBR voltage to dp domain **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); From f2e6cc35295df03af0aa79a039aa899c668a7c4c Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 13:56:16 +0100 Subject: [PATCH 26/68] add smib load step with tpm and vbr to test sources Signed-off-by: Jan Dinkelbach --- dpsim/examples/cxx/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpsim/examples/cxx/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 1a75147435..b7662f197f 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -133,6 +133,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) From 9e9e859a8747b121567277bc9a09d4aaa9ddec5d Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 14:51:23 +0100 Subject: [PATCH 27/68] use explicit expression for Rconst in tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 13 ++---- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 46 +++++++++---------- ...SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb | 33 ++++++++++++- 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index e136f2e1d3..8d9f067b32 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -28,24 +28,19 @@ namespace Ph1 { const Attribute::Ptr mEvbr; protected: - /// Resistance matrix in dq reference frame - Matrix mResistanceMatrixDq; - /// Conductance matrix phase A - Matrix mConductanceMatrix; + /// Constant part of conductance matrix + Matrix mConductanceMatrixConst = Matrix::Zero(2,2); + /// 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 @@ -59,7 +54,7 @@ namespace Ph1 { /// history term of voltage behind the transient reactance Matrix mEh_vbr; /// - MatrixComp mIdq_2prev; + MatrixComp mIdpTwoPrev; public: /// SynchronGenerator4OrderTPM(String uid, String name, Logger::Level logLevel = Logger::Level::off); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 7ed59694b4..69f1bf5874 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -28,9 +28,6 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM **mIntfVoltage = MatrixComp::Zero(1, 1); **mIntfCurrent = MatrixComp::Zero(1, 1); - // 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; @@ -119,12 +116,6 @@ void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; calculateAuxiliarConstants(); - // 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); - mSLog->info( "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" @@ -138,14 +129,24 @@ void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { void DP::Ph1::SynchronGenerator4OrderTPM::calculateConductanceMatrix() { 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; - resistanceMatrix(0,0) = mR_const_1ph.real(); - resistanceMatrix(0,1) = -mR_const_1ph.imag(); - resistanceMatrix(1,0) = mR_const_1ph.imag(); - resistanceMatrix(1,1) = mR_const_1ph.real(); + SPDLOG_LOGGER_INFO(mSLog, "\nR_const [pu]: {}", Logger::matrixToString(resistanceMatrix)); resistanceMatrix = resistanceMatrix * mBase_Z; - mConductanceMatrix = resistanceMatrix.inverse(); + SPDLOG_LOGGER_INFO(mSLog, "\nR_const [Ohm]: {}", Logger::matrixToString(resistanceMatrix)); + + mConductanceMatrixConst = resistanceMatrix.inverse(); + + mSLog->info( + "\n--- Model specific initialization ---", + (**mEdq_t)(0,0), + (**mEdq_t)(1,0) + ); + mSLog->flush(); } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { @@ -158,14 +159,14 @@ void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(Matrix& // Stamp conductance // set upper left block - Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[0]->matrixNodeIndex(), mConductanceMatrix); + Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[0]->matrixNodeIndex(), mConductanceMatrixConst); // set buttom right block - Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrix); + Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrixConst); // Set off diagonal blocks - Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), matrixNodeIndex(0, 0), -mConductanceMatrix); - Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), mVirtualNodes[0]->matrixNodeIndex(), -mConductanceMatrix); + 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() { @@ -186,13 +187,13 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // set previous values of stator current at simulation start if (mSimTime == 0.0) { (**mIntfCurrent)(0,0) = std::conj(mInitElecPower / (mInitVoltage * mBase_V_RMS)); - mIdq_2prev = **mIntfCurrent; + mIdpTwoPrev = **mIntfCurrent; } // predict stator current (linear extrapolation) Matrix IdpPrediction = Matrix::Zero(2,1); - IdpPrediction(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdq_2prev(0,0).real(); - IdpPrediction(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdq_2prev(0,0).imag(); + IdpPrediction(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdpTwoPrev(0,0).real(); + IdpPrediction(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdpTwoPrev(0,0).imag(); // calculate emf at t=k (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; @@ -210,8 +211,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpPrediction)(1,0)); // Store previous current for later use - // FIXME: Rename to dp - mIdq_2prev = **mIntfCurrent; + mIdpTwoPrev = **mIntfCurrent; } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { 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 index 05fe1d0bf6..148c05718a 100644 --- a/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb @@ -168,7 +168,38 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Comparative Plot" + "## 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" ] }, { From 4f0990d8024534dc6400b1bbdb3ef6ab5ab96c00 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 14:59:59 +0100 Subject: [PATCH 28/68] avoid meaningless recomputation of Rvar in tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 4 ++- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 26 ++++++------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index 8d9f067b32..d7e2f592f4 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -28,8 +28,10 @@ namespace Ph1 { const Attribute::Ptr mEvbr; protected: - /// Constant part of conductance matrix + /// Constant part as conductance matrix Matrix mConductanceMatrixConst = Matrix::Zero(2,2); + /// Varying part as resistance matrix + Matrix mResistanceMatrixVarying = Matrix::Zero(2,2); /// Ka Matrix MatrixComp mKa; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 69f1bf5874..ead562bfa7 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -177,11 +177,10 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { calculateAuxiliarVariables(); // determine time-varying part of resistance matrix - Matrix resistanceMatrixVarying = Matrix::Zero(2,2); - resistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); - resistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); + mResistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); + mResistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); + mResistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); + mResistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); // predict electrical vars // set previous values of stator current at simulation start @@ -207,8 +206,8 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); // add current prediction based component to VBR voltage in dp domain - **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpPrediction)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpPrediction)(1,0)); + **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(1,0)); // Store previous current for later use mIdpTwoPrev = **mIntfCurrent; @@ -242,21 +241,12 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { IdpCorrection(0,0) = IdpCorrectionComplex.real(); IdpCorrection(1,0) = IdpCorrectionComplex.imag(); - // Determine time-varying part of resistance matrix - // FIXME: No recomputation of mechanical vars in corrector step, - // so reuse of this save computation time - Matrix resistanceMatrixVarying = Matrix::Zero(2,2); - resistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); - resistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); - // reset original VBR voltage in dp domain **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); // add current correction based component to VBR voltage in dp domain - **mEvbr += - Complex(mBase_Z * (resistanceMatrixVarying * IdpCorrection)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (resistanceMatrixVarying * IdpCorrection)(1,0)); + **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(0,0), 0); + **mEvbr += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(1,0)); // stamp currents (**mRightVector).setZero(); From a98071ae880ebf156403fc6fc23c4586e7d187f5 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 9 Mar 2023 16:06:59 +0100 Subject: [PATCH 29/68] use explicit expression for Rvar in tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 21 ++--------- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 36 ++++++------------- 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index d7e2f592f4..e3ea30b05d 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -33,27 +33,15 @@ namespace Ph1 { /// Varying part as resistance matrix Matrix mResistanceMatrixVarying = Matrix::Zero(2,2); - /// Ka Matrix - MatrixComp mKa; - /// Kb Matrix - MatrixComp mKb; - /// Kb Matrix - MatrixComp mKc; - /// phase a equivalent part of mKa - Complex mKa_1ph; - /// phase a equivalent part of mKb - Complex mKb_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; // #### Model specific variables #### - /// voltage behind transient reactance + /// Transient emf const Attribute::Ptr mEdq_t; - /// history term of voltage behind the transient reactance + /// VBR voltage Matrix mEh_vbr; /// MatrixComp mIdpTwoPrev; @@ -80,7 +68,7 @@ namespace Ph1 { void updateVoltage(const Matrix& leftVector) override; /// bool requiresIteration() override; - /// Calculate Ka, Kb and Kvbr + /// void calculateAuxiliarVariables(); /// Matrix get_parkTransformMatrix(); @@ -98,9 +86,6 @@ namespace Ph1 { 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/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index ead562bfa7..16d3cf3e78 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -24,15 +24,9 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM /// initialize attributes mNumIter = mAttributes->create("NIterations", 0); - // model variables - **mIntfVoltage = MatrixComp::Zero(1, 1); - **mIntfCurrent = MatrixComp::Zero(1, 1); - - // + // TODO: Remove using MathUtils 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); // model variables mEh_vbr = Matrix::Zero(2,1); @@ -75,25 +69,12 @@ void DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit(Real n }; void DP::Ph1::SynchronGenerator4OrderTPM::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::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { - 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; - // Initialize matrix of state representation of corrector step mA_prev = Matrix::Zero(2,2); mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, @@ -114,6 +95,7 @@ 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; + calculateAuxiliarConstants(); mSLog->info( @@ -150,14 +132,15 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateConductanceMatrix() { } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { + updateMatrixNodeIndices(); calculateConductanceMatrix(); + // 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); @@ -176,11 +159,12 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // update auxiliar variables for time-varying VBR voltage and matrix depending on mThetaMech calculateAuxiliarVariables(); - // determine time-varying part of resistance matrix - mResistanceMatrixVarying(0,0) = mKa_1ph.real() + mKb_1ph.real(); - mResistanceMatrixVarying(0,1) = - mKa_1ph.imag() + mKb_1ph.imag(); - mResistanceMatrixVarying(1,0) = mKa_1ph.imag() + mKb_1ph.imag(); - mResistanceMatrixVarying(1,1) = mKa_1ph.real() - mKb_1ph.real(); + 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 From 6cbfea1af620626e169475e720e24c79e54f08bf Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 9 Mar 2023 17:22:11 +0100 Subject: [PATCH 30/68] remove unused variables of PCM models Signed-off-by: Martin Moraga --- .../dpsim-models/Solver/MNASyncGenInterface.h | 15 --- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 95 +++++-------------- 2 files changed, 22 insertions(+), 88 deletions(-) diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 8eabe245f4..07ebd35f58 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -50,17 +50,6 @@ namespace CPS { Matrix mB_euler; Matrix mC_euler; - /// Matrixes used when numerical method of predictor step = trapezoidal rule - /// State Matrix trapezoidal rule: mStates = mA_trap_inv * mB_trap * mStates_prev + mA_trap_inv * mC_trap * Ef - /// where mStates_prev << Ed(k-1), Ed(k-1), Id(k-1), Iq(k-1), Vd(k-1), Vq(k-1) - /// and mStates << Ed(k), Ed(k), Id(k), Iq(k) - Matrix mA_trap; - Matrix mA_trap_inv; - Matrix mB_trap; - Matrix mC_trap; - Matrix mStates_trap_prev; - Matrix mStates_trap; - /// Matrixes used when numerical method of corrector step = trapezoidal rule /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + mC_corr * Ef Matrix mA_prev; @@ -80,8 +69,6 @@ namespace CPS { Int mMaxIter = 25; /// Real mTolerance = 1e-6; - /// - CPS::NumericalMethod mNumericalMethod = CPS::NumericalMethod::Trapezoidal; public: typedef std::shared_ptr Ptr; @@ -100,8 +87,6 @@ namespace CPS { void setMaxIterations(Int maxIterations) {mMaxIter = maxIterations;} /// void setTolerance(Real Tolerance) {mTolerance = Tolerance;} - /// - void setNumericalMethod(CPS::NumericalMethod numericalMethod) {mNumericalMethod = numericalMethod;} protected: /// Constructor diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 495645e06a..e4aa8e9e18 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -74,66 +74,23 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { // Initialize matrix of state representation of predictor step - if (mNumericalMethod == CPS::NumericalMethod::Euler) { - mA_euler = Matrix::Zero(4,4); - mA_euler << 1 - mTimeStep / mTq0_t, 0.0, 0.0, 0.0, - 0.0, 1 - mTimeStep / mTd0_t, 0.0, 0.0, - mTimeStep / mTq0_s, 0.0, 1 - mTimeStep / mTq0_s, 0.0, - 0.0, mTimeStep / mTd0_s, 0.0, 1 - mTimeStep / mTd0_s; - mB_euler = Matrix::Zero(4,2); - mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, - - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0, - 0.0, (mLq_t - mLq_s) * mTimeStep / mTq0_s, - - (mLd_t - mLd_s) * mTimeStep / mTd0_s, 0.0; - mC_euler = Matrix::Zero(4,1); - mC_euler << 0.0, - (mTimeStep / mTd0_t), - 0.0, - 0.0; - - } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { - // - mStates_trap = Matrix::Zero(6,1); - mStates_trap << **mEdq, **mIdq; - mStates_trap_prev = Matrix::Zero(8,1); - - // - Real Ad_t = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); - Real Bd_t = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); - Real Aq_t = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); - Real Bq_t = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - Real Dq_t = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); - - // - Real Ad_s = mTimeStep * (mLq_t - mLq_s) / (2 * mTq0_s + mTimeStep); - Real Bd_s = mTimeStep / (2 * mTq0_s + mTimeStep); - Real Cd_s = (2 * mTq0_s - mTimeStep) / (2 * mTq0_s + mTimeStep); - Real Aq_s = mTimeStep * (mLd_t - mLd_s) / (2 * mTd0_s + mTimeStep); - Real Bq_s = mTimeStep / (2 * mTd0_s + mTimeStep); - Real Cq_s = (2 * mTd0_s - mTimeStep) / (2 * mTd0_s + mTimeStep); - - // Initialize matrix of state representation - mA_trap = Matrix::Zero(6,6); - mA_trap << 1, 0, -Bd_s, 0, 0, -Ad_s, - 0, 1, 0, -Bq_s, Aq_s, 0, - 0, 0, 1, 0, 0, -Ad_t, - 0, 0, 0, 1, -Aq_t, 0, - 0, -1, 0, 0, mLd_s, 0, - 1, 0, 0, 0, 0, mLq_s; - mA_trap_inv = mA_trap.inverse(); - - mB_trap = Matrix::Zero(6,8); - mB_trap << Cd_s, 0, Bd_s, 0, 0, Ad_s, 0, 0, - 0, Cq_s, 0, Bq_s, -Aq_s, 0, 0, 0, - 0, 0, Bd_t, 0, 0, Ad_t, 0, 0, - 0, 0, 0, Bq_t, Aq_t, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, -1, - 0, 0, 0, 0, 0, 0, 1, 0; - - mC_trap = Matrix::Zero(6,1); - mC_trap << 0, 0, 0, Dq_t, 0, 0; - } - + mA_euler = Matrix::Zero(4,4); + mA_euler << 1 - mTimeStep / mTq0_t, 0.0, 0.0, 0.0, + 0.0, 1 - mTimeStep / mTd0_t, 0.0, 0.0, + mTimeStep / mTq0_s, 0.0, 1 - mTimeStep / mTq0_s, 0.0, + 0.0, mTimeStep / mTd0_s, 0.0, 1 - mTimeStep / mTd0_s; + mB_euler = Matrix::Zero(4,2); + mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, + - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0, + 0.0, (mLq_t - mLq_s) * mTimeStep / mTq0_s, + - (mLd_t - mLd_s) * mTimeStep / mTd0_s, 0.0; + mC_euler = Matrix::Zero(4,1); + mC_euler << 0.0, + (mTimeStep / mTd0_t), + 0.0, + 0.0; + + // Initialize matrix of state representation of corrector step mA_prev = Matrix::Zero(4, 4); mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, 0.0, 1 - mTimeStep / (2 * mTd0_t), 0.0, 0.0, @@ -176,19 +133,11 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { } // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - if (mNumericalMethod == CPS::NumericalMethod::Euler) { - mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); - - // predict armature currents for at t=k+1 - mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; - mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(2,0) ) / mLq_s; - - } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { - mStates_trap_prev << **mEdq, **mIdq, **mVdq; - mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * (**mEf); - mEdq_pred << mStates_trap(0,0), mStates_trap(1,0), mStates_trap(2,0), mStates_trap(3,0); - mIdq_pred << mStates_trap(4,0), mStates_trap(5,0); - } + mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + + // predict armature currents for at t=k+1 + mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; + mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(2,0) ) / mLq_s; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); From 891e967ce5f10cc23ececd378043bfb7e58e1ec2 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 9 Mar 2023 17:33:35 +0100 Subject: [PATCH 31/68] fix rebase errors of PCM models Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 8 +- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 8 +- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 95 +++++++------------ .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 26 +++-- 4 files changed, 61 insertions(+), 76 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 4b95018007..f75cd8aab0 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -48,11 +48,13 @@ namespace Ph1 { // #### MNA Functions #### /// - void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; + void mnaCompPreStep(Real time, Int timeStepCount) final; /// - void mnaCompPostStep(const Matrix& leftVector) override; + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) override {}; + void mnaCompPostStep(const Matrix& leftVector) final; + /// + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index cce907540f..0e7ae33d2a 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -47,11 +47,13 @@ namespace Ph1 { // #### MNA Functions #### /// - void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; + void mnaCompPreStep(Real time, Int timeStepCount) final; /// - void mnaCompPostStep(const Matrix& leftVector) override; + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) override {}; + void mnaCompPostStep(const Matrix& leftVector) final; + /// + void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; }; } } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 61e314d898..45466d8489 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -67,51 +67,20 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { // Initialize matrices of state representation of predictor step - if (mNumericalMethod == CPS::NumericalMethod::Euler) { - mA_euler = Matrix::Zero(2,2); - mA_euler << 1 - mTimeStep / mTq0_t, 0.0, - 0.0, 1 - mTimeStep / mTd0_t; - mB_euler = Matrix::Zero(2,2); - mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, - - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0; - mC_euler = Matrix::Zero(2,1); - mC_euler << 0.0, - (mTimeStep / mTd0_t); - } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { - // TODO: State space model applied here is different than the Euler representation. - // This needs harmonisation or further documentation. - mC_trap = Matrix::Zero(4,1); - mStates_trap_prev = Matrix::Zero(6,1); - mStates_trap = Matrix::Zero(4,1); - - Real Ad = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); - Real Bd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); - - Real Aq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); - Real Bq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - Real Cq = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); - - mA_trap = Matrix::Zero(4,4); - mA_trap << 1, 0, 0, -Ad, - 0, 1, -Aq, 0, - 0, -1, mLd_t, 0, - 1, 0, 0, mLq_t; - mA_trap_inv = mA_trap.inverse(); - - mB_trap = Matrix::Zero(4,6); - mB_trap << Bd, 0, 0, Ad, 0, 0, - 0, Bq, Aq, 0, 0, 0, - 0, 0, 0, 0, 0, -1, - 0, 0, 0, 0, 1, 0; - - mC_trap = Matrix::Zero(4,1); - mC_trap << 0, Cq, 0, 0; - } + mA_euler = Matrix::Zero(2,2); + mA_euler << 1 - mTimeStep / mTq0_t, 0.0, + 0.0, 1 - mTimeStep / mTd0_t; + mB_euler = Matrix::Zero(2,2); + mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, + - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0; + mC_euler = Matrix::Zero(2,1); + mC_euler << 0.0, + (mTimeStep / mTd0_t); // Initialize matrix of state representation of corrector step mA_prev = Matrix::Zero(2,2); mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, 1 - mTimeStep / (2 * mTd0_t); + 0.0, 1 - mTimeStep / (2 * mTd0_t); mA_corr = Matrix::Zero(2,2); mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, 0.0, - mTimeStep / (2 * mTd0_t); @@ -124,42 +93,42 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { (mTimeStep / mTd0_t); } +void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompPreStep(Real time, Int timeStepCount) { + mSimTime = time; + + // model specific calculation of electrical vars + stepInPerUnit(); + + // stamp model specific right side vector after calculation of electrical vars + (**mRightVector).setZero(); + mnaCompApplyRightSideVectorStamp(**mRightVector); +} + void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // set number of iterations equal to zero **mNumIter = 0; - // prediction of mechanical vars + // prediction of mechanical variables with forward euler if (mSimTime > 0.0) { - // calculate electrical torque at t=k **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); + mOmMech_pred = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); - // predict mechanical variables at t=k+1 (forward euler) - mOmMech_pred = **mOmMech + mTimeStep / (2 * mH) * (**mMechTorque - **mElecTorque); - mDelta_pred = **mDelta + mTimeStep * mBase_OmMech * (**mOmMech - 1); - mThetaMech_pred = **mThetaMech + mTimeStep * **mOmMech * mBase_OmMech; + // predict theta and delta at t=k+1 (backward euler) + mThetaMech_pred = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); + mDelta_pred = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; } else { - // prediction by assuming constant mechanical vars at t=0 - // TODO: add further explanatory comment why this is required + // In step 0 do not update electrical variables to avoid delay with voltage sources mOmMech_pred = **mOmMech; mDelta_pred = **mDelta; mThetaMech_pred = **mThetaMech; } // prediction of electrical vars - if (mNumericalMethod == CPS::NumericalMethod::Euler) { - // predict emf at t=k+1 (forward euler) - mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); - - // calculate stator currents at t=k+1 (assuming Vdq(k+1)=Vdq(k)) - mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; - } else if (mNumericalMethod == CPS::NumericalMethod::Trapezoidal) { - // predict emfs and stator currents at t=k+1 (trapezoidal rule) - mStates_trap_prev << **mEdq, **mIdq, **mVdq; - mStates_trap = mA_trap_inv * mB_trap * mStates_trap_prev + mA_trap_inv * mC_trap * (**mEf); - mEdq_pred << mStates_trap(0,0), mStates_trap(1,0); - mIdq_pred << mStates_trap(2,0), mStates_trap(3,0); - } + mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + + // calculate stator currents at t=k+1 (assuming Vdq(k+1)=Vdq(k)) + mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; + mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; // convert currents to dp domain mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index e4aa8e9e18..5b92c605fe 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -114,19 +114,31 @@ void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { 0.0; } +void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompPreStep(Real time, Int timeStepCount) { + mSimTime = time; + + // model specific calculation of electrical vars + stepInPerUnit(); + + // stamp model specific right side vector after calculation of electrical vars + (**mRightVector).setZero(); + mnaCompApplyRightSideVectorStamp(**mRightVector); +} + void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // Predictor step (backward euler) - if (mSimTime>0.0) { - // calculate electrical torque at t=k-1 + // prediction of mechanical variables with forward euler + if (mSimTime > 0.0) { **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - // predict mechanical variables at t=k - mOmMech_pred = **mOmMech + mTimeStep / (2 * mH) * (**mMechTorque - **mElecTorque); - mDelta_pred = **mDelta + mTimeStep * mBase_OmMech * (**mOmMech - 1); - mThetaMech_pred = **mThetaMech + mTimeStep * **mOmMech * mBase_OmMech; + mOmMech_pred = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); + + // predict theta and delta at t=k+1 (backward euler) + mThetaMech_pred = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); + mDelta_pred = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; } else { + // In step 0 do not update electrical variables to avoid delay with voltage sources mOmMech_pred = **mOmMech; mDelta_pred = **mDelta; mThetaMech_pred = **mThetaMech; From 1f3553d963467a53505ec92f5df6adaa68e7162e Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 09:51:49 +0100 Subject: [PATCH 32/68] improve naming of vars in tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 12 ++++- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 53 ++++++++----------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index e3ea30b05d..df6f8b2ddd 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -43,8 +43,16 @@ namespace Ph1 { const Attribute::Ptr mEdq_t; /// VBR voltage Matrix mEh_vbr; - /// - MatrixComp mIdpTwoPrev; + + // Variables saving values for later use + /// Idp at k-1 + MatrixComp mIdpTwoPrevStep; + /// Idq at k + Matrix mIdqPrevStep; + /// Vdq at j-1 + Matrix mVdqPrevIter; + /// Edq_t at k + Matrix mEdqtPrevStep; public: /// SynchronGenerator4OrderTPM(String uid, String name, Logger::Level logLevel = Logger::Level::off); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 16d3cf3e78..646a6f4e76 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -170,13 +170,13 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // set previous values of stator current at simulation start if (mSimTime == 0.0) { (**mIntfCurrent)(0,0) = std::conj(mInitElecPower / (mInitVoltage * mBase_V_RMS)); - mIdpTwoPrev = **mIntfCurrent; + mIdpTwoPrevStep = **mIntfCurrent; } // predict stator current (linear extrapolation) Matrix IdpPrediction = Matrix::Zero(2,1); - IdpPrediction(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdpTwoPrev(0,0).real(); - IdpPrediction(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdpTwoPrev(0,0).imag(); + 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; @@ -193,8 +193,10 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(0,0), 0); **mEvbr += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(1,0)); - // Store previous current for later use - mIdpTwoPrev = **mIntfCurrent; + // store values currently at t=k for later use + mIdpTwoPrevStep = **mIntfCurrent; + mIdqPrevStep = **mIdq; + mEdqtPrevStep = **mEdq_t; } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -208,18 +210,19 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // correct electrical vars // CHECK: Is this computation really required? PCM models does // simply mEdq_pred = mEdq_corr at the end of each iteration - mEdq_pred(0,0) = (**mVdq)(0,0) - mIdq_pred(1,0) * mLq_t; - mEdq_pred(1,0) = (**mVdq)(1,0) + mIdq_pred(0,0) * mLd_t; + // calculate emf at j-1 according to system solution + (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; + (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - // correct emf at t=k+1 (trapezoidal rule) - mEdq_corr = mA_prev * **mEdq_t + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * (**mEf); + // calculate emf at j (trapezoidal rule) + (**mEdq_t) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); - // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) - mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; + // calculate stator currents at j + (**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 = (mKvbr * mIdq_corr)(0,0) * mBase_I_RMS; + Complex IdpCorrectionComplex = (mKvbr * (**mIdq))(0,0) * mBase_I_RMS; Matrix IdpCorrection = Matrix::Zero(2,1); IdpCorrection(0,0) = IdpCorrectionComplex.real(); @@ -235,27 +238,13 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // 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) { - mVdq_prev = **mVdq; - (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); - - // - Matrix parkTransform = get_parkTransformMatrix(); - - // convert armature voltage into dq reference frame - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix 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 current into dq reference frame - MatrixComp Iabc_ = (**mIntfCurrent)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix Iabc = Matrix(3,1); - Iabc << Iabc_(0,0).real(), Iabc_(1,0).real(), Iabc_(2,0).real(); - mIdq_pred = parkTransform * Iabc / mBase_I_RMS; + mnaCompPostStep(leftVector); } bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { @@ -268,7 +257,7 @@ bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { return true; } else { // check voltage convergence according to tolerance - Matrix voltageDifference = **mVdq - mVdq_prev; + Matrix voltageDifference = **mVdq - mVdqPrevIter; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) return true; else From e28834f34183d9be36767d62839d7c92880e433a Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 09:53:30 +0100 Subject: [PATCH 33/68] add missing update of internal iterative model vars after prediction step system solution Signed-off-by: Jan Dinkelbach --- dpsim/src/MNASolverDirect.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dpsim/src/MNASolverDirect.cpp b/dpsim/src/MNASolverDirect.cpp index aaa9e9c1d4..bb5e4adba5 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -233,6 +233,11 @@ 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; From 24171f4536b83733d208b3381b70028ac4ad98ac Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 09:55:02 +0100 Subject: [PATCH 34/68] remove recomputation emf for prev iter according to system solution in corrector step Signed-off-by: Jan Dinkelbach --- dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 646a6f4e76..5bff7bfcc8 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -208,12 +208,6 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { **mNumIter = **mNumIter + 1; // correct electrical vars - // CHECK: Is this computation really required? PCM models does - // simply mEdq_pred = mEdq_corr at the end of each iteration - // calculate emf at j-1 according to system solution - (**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 emf at j (trapezoidal rule) (**mEdq_t) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); From 590f24b7d8d9b45a685e01f8ef4e8833915283da Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 10:11:08 +0100 Subject: [PATCH 35/68] update thresholds in tpm validation notebook Signed-off-by: Jan Dinkelbach --- .../Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 148c05718a..37d3e800fb 100644 --- a/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb @@ -253,10 +253,10 @@ "outputs": [], "source": [ "assert(rmse_list[0]<0.32)\n", - "assert(rmse_list[1]<3.23)\n", - "assert(rmse_list[2]<4.42)\n", - "assert(rmse_list[3]<0.55)\n", - "assert(rmse_list[4]<0.03)" + "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)" ] } ], From b51aeded98a0a7cb1508179ae1a9c64c8f989824 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Fri, 10 Mar 2023 13:56:45 +0100 Subject: [PATCH 36/68] improve namiv of vars in pcm models Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 13 +- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 13 +- .../dpsim-models/Solver/MNASyncGenInterface.h | 31 +---- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 101 +++++----------- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 2 - .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 113 +++++++----------- 6 files changed, 97 insertions(+), 176 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index f75cd8aab0..559d98e641 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -48,13 +48,22 @@ namespace Ph1 { // #### MNA Functions #### /// - void mnaCompPreStep(Real time, Int timeStepCount) final; - /// void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; /// void mnaCompPostStep(const Matrix& leftVector) final; /// void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; + + /// + Matrix mEdqtPrevStep; + Matrix mIdqPrevStep; + Real mElecTorquePrevStep; + Real mOmMechPrevStep; + Real mThetaMechPrevStep; + Real mDeltaPrevStep; + + /// + Matrix mVdqPrevIter; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 0e7ae33d2a..f4b1517ec0 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -47,13 +47,22 @@ namespace Ph1 { // #### MNA Functions #### /// - void mnaCompPreStep(Real time, Int timeStepCount) final; - /// void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; /// void mnaCompPostStep(const Matrix& leftVector) final; /// void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; + + /// + Matrix mEdqtPrevStep; + Matrix mIdqPrevStep; + Real mElecTorquePrevStep; + Real mOmMechPrevStep; + Real mThetaMechPrevStep; + Real mDeltaPrevStep; + + /// + Matrix mVdqPrevIter; }; } } diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 07ebd35f58..44b9d156e6 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -17,32 +17,8 @@ namespace CPS { class MNASyncGenInterface { protected: // #### Model specific variables #### - /// generator terminal at time k-1 - Matrix mVdq_prev; - /// predicted generator current at time k - Matrix mIdq_pred; - /// corrected generator current at time k - Matrix mIdq_corr; /// voltage behind the transient/subtransiet impedance at time k-1 Attribute::Ptr mEdq; - /// predicted voltage behind the transient and subtransient impedance at time k+1 - Matrix mEdq_pred; - /// corrected voltage behind the transient and subtransient impedance at time k+1 - Matrix mEdq_corr; - /// corrected electrical torque - Real mElecTorque_corr; - /// predicted mechanical omega at time k - Real mOmMech_pred; - /// corrected mechanical omega at time k - Real mOmMech_corr; - /// prediction mechanical system angle at time k - Real mThetaMech_pred; - /// corrected mechanical system angle at time k - Real mThetaMech_corr; - /// predicted delta at time k - Real mDelta_pred; - /// corrected delta at time k - Real mDelta_corr; /// Matrix used when numerical method of predictor step = Euler /// State Matrix backward euler: Edq(k) = mA_euler * Edq(k) + mB_euler * Idq + mC_euler * Ef @@ -51,7 +27,7 @@ namespace CPS { Matrix mC_euler; /// Matrixes used when numerical method of corrector step = trapezoidal rule - /// State Matrix trapezoidal rule (corrector step): x(k+1) = mA_prev * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + mC_corr * Ef + /// State Matrix trapezoidal rule (corrector step): x(k+1) = mAPrevIter * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + mC_corr * Ef Matrix mA_prev; Matrix mA_corr; Matrix mB_corr; @@ -91,12 +67,7 @@ namespace CPS { protected: /// Constructor MNASyncGenInterface() { - // inizialize matrix that are not model dependent - mIdq_pred = Matrix::Zero(2,1); - mIdq_corr = Matrix::Zero(2,1); - // Vector to convert 1phase to 3phase - mShiftVector = Matrix::Zero(3,1); mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 45466d8489..a93a99d921 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -23,8 +23,6 @@ DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM // Initialize matrix **mEdq = Matrix::Zero(2,1); - mEdq_pred = Matrix::Zero(2,1); - mEdq_corr = Matrix::Zero(2,1); } DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM @@ -93,47 +91,29 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { (mTimeStep / mTd0_t); } -void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompPreStep(Real time, Int timeStepCount) { - mSimTime = time; - - // model specific calculation of electrical vars - stepInPerUnit(); - - // stamp model specific right side vector after calculation of electrical vars - (**mRightVector).setZero(); - mnaCompApplyRightSideVectorStamp(**mRightVector); -} - void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // set number of iterations equal to zero **mNumIter = 0; - // prediction of mechanical variables with forward euler - if (mSimTime > 0.0) { - **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - mOmMech_pred = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); + // store values currently at t=k-1 for later use + mEdqtPrevStep = **mEdq; + mIdqPrevStep = **mIdq; + mElecTorquePrevStep = **mElecTorque; + mOmMechPrevStep = **mOmMech; + mThetaMechPrevStep = **mThetaMech; + mDeltaPrevStep = **mDelta; - // predict theta and delta at t=k+1 (backward euler) - mThetaMech_pred = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); - mDelta_pred = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - } else { - // In step 0 do not update electrical variables to avoid delay with voltage sources - mOmMech_pred = **mOmMech; - mDelta_pred = **mDelta; - mThetaMech_pred = **mThetaMech; - } - - // prediction of electrical vars - mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + // prediction emf at t=k + **mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); - // calculate stator currents at t=k+1 (assuming Vdq(k+1)=Vdq(k)) - mIdq_pred(0,0) = (mEdq_pred(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(0,0) ) / mLq_t; + // predict stator currents at t=k (assuming Vdq(k+1)=Vdq(k)) + (**mIdq)(0,0) = ((**mEdq)(1,0) - (**mVdq)(1,0)) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(0,0)) / mLq_t; // convert currents to dp domain - mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; + mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -150,35 +130,29 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // correction of mechanical vars if (mSimTime>0.0) { + // In step 0 do not update electrical variables to avoid delay with voltage sources // calculate electrical torque at t=k+1 - mElecTorque_corr = (**mVdq)(0,0) * (mIdq_pred)(0,0) + (**mVdq)(1,0) * (mIdq_pred)(1,0); + **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); // correct mechanical variables at t=k+1 (trapezoidal rule) - mOmMech_corr = **mOmMech + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorque_corr); - mDelta_corr = **mDelta + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMech_pred - 2); + **mOmMech = mOmMechPrevStep + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorquePrevStep); + **mDelta = mDeltaPrevStep + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMechPrevStep - 2); // CHECK: For mThetaMech_corr use mOmMech_corr already? - mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_pred) * mBase_OmMech; - } else { - // correction by assuming constant mechanical vars at t=0 - // TODO: add further explanatory comment why this is required - mElecTorque_corr = **mElecTorque; - mOmMech_corr = mOmMech_pred; - mDelta_corr = mDelta_pred; - mThetaMech_corr = mThetaMech_pred; + **mThetaMech = mThetaMechPrevStep + mTimeStep / 2. * (**mOmMech + mOmMechPrevStep) * mBase_OmMech; } // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) - mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * (**mEf); + (**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) - mIdq_corr(0,0) = (mEdq_corr(1,0) - (**mVdq)(1,0) ) / mLd_t; - mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(0,0) ) / mLq_t; + (**mIdq)(0,0) = ((**mEdq)(1,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(0,0) ) / mLq_t; // convert corrected currents to dp domain - mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; + mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); @@ -189,7 +163,10 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector // Probably better to use `PrevIter` and `CurrIter` // variables instead of misusing `_pred` variables or `mVdq`. - mVdq_prev = **mVdq; + // 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 @@ -197,15 +174,9 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); if (**mNumIter == 0) - **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; else - **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; - - mOmMech_pred = mOmMech_corr; - mDelta_pred = mDelta_corr; - mThetaMech_pred = mThetaMech_corr; - mIdq_pred = mIdq_corr; - mEdq_pred = mEdq_corr; + **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { @@ -213,7 +184,7 @@ bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { // if no corrector step has been performed yet return true; - Matrix voltageDifference = **mVdq - mVdq_prev; + Matrix voltageDifference = **mVdq - mVdqPrevIter; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { if (**mNumIter >= mMaxIter) { return false; @@ -226,10 +197,4 @@ bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompPostStep(const Matrix& leftVector) { - // update variables - **mOmMech = mOmMech_corr; - **mThetaMech = mThetaMech_corr; - **mDelta = mDelta_corr; - **mEdq = mEdq_corr; - **mIdq = mIdq_corr; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 5bff7bfcc8..757eedf43b 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -31,8 +31,6 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM // model variables mEh_vbr = Matrix::Zero(2,1); **mEdq_t = Matrix::Zero(2,1); - mEdq_corr = Matrix::Zero(2,1); - mEdq_pred = Matrix::Zero(2,1); } DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 5b92c605fe..9bb235f5ad 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -23,8 +23,6 @@ DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM // Initialize matrix **mEdq = Matrix::Zero(4,1); - mEdq_pred = Matrix::Zero(4,1); - mEdq_corr = Matrix::Zero(4,1); } DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM @@ -114,47 +112,29 @@ void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { 0.0; } -void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompPreStep(Real time, Int timeStepCount) { - mSimTime = time; - - // model specific calculation of electrical vars - stepInPerUnit(); - - // stamp model specific right side vector after calculation of electrical vars - (**mRightVector).setZero(); - mnaCompApplyRightSideVectorStamp(**mRightVector); -} - void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // prediction of mechanical variables with forward euler - if (mSimTime > 0.0) { - **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - mOmMech_pred = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); - - // predict theta and delta at t=k+1 (backward euler) - mThetaMech_pred = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); - mDelta_pred = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - } else { - // In step 0 do not update electrical variables to avoid delay with voltage sources - mOmMech_pred = **mOmMech; - mDelta_pred = **mDelta; - mThetaMech_pred = **mThetaMech; - } + // store values currently at t=k-1 for later use + mEdqtPrevStep = **mEdq; + mIdqPrevStep = **mIdq; + mElecTorquePrevStep = **mElecTorque; + mOmMechPrevStep = **mOmMech; + mThetaMechPrevStep = **mThetaMech; + mDeltaPrevStep = **mDelta; - // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - mEdq_pred = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + // prediction emf at t=k + **mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); // predict armature currents for at t=k+1 - mIdq_pred(0,0) = (mEdq_pred(3,0) - (**mVdq)(1,0) ) / mLd_s; - mIdq_pred(1,0) = ((**mVdq)(0,0) - mEdq_pred(2,0) ) / mLq_s; + (**mIdq)(0,0) = ((**mEdq)(3,0) - (**mVdq)(1,0) ) / mLd_s; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(2,0) ) / mLq_s; // convert currents into the abc reference frame - mDpToDq(0,0) = Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime), sin(mThetaMech_pred - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_pred - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_pred)(0,0) * mBase_I_RMS; + mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -165,39 +145,41 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; + // correction of mechanical vars if (mSimTime>0.0) { - // calculate electrical torque at t=k - mElecTorque_corr = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - // correct mechanical variables at t=k - mOmMech_corr = **mOmMech + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorque_corr); - mDelta_corr = **mDelta + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMech_pred - 2); - mThetaMech_corr = **mThetaMech + mTimeStep / 2. *(**mOmMech + mOmMech_pred) * mBase_OmMech; - } else { - mElecTorque_corr = **mElecTorque; - mOmMech_corr = mOmMech_pred; - mDelta_corr = mDelta_pred; - mThetaMech_corr = mThetaMech_pred; - } + // In step 0 do not update electrical variables to avoid delay with voltage sources + // calculate electrical torque at t=k+1 + **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - //predict voltage behind transient reactance - mEdq_corr = mA_prev * **mEdq + mA_corr * mEdq_pred + mB_corr * (**mIdq + mIdq_pred) + mC_corr * (**mEf); + // correct mechanical variables at t=k+1 (trapezoidal rule) + **mOmMech = mOmMechPrevStep + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorquePrevStep); + **mDelta = mDeltaPrevStep + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMechPrevStep - 2); + // CHECK: For mThetaMech_corr use mOmMech_corr already? + **mThetaMech = mThetaMechPrevStep + mTimeStep / 2. * (**mOmMech + mOmMechPrevStep) * mBase_OmMech; + } - // armature currents for at t=k+1 - mIdq_corr(0,0) = (mEdq_corr(3,0) - (**mVdq)(1,0) ) / mLd_s; - mIdq_corr(1,0) = ((**mVdq)(0,0) - mEdq_corr(2,0) ) / mLq_s; + // correction of electrical vars + // correct emf at t=k+1 (trapezoidal rule) + (**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) + (**mIdq)(0,0) = ((**mEdq)(3,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(2,0) ) / mLq_t; - // convert currents into the abc reference frame - mDpToDq(0,0) = Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime), sin(mThetaMech_corr - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.), sin(mThetaMech_corr - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * mIdq_corr)(0,0) * mBase_I_RMS; + // convert corrected currents to dp domain + mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); } void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector) { - mVdq_prev = **mVdq; + // 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 @@ -205,9 +187,9 @@ void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); if (**mNumIter == 0) - **mVdq = parkTransform(mThetaMech_pred, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; else - **mVdq = parkTransform(mThetaMech_corr, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { @@ -215,18 +197,11 @@ bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { // if no corrector step has been performed yet return true; - Matrix voltageDifference = **mVdq - mVdq_prev; + Matrix voltageDifference = **mVdq - mVdqPrevIter; if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { if (**mNumIter == mMaxIter) { return false; } else { - // set predicted values equal to corrected values for the next iteration - mOmMech_pred = mOmMech_corr; - mDelta_pred = mDelta_corr; - mThetaMech_pred= mThetaMech_corr; - mIdq_pred = mIdq_corr; - mEdq_pred = mEdq_corr; - return true; } } else { @@ -235,11 +210,5 @@ bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { } void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompPostStep(const Matrix& leftVector) { - // update variables - **mOmMech = mOmMech_corr; - **mThetaMech = mThetaMech_corr; - **mDelta = mDelta_corr; - **mEdq = mEdq_corr; - **mIdq = mIdq_corr; } From cdaf006203cd2edd695ef678b39bc2603f5d670d Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 13:57:57 +0100 Subject: [PATCH 37/68] fix iterative example after rebase Signed-off-by: Jan Dinkelbach --- dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 4 ++-- .../cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 757eedf43b..ce7a0419fc 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -206,10 +206,10 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { **mNumIter = **mNumIter + 1; // correct electrical vars - // calculate emf at j (trapezoidal rule) + // calculate emf at j and k+1 (trapezoidal rule) (**mEdq_t) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); - // calculate stator currents at j + // 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; diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index 226b9e0ab3..eca1189382 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -33,7 +33,6 @@ int main(int argc, char* argv[]) { int maxIter = defaultConfig.maxIter; String SGModel = defaultConfig.sgType + "Iter"; SGModel = "4TPM"; // options: "4PCM", "4TPM", "6PCM" - NumericalMethod numericalMethod = NumericalMethod::Euler; // only for "4PCM" or "6PCM" // Command line args processing CommandLineArgs args(argc, argv); @@ -151,7 +150,6 @@ int main(int argc, char* argv[]) { genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); - std::dynamic_pointer_cast(genDP)->setNumericalMethod(numericalMethod); //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); From 29a6f1544b1f8340b1f4ad032785c174f02b76b3 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 17:02:42 +0100 Subject: [PATCH 38/68] add higher number of iter to tpm model validation notebook Signed-off-by: Jan Dinkelbach --- ...are_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 index 37d3e800fb..c1205244e5 100644 --- a/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb @@ -62,7 +62,7 @@ "roi_end = 12.0\n", "\n", "# tpm config params\n", - "max_iter_array = [0, 1, 2, 5, 10]\n", + "max_iter_array = [0, 1, 2, 5, 10, 20]\n", "tolerance = 1e-10\n", "sg_model = '4TPM'\n", "\n", @@ -256,8 +256,16 @@ "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)" + "assert(rmse_list[4]<0.003)\n", + "assert(rmse_list[5]<2.4e-5)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 0903d0d542a99b76e388d0f8e7bf2ec580e9cd2d Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 17:26:08 +0100 Subject: [PATCH 39/68] unify and simplify dq to dp transforms in tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 16 +++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 65 +++++++++---------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index df6f8b2ddd..2ff3f6534d 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -33,10 +33,10 @@ namespace Ph1 { /// Varying part as resistance matrix Matrix mResistanceMatrixVarying = Matrix::Zero(2,2); - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - /// Matrix to convert Evbr from dq domain to abc domain (only phase a) - MatrixComp mKvbr; + /// Transform from DQ to DP domain + Matrix mDQToDPTransform = Matrix::Zero(2,2); + /// Transform from DP to DQ domain + Matrix mDPToDQTransform = Matrix::Zero(2,2); // #### Model specific variables #### /// Transient emf @@ -77,9 +77,13 @@ namespace Ph1 { /// bool requiresIteration() override; /// - void calculateAuxiliarVariables(); + void updateDQToDPTransform(); /// - Matrix get_parkTransformMatrix(); + void updateDPToDQTransform(); + /// + Complex applyDQToDPTransform(const Matrix& dqMatrix); + /// + Matrix applyDPToDQTransform(const Complex& dpComplex); /// void initializeResistanceMatrix() final {}; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index ce7a0419fc..5abe12e765 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -24,10 +24,6 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM /// initialize attributes mNumIter = mAttributes->create("NIterations", 0); - // TODO: Remove using MathUtils - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - // model variables mEh_vbr = Matrix::Zero(2,1); **mEdq_t = Matrix::Zero(2,1); @@ -66,10 +62,27 @@ void DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit(Real n Td0_t, Tq0_t); }; -void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarVariables() { - 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::SynchronGenerator4OrderTPM::updateDQToDPTransform() { + mDQToDPTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), + sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::updateDPToDQTransform() { + mDPToDQTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime), + -sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); +} + +Complex DP::Ph1::SynchronGenerator4OrderTPM::applyDQToDPTransform(const Matrix& dqMatrix) { + Complex dpComplex; + dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); + return dpComplex; +} + +Matrix DP::Ph1::SynchronGenerator4OrderTPM::applyDPToDQTransform(const Complex& dpComplex) { + Matrix dqMatrix = Matrix::Zero(2,1); + 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; } void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { @@ -154,8 +167,9 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // update auxiliar variables for time-varying VBR voltage and matrix depending on mThetaMech - calculateAuxiliarVariables(); + // update DQ-DP transforms according to mThetaMech + updateDQToDPTransform(); + updateDPToDQTransform(); Real DeltaTheta = **mThetaMech - mBase_OmMech * mSimTime; mResistanceMatrixVarying(0,0) = - (mA + mB) / 2.0 * sin(2*DeltaTheta); @@ -185,7 +199,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { mEh_vbr(1,0) = mAq_t * (**mIdq)(0,0) + mBq_t * (**mEdq_t)(1,0) + mDq_t * mEf_prev + mDq_t * (**mEf); // convert original VBR voltage to dp domain - **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); + **mEvbr = applyDQToDPTransform(mEh_vbr) * mBase_V_RMS; // add current prediction based component to VBR voltage in dp domain **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(0,0), 0); @@ -214,14 +228,14 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; // convert corrected currents to dp domain - Complex IdpCorrectionComplex = (mKvbr * (**mIdq))(0,0) * mBase_I_RMS; + Complex IdpCorrectionComplex = applyDQToDPTransform(**mIdq) * mBase_I_RMS; Matrix IdpCorrection = Matrix::Zero(2,1); IdpCorrection(0,0) = IdpCorrectionComplex.real(); IdpCorrection(1,0) = IdpCorrectionComplex.imag(); // reset original VBR voltage in dp domain - **mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); + **mEvbr = applyDQToDPTransform(mEh_vbr) * mBase_V_RMS; // add current correction based component to VBR voltage in dp domain **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(0,0), 0); @@ -262,26 +276,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompPostStep(const Matrix& leftVect (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); - // convert armature voltage into dq reference frame - Matrix parkTransform = get_parkTransformMatrix(); - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix 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 current into dq reference frame - MatrixComp Iabc_ = (**mIntfCurrent)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix 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::SynchronGenerator4OrderTPM::get_parkTransformMatrix() { - 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 voltages and currents to dq reference frame + **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; + **mIdq = applyDPToDQTransform((**mIntfCurrent)(0, 0)) / mBase_I_RMS; } From 992de9a3af3480287f62df7642787b2e13cec838 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 10 Mar 2023 19:35:11 +0100 Subject: [PATCH 40/68] use state space with voltage input in corrector step of tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 11 ++++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 36 ++++++++----------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index 2ff3f6534d..d3f81964d0 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -47,12 +47,19 @@ namespace Ph1 { // Variables saving values for later use /// Idp at k-1 MatrixComp mIdpTwoPrevStep; - /// Idq at k - Matrix mIdqPrevStep; + /// 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); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 5abe12e765..feb7ca0117 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -86,20 +86,12 @@ Matrix DP::Ph1::SynchronGenerator4OrderTPM::applyDPToDQTransform(const Complex& } void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { - // Initialize matrix of state representation of corrector step - mA_prev = Matrix::Zero(2,2); - mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, 1 - mTimeStep / (2 * mTd0_t); - mA_corr = Matrix::Zero(2,2); - mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, - mTimeStep / (2 * mTd0_t); - mB_corr = Matrix::Zero(2,2); - mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; - - mC_corr = Matrix::Zero(2,1); - mC_corr << 0.0, - (mTimeStep / mTd0_t); + 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, + **mEf / mTd0_t; } void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { @@ -207,7 +199,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // store values currently at t=k for later use mIdpTwoPrevStep = **mIntfCurrent; - mIdqPrevStep = **mIdq; + mVdqPrevStep = **mVdq; mEdqtPrevStep = **mEdq_t; } @@ -221,7 +213,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // correct electrical vars // calculate emf at j and k+1 (trapezoidal rule) - (**mEdq_t) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mVdq, mVdqPrevStep); // calculate stator currents at j and k+1 (**mIdq)(0,0) = ((**mEdq_t)(1,0) - (**mVdq)(1,0) ) / mLd_t; @@ -250,7 +242,11 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { } void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector) { - mnaCompPostStep(leftVector); + // update armature voltage + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage to dq reference frame + **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { @@ -272,11 +268,9 @@ bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompPostStep(const Matrix& leftVector) { - // update armature voltage and current - (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + // update armature current (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); - // convert armature voltages and currents to dq reference frame - **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; + // convert armature current to dq reference frame **mIdq = applyDPToDQTransform((**mIntfCurrent)(0, 0)) / mBase_I_RMS; } From 0f67466606aab3c9186ea673424088ec299fe345 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Mon, 13 Mar 2023 13:51:52 +0100 Subject: [PATCH 41/68] further naming improvements tpm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 15 ++--- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 57 ++++++++----------- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 2 +- 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index d3f81964d0..25b5591fa8 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -22,11 +22,6 @@ namespace Ph1 { public Base::ReducedOrderSynchronGenerator, public MNASyncGenInterface, public SharedFactory { - public: - // Common elements of all VBR models - /// voltage behind reactance phase a - const Attribute::Ptr mEvbr; - protected: /// Constant part as conductance matrix Matrix mConductanceMatrixConst = Matrix::Zero(2,2); @@ -41,8 +36,10 @@ namespace Ph1 { // #### Model specific variables #### /// Transient emf const Attribute::Ptr mEdq_t; - /// VBR voltage - Matrix mEh_vbr; + /// Original history voltage of VBR model + Matrix mEh = Matrix::Zero(2,1); + /// Modified history voltage of TPM model + const Attribute::Ptr mEhMod; // Variables saving values for later use /// Idp at k-1 @@ -72,9 +69,9 @@ namespace Ph1 { /// Initializes component from power flow data void specificInitialization() override; /// - void calculateAuxiliarConstants(); + void calculateStateSpaceMatrices(); /// - void calculateConductanceMatrix(); + void calculateConstantConductanceMatrix(); /// void stepInPerUnit() override; /// diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index feb7ca0117..9841ea165a 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -13,8 +13,8 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM (String uid, String name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEvbr(mAttributes->create("Evbr")), - mEdq_t(mAttributes->create("Edq")) { + mEhMod(mAttributes->create("Ehmod")), + mEdq_t(mAttributes->create("Edq_t")) { mSGOrder = SGOrder::SG4Order; mPhaseType = PhaseType::Single; @@ -25,7 +25,6 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM mNumIter = mAttributes->create("NIterations", 0); // model variables - mEh_vbr = Matrix::Zero(2,1); **mEdq_t = Matrix::Zero(2,1); } @@ -85,7 +84,7 @@ Matrix DP::Ph1::SynchronGenerator4OrderTPM::applyDPToDQTransform(const Complex& return dqMatrix; } -void DP::Ph1::SynchronGenerator4OrderTPM::calculateAuxiliarConstants() { +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, @@ -98,10 +97,7 @@ 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; - - calculateAuxiliarConstants(); - - mSLog->info( + SPDLOG_LOGGER_INFO(mSLog, "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" "\nInitial Eq_t (per unit): {:f}" @@ -109,10 +105,11 @@ void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { (**mEdq_t)(0,0), (**mEdq_t)(1,0) ); - mSLog->flush(); + + calculateStateSpaceMatrices(); } -void DP::Ph1::SynchronGenerator4OrderTPM::calculateConductanceMatrix() { +void DP::Ph1::SynchronGenerator4OrderTPM::calculateConstantConductanceMatrix() { Matrix resistanceMatrix = Matrix::Zero(2,2); resistanceMatrix(0,0) = 0; resistanceMatrix(0,1) = (mA - mB) / 2.0; @@ -125,19 +122,13 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateConductanceMatrix() { SPDLOG_LOGGER_INFO(mSLog, "\nR_const [Ohm]: {}", Logger::matrixToString(resistanceMatrix)); mConductanceMatrixConst = resistanceMatrix.inverse(); - - mSLog->info( - "\n--- Model specific initialization ---", - (**mEdq_t)(0,0), - (**mEdq_t)(1,0) - ); - mSLog->flush(); + SPDLOG_LOGGER_INFO(mSLog, "\nG_const [S]: {}", Logger::matrixToString(mConductanceMatrixConst)); } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { - updateMatrixNodeIndices(); - calculateConductanceMatrix(); + + calculateConstantConductanceMatrix(); // Stamp voltage source Math::setMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[1]->matrixNodeIndex(), Complex(-1, 0)); @@ -186,16 +177,16 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { (**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 VBR voltage (trapezoidal rule) - 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); + // 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); - // convert original VBR voltage to dp domain - **mEvbr = applyDQToDPTransform(mEh_vbr) * mBase_V_RMS; + // set to original history voltage in dp domain + **mEhMod = applyDQToDPTransform(mEh) * mBase_V_RMS; - // add current prediction based component to VBR voltage in dp domain - **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(1,0)); + // 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; @@ -204,7 +195,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), **mEvbr); + Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), **mEhMod); } void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { @@ -226,12 +217,12 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { IdpCorrection(0,0) = IdpCorrectionComplex.real(); IdpCorrection(1,0) = IdpCorrectionComplex.imag(); - // reset original VBR voltage in dp domain - **mEvbr = applyDQToDPTransform(mEh_vbr) * mBase_V_RMS; + // reset to original history voltage of VBR model in dp domain + **mEhMod = applyDQToDPTransform(mEh) * mBase_V_RMS; - // add current correction based component to VBR voltage in dp domain - **mEvbr += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(0,0), 0); - **mEvbr += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(1,0)); + // 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(); diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index eca1189382..c1d424c8ce 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -180,7 +180,7 @@ int main(int argc, char* argv[]) { // log generator vars logger->logAttribute(genDP->name() + ".Te", genDP->attribute("Te")); logger->logAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); - logger->logAttribute(genDP->name() + ".Edq", genDP->attribute("Edq")); + logger->logAttribute(genDP->name() + ".Edq_t", genDP->attribute("Edq_t")); 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")); From 408c2c91407a2bd730c22af58ad133237a9b7df3 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Sun, 12 Mar 2023 22:09:49 +0100 Subject: [PATCH 42/68] Use mEdq_s, mEdq_t instead of mEdq Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 18 +++++++-- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 22 ++++++++-- .../dpsim-models/Solver/MNASyncGenInterface.h | 4 -- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 30 +++++++------- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 40 ++++++++++--------- 5 files changed, 69 insertions(+), 45 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 559d98e641..f75f52bb8c 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -54,16 +54,26 @@ namespace Ph1 { /// void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; - /// + protected: + // #### Model specific variables #### + /// Transient emf + const Attribute::Ptr mEdq_t; + + // Variables saving values for later use + /// Edqt at k Matrix mEdqtPrevStep; + /// Vdq at j-1 + Matrix mVdqPrevIter; + /// Idq at k Matrix mIdqPrevStep; + /// Electrica torque at k Real mElecTorquePrevStep; + /// Mechanical omega at k Real mOmMechPrevStep; + /// Mechanical theta at k Real mThetaMechPrevStep; + /// Delta at k Real mDeltaPrevStep; - - /// - Matrix mVdqPrevIter; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index f4b1517ec0..b99b9b8728 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -53,16 +53,30 @@ namespace Ph1 { /// void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; - /// + protected: + // #### Model specific variables #### + /// Subransient emf + const Attribute::Ptr mEdq_s; + /// Transient emf + const Attribute::Ptr mEdq_t; + + // Variables saving values for later use + /// Edqs at k + Matrix mEdqsPrevStep; + /// Edqt at k Matrix mEdqtPrevStep; + /// Vdq at j-1 + Matrix mVdqPrevIter; + /// Idq at k Matrix mIdqPrevStep; + /// Electrica torque at k Real mElecTorquePrevStep; + /// Mechanical omega at k Real mOmMechPrevStep; + /// Mechanical theta at k Real mThetaMechPrevStep; + /// Delta at k Real mDeltaPrevStep; - - /// - Matrix mVdqPrevIter; }; } } diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 44b9d156e6..0e23dc2e78 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -16,10 +16,6 @@ namespace CPS { /// Interface to be used by synchronous generators class MNASyncGenInterface { protected: - // #### Model specific variables #### - /// voltage behind the transient/subtransiet impedance at time k-1 - Attribute::Ptr mEdq; - /// Matrix used when numerical method of predictor step = Euler /// State Matrix backward euler: Edq(k) = mA_euler * Edq(k) + mB_euler * Idq + mC_euler * Ef Matrix mA_euler; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index a93a99d921..1b76e98074 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -12,17 +12,17 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& uid, const String& name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t_t(mAttributes->create("Edq")) { mPhaseType = PhaseType::Single; setTerminalNumber(1); /// initialize attributes - mEdq = mAttributes->create("Edq"); mNumIter = mAttributes->create("NIterations", 0); - // Initialize matrix - **mEdq = Matrix::Zero(2,1); + // model variables + **mEdq_t_t = Matrix::Zero(2,1); } DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM @@ -41,8 +41,8 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { calculateStateMatrix(); // initial voltage behind the transient reactance in the dq0 reference frame - (**mEdq)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; - (**mEdq)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; + (**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 transformation matrix dp->dq mDpToDq = Matrix::Zero(1,2); @@ -55,8 +55,8 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - (**mEdq)(0,0), - (**mEdq)(1,0), + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), mMaxIter, mTolerance ); @@ -96,7 +96,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { **mNumIter = 0; // store values currently at t=k-1 for later use - mEdqtPrevStep = **mEdq; + mEdq_ttPrevStep = **mEdq_t; mIdqPrevStep = **mIdq; mElecTorquePrevStep = **mElecTorque; mOmMechPrevStep = **mOmMech; @@ -104,11 +104,11 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { mDeltaPrevStep = **mDelta; // prediction emf at t=k - **mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + **mEdq_t = mA_euler * (**mEdq_t) + mB_euler * **mIdq + mC_euler * (**mEf); // predict stator currents at t=k (assuming Vdq(k+1)=Vdq(k)) - (**mIdq)(0,0) = ((**mEdq)(1,0) - (**mVdq)(1,0)) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(0,0)) / mLq_t; + (**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 mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); @@ -143,11 +143,11 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) - (**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + (**mEdq_t) = mA_prev * mEdq_ttPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) - (**mIdq)(0,0) = ((**mEdq)(1,0) - (**mVdq)(1,0) ) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(0,0) ) / mLq_t; + (**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 mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 9bb235f5ad..149f81701e 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -12,17 +12,19 @@ using namespace CPS; DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM (const String& uid, const String& name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel) { + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(mAttributes->create("Edq_t")), + mEdq_s(mAttributes->create("Edq_s")) { mPhaseType = PhaseType::Single; setTerminalNumber(1); /// initialize attributes - mEdq = mAttributes->create("Edq"); mNumIter = mAttributes->create("NIterations", 0); - // Initialize matrix - **mEdq = Matrix::Zero(4,1); + // model variables + **mEdq_t = Matrix::Zero(2,1); + **mEdq_s = Matrix::Zero(2,1); } DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM @@ -42,10 +44,10 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { calculateStateMatrix(); // initial voltage behind the transient reactance in the dq0 reference frame - (**mEdq)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); - (**mEdq)(1,0) = - (mLd- mLd_t) * (**mIdq)(0,0) + (**mEf); - (**mEdq)(2,0) = (**mEdq)(0,0) + (mLq_t - mLq_s) * (**mIdq)(1,0); - (**mEdq)(3,0) = (**mEdq)(1,0) - (mLd_t - mLd_s) * (**mIdq)(0,0); + (**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); // initialize transformation matrix dp->dq mDpToDq = Matrix::Zero(1,2); @@ -60,10 +62,10 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { "\nTolerance: {:f}" "\n--- Model specific initialization finished ---", - (**mEdq)(0,0), - (**mEdq)(1,0), - (**mEdq)(2,0), - (**mEdq)(3,0), + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), + (**mEdq_s)(0,0), + (**mEdq_s)(1,0), mMaxIter, mTolerance ); @@ -117,7 +119,8 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { **mNumIter = 0; // store values currently at t=k-1 for later use - mEdqtPrevStep = **mEdq; + mEdqsPrevStep = **mEdq_s; + mEdqtPrevStep = **mEdq_t; mIdqPrevStep = **mIdq; mElecTorquePrevStep = **mElecTorque; mOmMechPrevStep = **mOmMech; @@ -125,11 +128,11 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { mDeltaPrevStep = **mDelta; // prediction emf at t=k - **mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + auto mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); // predict armature currents for at t=k+1 - (**mIdq)(0,0) = ((**mEdq)(3,0) - (**mVdq)(1,0) ) / mLd_s; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(2,0) ) / mLq_s; + (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_s; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_s; // convert currents into the abc reference frame mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); @@ -160,11 +163,12 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) + // TODO (**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) - (**mIdq)(0,0) = ((**mEdq)(3,0) - (**mVdq)(1,0) ) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq)(2,0) ) / mLq_t; + (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_t; // convert corrected currents to dp domain mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); From 9741c0f4a09141530f7d2c83d2fde342c5ce1b76 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Sun, 12 Mar 2023 23:53:16 +0100 Subject: [PATCH 43/68] PCM: Remove TR on mechanical variables in correctorStep Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 8 ---- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 8 ---- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 40 +++---------------- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 29 ++------------ 4 files changed, 10 insertions(+), 75 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index f75f52bb8c..5b06d41054 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -66,14 +66,6 @@ namespace Ph1 { Matrix mVdqPrevIter; /// Idq at k Matrix mIdqPrevStep; - /// Electrica torque at k - Real mElecTorquePrevStep; - /// Mechanical omega at k - Real mOmMechPrevStep; - /// Mechanical theta at k - Real mThetaMechPrevStep; - /// Delta at k - Real mDeltaPrevStep; }; } } diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index b99b9b8728..83521471c8 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -69,14 +69,6 @@ namespace Ph1 { Matrix mVdqPrevIter; /// Idq at k Matrix mIdqPrevStep; - /// Electrica torque at k - Real mElecTorquePrevStep; - /// Mechanical omega at k - Real mOmMechPrevStep; - /// Mechanical theta at k - Real mThetaMechPrevStep; - /// Delta at k - Real mDeltaPrevStep; }; } } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 1b76e98074..1c27224d92 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -13,7 +13,7 @@ using namespace CPS; DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM (const String& uid, const String& name, Logger::Level logLevel) : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t_t(mAttributes->create("Edq")) { + mEdq_t(mAttributes->create("Edq")) { mPhaseType = PhaseType::Single; setTerminalNumber(1); @@ -22,7 +22,7 @@ DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM mNumIter = mAttributes->create("NIterations", 0); // model variables - **mEdq_t_t = Matrix::Zero(2,1); + **mEdq_t = Matrix::Zero(2,1); } DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM @@ -96,16 +96,12 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { **mNumIter = 0; // store values currently at t=k-1 for later use - mEdq_ttPrevStep = **mEdq_t; + mEdqtPrevStep = **mEdq_t; mIdqPrevStep = **mIdq; - mElecTorquePrevStep = **mElecTorque; - mOmMechPrevStep = **mOmMech; - mThetaMechPrevStep = **mThetaMech; - mDeltaPrevStep = **mDelta; // prediction emf at t=k **mEdq_t = mA_euler * (**mEdq_t) + mB_euler * **mIdq + mC_euler * (**mEf); - + // predict stator currents at t=k (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; @@ -121,37 +117,19 @@ void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matri } void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { - // TODO: The naming of variables becomes confusing. - // Probably better to use `PrevIter` and `CurrIter` - // variables instead of misusing `_pred` variables or `mVdq`. // increase number of iterations **mNumIter = **mNumIter + 1; - // correction of mechanical vars - if (mSimTime>0.0) { - // In step 0 do not update electrical variables to avoid delay with voltage sources - // calculate electrical torque at t=k+1 - **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - - // correct mechanical variables at t=k+1 (trapezoidal rule) - **mOmMech = mOmMechPrevStep + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorquePrevStep); - **mDelta = mDeltaPrevStep + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMechPrevStep - 2); - // CHECK: For mThetaMech_corr use mOmMech_corr already? - **mThetaMech = mThetaMechPrevStep + mTimeStep / 2. * (**mOmMech + mOmMechPrevStep) * mBase_OmMech; - } - // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) - (**mEdq_t) = mA_prev * mEdq_ttPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + (**mEdq_t) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); // 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 - mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; // stamp currents @@ -159,9 +137,6 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { } void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { - // TODO: The naming of variables becomes confusing. - // Probably better to use `PrevIter` and `CurrIter` - // variables instead of misusing `_pred` variables or `mVdq`. // store voltage value currently at j-1 for later use mVdqPrevIter = **mVdq; @@ -173,10 +148,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - if (**mNumIter == 0) - **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; - else - **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 149f81701e..3257576d43 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -122,13 +122,10 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { mEdqsPrevStep = **mEdq_s; mEdqtPrevStep = **mEdq_t; mIdqPrevStep = **mIdq; - mElecTorquePrevStep = **mElecTorque; - mOmMechPrevStep = **mOmMech; - mThetaMechPrevStep = **mThetaMech; - mDeltaPrevStep = **mDelta; // prediction emf at t=k - auto mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + // TODO + //auto mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); // predict armature currents for at t=k+1 (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_s; @@ -148,31 +145,16 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; - // correction of mechanical vars - if (mSimTime>0.0) { - // In step 0 do not update electrical variables to avoid delay with voltage sources - // calculate electrical torque at t=k+1 - **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); - - // correct mechanical variables at t=k+1 (trapezoidal rule) - **mOmMech = mOmMechPrevStep + mTimeStep / (4. * mH) * (2 * **mMechTorque - **mElecTorque - mElecTorquePrevStep); - **mDelta = mDeltaPrevStep + mTimeStep / 2. * mBase_OmMech * (**mOmMech + mOmMechPrevStep - 2); - // CHECK: For mThetaMech_corr use mOmMech_corr already? - **mThetaMech = mThetaMechPrevStep + mTimeStep / 2. * (**mOmMech + mOmMechPrevStep) * mBase_OmMech; - } - // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) // TODO - (**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + //(**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_t; (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_t; // convert corrected currents to dp domain - mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; // stamp currents @@ -190,10 +172,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); Matrix Vabc = Matrix(3,1); Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - if (**mNumIter == 0) - **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; - else - **mVdq = parkTransform(**mOmMech, Vabc) / mBase_V_RMS; + **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { From 8e1a7db24449bc1f235ee621dd32ced2313bdb8b Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Tue, 14 Mar 2023 17:48:40 +0100 Subject: [PATCH 44/68] PCM SG models: Unify and simplify DQ to DP transform Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 13 ++++++ .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.h | 3 -- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 13 ++++++ .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 45 ++++++++++++++----- .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp | 10 ++--- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 44 +++++++++++++----- 6 files changed, 95 insertions(+), 33 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 5b06d41054..27c46cfcc2 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -42,6 +42,14 @@ namespace Ph1 { /// void updateVoltage(const Matrix& leftVector) override; /// + void updateDQToDPTransform(); + /// + void updateDPToDQTransform(); + /// + Complex applyDQToDPTransform(const Matrix& dqMatrix); + /// + Matrix applyDPToDQTransform(const Complex& dpComplex); + /// bool requiresIteration() override; /// void initializeResistanceMatrix() final {}; @@ -55,6 +63,11 @@ namespace Ph1 { void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; protected: + /// Transform from DQ to DP domain + Matrix mDQToDPTransform = Matrix::Zero(2,2); + /// Transform from DP to DQ domain + Matrix mDPToDQTransform = Matrix::Zero(2,2); + // #### Model specific variables #### /// Transient emf const Attribute::Ptr mEdq_t; diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h index f25edad1f1..651770ba25 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h @@ -62,9 +62,6 @@ namespace Ph1 { /// Matrix mC; - /// Transformation matrix dp->dq - MatrixComp mDpToDq; - /// Vector to create abc vector from a component MatrixComp mShiftVector; diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 83521471c8..8a246fe1c7 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -43,6 +43,14 @@ namespace Ph1 { /// bool requiresIteration() override; /// + void updateDQToDPTransform(); + /// + void updateDPToDQTransform(); + /// + Complex applyDQToDPTransform(const Matrix& dqMatrix); + /// + Matrix applyDPToDQTransform(const Complex& dpComplex); + /// void initializeResistanceMatrix() final {}; // #### MNA Functions #### @@ -54,6 +62,11 @@ namespace Ph1 { void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; protected: + /// Transform from DQ to DP domain + Matrix mDQToDPTransform = Matrix::Zero(2,2); + /// Transform from DP to DQ domain + Matrix mDPToDQTransform = Matrix::Zero(2,2); + // #### Model specific variables #### /// Subransient emf const Attribute::Ptr mEdq_s; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 1c27224d92..150f34aaf8 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -13,8 +13,9 @@ 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")) { + mEdq_t(mAttributes->create("Edq_t")) { + mSGOrder = SGOrder::SG4Order; mPhaseType = PhaseType::Single; setTerminalNumber(1); @@ -44,9 +45,6 @@ void DP::Ph1::SynchronGenerator4OrderPCM::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; - // initialize transformation matrix dp->dq - mDpToDq = Matrix::Zero(1,2); - mSLog->info( "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" @@ -63,6 +61,29 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { mSLog->flush(); } +void DP::Ph1::SynchronGenerator4OrderPCM::updateDQToDPTransform() { + mDQToDPTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), + sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); +} + +void DP::Ph1::SynchronGenerator4OrderPCM::updateDPToDQTransform() { + mDPToDQTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime), + -sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); +} + +Complex DP::Ph1::SynchronGenerator4OrderPCM::applyDQToDPTransform(const Matrix& dqMatrix) { + Complex dpComplex; + dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); + return dpComplex; +} + +Matrix DP::Ph1::SynchronGenerator4OrderPCM::applyDPToDQTransform(const Complex& dpComplex) { + Matrix dqMatrix = Matrix::Zero(2,1); + 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; +} + void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { // Initialize matrices of state representation of predictor step mA_euler = Matrix::Zero(2,2); @@ -95,6 +116,10 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // set number of iterations equal to zero **mNumIter = 0; + // update DQ-DP transforms according to mThetaMech + updateDQToDPTransform(); + updateDPToDQTransform(); + // store values currently at t=k-1 for later use mEdqtPrevStep = **mEdq_t; mIdqPrevStep = **mIdq; @@ -107,9 +132,8 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0)) / mLq_t; // convert currents to dp domain - mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -130,7 +154,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; // convert corrected currents to dp domain - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); @@ -145,10 +169,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage into dq reference frame - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix Vabc = Matrix(3,1); - Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; + **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp index efdf2a445f..9076247f47 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp @@ -25,9 +25,6 @@ DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM // mShiftVector = Matrix::Zero(3,1); mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - - // initialize transformation matrix dp->dq - mDpToDq = Matrix::Zero(1,2); } DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM @@ -128,9 +125,10 @@ void DP::Ph1::SynchronGenerator6OrderDCIM::stepInPerUnit() { **mIdq << mStates(4,0), mStates(5,0); // convert currents into the abc reference frame - mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + // TODO + //mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); + //mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); + //(**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 3257576d43..1a92a3ec02 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -16,6 +16,7 @@ DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM mEdq_t(mAttributes->create("Edq_t")), mEdq_s(mAttributes->create("Edq_s")) { + mSGOrder = SGOrder::SG6bOrder; mPhaseType = PhaseType::Single; setTerminalNumber(1); @@ -49,9 +50,6 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { (**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); - // initialize transformation matrix dp->dq - mDpToDq = Matrix::Zero(1,2); - mSLog->info( "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" @@ -72,6 +70,29 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { mSLog->flush(); } +void DP::Ph1::SynchronGenerator6OrderPCM::updateDQToDPTransform() { + mDQToDPTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), + sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); +} + +void DP::Ph1::SynchronGenerator6OrderPCM::updateDPToDQTransform() { + mDPToDQTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime), + -sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); +} + +Complex DP::Ph1::SynchronGenerator6OrderPCM::applyDQToDPTransform(const Matrix& dqMatrix) { + Complex dpComplex; + dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); + return dpComplex; +} + +Matrix DP::Ph1::SynchronGenerator6OrderPCM::applyDPToDQTransform(const Complex& dpComplex) { + Matrix dqMatrix = Matrix::Zero(2,1); + 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; +} + void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { // Initialize matrix of state representation of predictor step mA_euler = Matrix::Zero(4,4); @@ -131,10 +152,8 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_s; (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_s; - // convert currents into the abc reference frame - mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + // convert currents to dp domain + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -145,6 +164,10 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; + // update DQ-DP transforms according to mThetaMech + updateDQToDPTransform(); + updateDPToDQTransform(); + // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) // TODO @@ -155,7 +178,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_t; // convert corrected currents to dp domain - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); @@ -169,10 +192,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage into dq reference frame - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix Vabc = Matrix(3,1); - Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; + **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { From c45c1d39bb8dd6323bc2f4ed499cd58c6fd02a47 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 16 Mar 2023 13:15:12 +0100 Subject: [PATCH 45/68] fix error in SG 6 Order PCM Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 17 +++- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 17 +++- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 61 +++++------- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 1 + .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 98 ++++++++----------- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 2 + 6 files changed, 92 insertions(+), 104 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 27c46cfcc2..527a5d81f7 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -34,7 +34,7 @@ namespace Ph1 { /// void specificInitialization() override; /// - void calculateStateMatrix(); + void calculateStateSpaceMatrices(); /// void stepInPerUnit() override; // @@ -67,7 +67,7 @@ namespace Ph1 { Matrix mDQToDPTransform = Matrix::Zero(2,2); /// Transform from DP to DQ domain Matrix mDPToDQTransform = Matrix::Zero(2,2); - + // #### Model specific variables #### /// Transient emf const Attribute::Ptr mEdq_t; @@ -75,10 +75,19 @@ namespace Ph1 { // Variables saving values for later use /// Edqt at k Matrix mEdqtPrevStep; - /// Vdq at j-1 - Matrix mVdqPrevIter; /// 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_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 8a246fe1c7..fb649d9872 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -33,7 +33,7 @@ namespace Ph1 { /// void specificInitialization() override; /// - void calculateStateMatrix(); + void calculateStateSpaceMatrices(); /// void stepInPerUnit() override; // @@ -72,16 +72,23 @@ namespace Ph1 { const Attribute::Ptr mEdq_s; /// Transient emf const Attribute::Ptr mEdq_t; + /// + Matrix mEdqts = Matrix::Zero(4,1); // Variables saving values for later use - /// Edqs at k - Matrix mEdqsPrevStep; - /// Edqt at k - Matrix mEdqtPrevStep; + /// 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/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 150f34aaf8..c0c878b6d8 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -39,7 +39,7 @@ SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderPCM::clone(const Stri void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { // calculate state representation matrix - calculateStateMatrix(); + calculateStateSpaceMatrices(); // initial voltage behind the transient reactance in the dq0 reference frame (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; @@ -51,6 +51,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { "\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), @@ -84,56 +85,41 @@ Matrix DP::Ph1::SynchronGenerator4OrderPCM::applyDPToDQTransform(const Complex& return dqMatrix; } -void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateMatrix() { +void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateSpaceMatrices() { // Initialize matrices of state representation of predictor step - mA_euler = Matrix::Zero(2,2); - mA_euler << 1 - mTimeStep / mTq0_t, 0.0, - 0.0, 1 - mTimeStep / mTd0_t; - mB_euler = Matrix::Zero(2,2); - mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, - - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0; - mC_euler = Matrix::Zero(2,1); - mC_euler << 0.0, - (mTimeStep / mTd0_t); - - // Initialize matrix of state representation of corrector step - mA_prev = Matrix::Zero(2,2); - mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, 1 - mTimeStep / (2 * mTd0_t); - mA_corr = Matrix::Zero(2,2); - mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, - 0.0, - mTimeStep / (2 * mTd0_t); - mB_corr = Matrix::Zero(2,2); - mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0; - - mC_corr = Matrix::Zero(2,1); - mC_corr << 0.0, - (mTimeStep / mTd0_t); + 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, + **mEf / mTd0_t; } 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; + // update DQ-DP transforms according to mThetaMech updateDQToDPTransform(); updateDPToDQTransform(); - // store values currently at t=k-1 for later use - mEdqtPrevStep = **mEdq_t; - mIdqPrevStep = **mIdq; - - // prediction emf at t=k - **mEdq_t = mA_euler * (**mEdq_t) + mB_euler * **mIdq + mC_euler * (**mEf); + // predict emf at t=k+1 (euler) using + (**mEdq_t) = Math::StateSpaceEuler(**mEdq_t, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mVdq); - // predict stator currents at t=k (assuming Vdq(k+1)=Vdq(k)) + // 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) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + // store values currently at t=k for later use + mVdqPrevStep = **mVdq; + mEdqtPrevStep = **mEdq_t; } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -145,16 +131,17 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // increase number of iterations **mNumIter = **mNumIter + 1; - // correction of electrical vars - // correct emf at t=k+1 (trapezoidal rule) - (**mEdq_t) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq_t) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + // correct electrical vars + // calculate emf at j and k+1 (trapezoidal rule) + (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **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) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 9841ea165a..6a10a05e36 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -101,6 +101,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { "\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) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 1a92a3ec02..7088b56f90 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -42,7 +42,7 @@ SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderPCM::clone(const Stri void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { // calculate state representation matrix - calculateStateMatrix(); + calculateStateSpaceMatrices(); // initial voltage behind the transient reactance in the dq0 reference frame (**mEdq_t)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); @@ -50,6 +50,9 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { (**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}" @@ -58,6 +61,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { "\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), @@ -93,64 +97,46 @@ Matrix DP::Ph1::SynchronGenerator6OrderPCM::applyDPToDQTransform(const Complex& return dqMatrix; } -void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateMatrix() { - // Initialize matrix of state representation of predictor step - mA_euler = Matrix::Zero(4,4); - mA_euler << 1 - mTimeStep / mTq0_t, 0.0, 0.0, 0.0, - 0.0, 1 - mTimeStep / mTd0_t, 0.0, 0.0, - mTimeStep / mTq0_s, 0.0, 1 - mTimeStep / mTq0_s, 0.0, - 0.0, mTimeStep / mTd0_s, 0.0, 1 - mTimeStep / mTd0_s; - mB_euler = Matrix::Zero(4,2); - mB_euler << 0.0, (mLq - mLq_t) * mTimeStep / mTq0_t, - - (mLd - mLd_t) * mTimeStep / mTd0_t, 0.0, - 0.0, (mLq_t - mLq_s) * mTimeStep / mTq0_s, - - (mLd_t - mLd_s) * mTimeStep / mTd0_s, 0.0; - mC_euler = Matrix::Zero(4,1); - mC_euler << 0.0, - (mTimeStep / mTd0_t), - 0.0, - 0.0; - - // Initialize matrix of state representation of corrector step - mA_prev = Matrix::Zero(4, 4); - mA_prev << 1 - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, - 0.0, 1 - mTimeStep / (2 * mTd0_t), 0.0, 0.0, - mTimeStep / (2 * mTq0_s), 0.0, 1 - mTimeStep / (2 * mTq0_s), 0.0, - 0.0, mTimeStep / (2 * mTd0_s), 0.0, 1 - mTimeStep / (2 * mTd0_s); - mA_corr = Matrix::Zero(4,4); - mA_corr << - mTimeStep / (2 * mTq0_t), 0.0, 0.0, 0.0, - 0.0, - mTimeStep / (2 * mTd0_t), 0.0, 0.0, - mTimeStep / (2 * mTq0_s), 0.0, - mTimeStep / (2 * mTq0_s), 0.0, - 0.0, mTimeStep / (2 * mTd0_s), 0.0, - mTimeStep / (2 * mTd0_s); - - mB_corr = Matrix::Zero(4,2); - mB_corr << 0.0, (mLq - mLq_t) * mTimeStep / (2 * mTq0_t), - - (mLd - mLd_t) * mTimeStep / (2 * mTd0_t), 0.0, - 0.0, (mLq_t - mLq_s) * mTimeStep / (2 * mTq0_s), - - (mLd_t - mLd_s) * mTimeStep / (2 * mTd0_s), 0.0; - mC_corr = Matrix::Zero(4,1); - mC_corr << 0.0, - (mTimeStep / mTd0_t), - 0.0, - 0.0; +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., + **mEf / mTd0_t, + 0., + 0.; } void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // store values currently at t=k-1 for later use - mEdqsPrevStep = **mEdq_s; - mEdqtPrevStep = **mEdq_t; + // store values currently at t=k for later use mIdqPrevStep = **mIdq; + mEdqtsPrevStep = mEdqts; + + // update DQ-DP transforms according to mThetaMech + updateDQToDPTransform(); + updateDPToDQTransform(); - // prediction emf at t=k - // TODO - //auto mEdq = mA_euler * (**mEdq) + mB_euler * **mIdq + mC_euler * (**mEf); + // predict emf at t=k+1 (euler) using + mEdqts = Math::StateSpaceEuler(mEdqts, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mIdq); // predict armature currents for at t=k+1 - (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_s; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_s; + (**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) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; @@ -164,18 +150,12 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { // corrector step (trapezoidal rule) **mNumIter = **mNumIter + 1; - // update DQ-DP transforms according to mThetaMech - updateDQToDPTransform(); - updateDPToDQTransform(); - - // correction of electrical vars // correct emf at t=k+1 (trapezoidal rule) - // TODO - //(**mEdq) = mA_prev * mEdqtPrevStep + mA_corr * (**mEdq) + mB_corr * (mIdqPrevStep + **mIdq) + mC_corr * (**mEf); + mEdqts = Math::StateSpaceTrapezoidal(mEdqtsPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mIdq, mIdqPrevStep); // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) - (**mIdq)(0,0) = ((**mEdq_s)(1,0) - (**mVdq)(1,0) ) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_s)(0,0) ) / mLq_t; + (**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) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; @@ -213,5 +193,7 @@ bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { } 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/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index c1d424c8ce..acc3ccb791 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -181,6 +181,8 @@ int main(int argc, char* argv[]) { 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")); From cc96faa23fbf1e787060792b7e3e65e9aba6158a Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 16 Mar 2023 17:06:48 +0100 Subject: [PATCH 46/68] add iterative SG models to dpsimpy Signed-off-by: Martin Moraga --- dpsim/src/pybind/BaseComponents.cpp | 17 +++++++++++++++++ dpsim/src/pybind/DPComponents.cpp | 21 ++++++++++++++++----- dpsim/src/pybind/EMTComponents.cpp | 6 +----- dpsim/src/pybind/SPComponents.cpp | 6 +----- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/dpsim/src/pybind/BaseComponents.cpp b/dpsim/src/pybind/BaseComponents.cpp index 49e9842b7f..05f9e62a62 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_current_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsCurrentSource, "model_as_current_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_current_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsCurrentSource, "model_as_current_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) From 3e5af48a1d6c2ab6318f6bb89c63792594765f87 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Thu, 16 Mar 2023 18:12:52 +0100 Subject: [PATCH 47/68] add validation notebook for PCM models Signed-off-by: Martin Moraga --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 8 +- ...SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb | 561 ++++++++++++++++++ 2 files changed, 563 insertions(+), 6 deletions(-) create mode 100644 examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index c0c878b6d8..b8710b0496 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -102,6 +102,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { // store values currently at t=k for later use mEdqtPrevStep = **mEdq_t; mIdqPrevStep = **mIdq; + mVdqPrevStep = **mVdq; // update DQ-DP transforms according to mThetaMech updateDQToDPTransform(); @@ -115,11 +116,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0)) / mLq_t; // convert currents to dp domain - (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; - - // store values currently at t=k for later use - mVdqPrevStep = **mVdq; - mEdqtPrevStep = **mEdq_t; + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -135,7 +132,6 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // calculate emf at j and k+1 (trapezoidal rule) (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **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; 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..f7356d9aef --- /dev/null +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb @@ -0,0 +1,561 @@ +{ + "cells": [ + { + "attachments": {}, + "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 = 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" + ] + }, + { + "attachments": {}, + "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; " + ] + }, + { + "attachments": {}, + "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()" + ] + }, + { + "attachments": {}, + "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" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run DPSim simulations (DP Domain)" + ] + }, + { + "attachments": {}, + "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])" + ] + }, + { + "attachments": {}, + "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])" + ] + }, + { + "attachments": {}, + "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])" + ] + }, + { + "attachments": {}, + "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])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compare Results" + ] + }, + { + "attachments": {}, + "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<1.07)" + ] + }, + { + "attachments": {}, + "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.8)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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 +} From b6dc0219ef76f7812c949b4c9790424fc035f74b Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Fri, 17 Mar 2023 09:20:18 +0100 Subject: [PATCH 48/68] PCM and TPM: remove Ef from state matrices Signed-off-by: Martin Moraga --- dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 6 +++--- dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 4 ++-- dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index b8710b0496..15df053cb5 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -92,7 +92,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateSpaceMatrices() { mBStateSpace << (mLq-mLq_t) / mTq0_t / mLq_t, 0.0, 0.0, (mLd-mLd_t) / mTd0_t / mLd_t; mCStateSpace << 0, - **mEf / mTd0_t; + 1 / mTd0_t; } void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { @@ -109,7 +109,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { updateDPToDQTransform(); // predict emf at t=k+1 (euler) using - (**mEdq_t) = Math::StateSpaceEuler(**mEdq_t, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mVdq); + (**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; @@ -130,7 +130,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // correct electrical vars // calculate emf at j and k+1 (trapezoidal rule) - (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mVdq, mVdqPrevStep); + (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **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; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 6a10a05e36..9f53e0d14a 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -90,7 +90,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateStateSpaceMatrices() { mBStateSpace << (mLq-mLq_t) / mTq0_t / mLq_t, 0.0, 0.0, (mLd-mLd_t) / mTd0_t / mLd_t; mCStateSpace << 0, - **mEf / mTd0_t; + 1 / mTd0_t; } void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { @@ -205,7 +205,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // correct electrical vars // calculate emf at j and k+1 (trapezoidal rule) - (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mVdq, mVdqPrevStep); + (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mVdq, mVdqPrevStep); // calculate stator currents at j and k+1 (**mIdq)(0,0) = ((**mEdq_t)(1,0) - (**mVdq)(1,0) ) / mLd_t; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 7088b56f90..0d739e4b41 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -114,7 +114,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateSpaceMatrices() { 0., Bd_s, -Bq_s, 0.; mCStateSpace << 0., - **mEf / mTd0_t, + 1 / mTd0_t, 0., 0.; } @@ -132,7 +132,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { updateDPToDQTransform(); // predict emf at t=k+1 (euler) using - mEdqts = Math::StateSpaceEuler(mEdqts, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mIdq); + 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; @@ -151,7 +151,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { **mNumIter = **mNumIter + 1; // correct emf at t=k+1 (trapezoidal rule) - mEdqts = Math::StateSpaceTrapezoidal(mEdqtsPrevStep, mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, **mIdq, mIdqPrevStep); + mEdqts = Math::StateSpaceTrapezoidal(mEdqtsPrevStep, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **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; From e49a6fee350a695f8092d68442938b91dff58126 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 17 Mar 2023 12:32:59 +0100 Subject: [PATCH 49/68] refactor iteration check of pcm model Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 15df053cb5..4cf3591c82 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -108,15 +108,15 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { updateDQToDPTransform(); updateDPToDQTransform(); - // predict emf at t=k+1 (euler) using + // 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) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -156,19 +156,20 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector } bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { - if (**mNumIter == 0) - // if no corrector step has been performed yet + 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; - - 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; + // 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; } } From fd4691ffee27563ca35ebd91b93d7378b2a21d00 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 17 Mar 2023 17:28:21 +0100 Subject: [PATCH 50/68] reduce vbr ref timestep in pcm validation notebook Signed-off-by: Jan Dinkelbach --- ...SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) 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 index f7356d9aef..6688624907 100644 --- a/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb @@ -1,7 +1,6 @@ { "cells": [ { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -64,7 +63,7 @@ "roi_end = 1\n", "\n", "# config params\n", - "timestep_vbr = 1e-3\n", + "timestep_vbr = 0.1e-3\n", "timestep_pcm = 10e-6\n", "max_iter = 10\n", "tolerance = 1e-6\n", @@ -82,7 +81,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -136,7 +134,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -205,7 +202,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -324,7 +320,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -332,7 +327,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -364,7 +358,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -396,7 +389,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -427,7 +419,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -457,7 +448,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -465,7 +455,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -493,11 +482,10 @@ "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<1.07)" + "assert(rmse<0.449)" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -525,13 +513,20 @@ "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.8)" + "assert(rmse<0.381)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, From 41e06e088d2b5ac87a4f58afcdd26eb544327ab9 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Sat, 18 Mar 2023 12:38:41 +0100 Subject: [PATCH 51/68] remove deprecated dp dcim models replaced by dp pcm models with zero iter Signed-off-by: Jan Dinkelbach --- .../include/dpsim-models/Components.h | 2 - .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.h | 82 -------- .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.h | 95 --------- dpsim-models/src/CMakeLists.txt | 2 - .../DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp | 142 ------------- .../DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp | 165 --------------- dpsim/examples/cxx/CMakeLists.txt | 2 - .../DP_SynGen4OrderDCIM_SMIB_Fault.cpp | 187 ----------------- .../DP_SynGen6OrderDCIM_SMIB_Fault.cpp | 188 ------------------ 9 files changed, 865 deletions(-) delete mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h delete mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h delete mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp delete mode 100644 dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp delete mode 100644 dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp delete mode 100644 dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index db4ad58173..27cc553d62 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -58,10 +58,8 @@ #include #include #include -#include #include #include -#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h deleted file mode 100644 index 878fbcfcc6..0000000000 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderDCIM.h +++ /dev/null @@ -1,82 +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 DP { -namespace Ph1 { - /// @brief Delayed-Current-Injection (DCIM) implementation - /// of 4th order synchronous generator model - class SynchronGenerator4OrderDCIM : - public Base::ReducedOrderSynchronGenerator, - public SharedFactory { - protected: - // ### State variables [p.u.]### - /// voltage behing the transient reactance - const Attribute::Ptr mEdq_t; - /// - Matrix mStates; - Matrix mStates_prev; - - /// state representation matrix - /// - Real mAd; - /// - Real mBd; - /// - Real mAq; - /// - Real mBq; - /// - Real mCq; - /// - Matrix mA; - /// - Matrix mA_inv; - /// - Matrix mB; - /// - Matrix mC; - - /// Transformation matrix dp->dq - MatrixComp mDpToDq; - - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - - /// Park Transformation - Matrix parkTransform(Real theta, const Matrix& abcVector); - - // #### General Functions #### - /// DPecific component initialization - void specificInitialization(); - /// - void stepInPerUnit(); - /// - void initializeResistanceMatrix() final {}; - - // ### MNA Section ### - /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) {}; - void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); - void mnaCompPostStep(const Matrix& leftVector); - - public: - /// - SynchronGenerator4OrderDCIM(String uid, String name, Logger::Level logLevel = Logger::Level::off); - /// - SynchronGenerator4OrderDCIM(String name, Logger::Level logLevel = Logger::Level::off); - /// - SimPowerComp::Ptr clone(String name); - }; -} -} -} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h deleted file mode 100644 index 651770ba25..0000000000 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderDCIM.h +++ /dev/null @@ -1,95 +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 DP { -namespace Ph1 { - /// @brief Delayed-Current-Injection (DCIM) implementation - /// of 4th order synchronous generator model - class SynchronGenerator6OrderDCIM : - public Base::ReducedOrderSynchronGenerator, - public SharedFactory { - protected: - // ### State variables [p.u.]### - /// voltage behing the transient reactance - const Attribute::Ptr mEdq_t; - /// voltage behind subtransient reactance - const Attribute::Ptr mEdq_s; - /// - Matrix mStates; - Matrix mStates_prev; - - /// Auxiliar VBR constants - /// - Real mAd_t; - /// - Real mBd_t; - /// - Real mAq_t; - /// - Real mBq_t; - /// - Real mDq_t; - /// - Real mAd_s; - /// - Real mAq_s; - /// - Real mBd_s; - /// - Real mBq_s; - /// - Real mCd_s; - /// - Real mCq_s; - - /// state representation matrix - /// - Matrix mA; - /// - Matrix mA_inv; - /// - Matrix mB; - /// - Matrix mC; - - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - - /// Park Transformation - Matrix parkTransform(Real theta, const Matrix& abcVector); - - // #### General Functions #### - /// DPecific component initialization - void specificInitialization(); - /// - void stepInPerUnit(); - /// - void initializeResistanceMatrix() final {}; - - // ### MNA Section ### - /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix); - void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); - void mnaCompPostStep(const Matrix& leftVector); - - public: - /// - SynchronGenerator6OrderDCIM(String uid, String name, Logger::Level logLevel = Logger::Level::off); - /// - SynchronGenerator6OrderDCIM(String name, Logger::Level logLevel = Logger::Level::off); - /// - SimPowerComp::Ptr clone(String name); - }; -} -} -} diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index bd30bb4ff8..4393012cef 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -37,10 +37,8 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp - DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp - DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp deleted file mode 100644 index 3d297c8303..0000000000 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ /dev/null @@ -1,142 +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; - -DP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM - (String uid, 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); - - // - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - - // initialize transformation matrix dp->dq - mDpToDq = Matrix::Zero(1,2); -} - -DP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM - (String name, Logger::Level logLevel) - : SynchronGenerator4OrderDCIM(name, name, logLevel) { -} - -SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderDCIM::clone(String name) { - - auto copy = SynchronGenerator4OrderDCIM::make(name, mLogLevel); - return copy; -} - -void DP::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; - - // - mStates = Matrix::Zero(4,1); - mStates << **mEdq_t, **mIdq; - mStates_prev = Matrix::Zero(6,1); - - // - mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); - mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); - - mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); - mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mCq = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); - - // Initialize matrix of state representation - mA = Matrix::Zero(4,4); - mA << 1, 0, 0, -mAd, - 0, 1, -mAq, 0, - 0, -1, mLd_t, 0, - 1, 0, 0, mLq_t; - mA_inv = mA.inverse(); - - mB = Matrix::Zero(4,6); - mB << mBd, 0, 0, mAd, 0, 0, - 0, mBq, mAq, 0, 0, 0, - 0, 0, 0, 0, 0, -1, - 0, 0, 0, 0, 1, 0; - - mC = Matrix::Zero(4,1); - mC << 0, mCq, 0, 0; - - mSLog->info( - "\n--- Model specific initialization ---" - "\nInitial Ed_t (per unit): {:f}" - "\nInitial Eq_t (per unit): {:f}" - "\n--- Model DPecific initialization finished ---", - - (**mEdq_t)(0,0), - (**mEdq_t)(1,0) - ); - mSLog->flush(); -} - -void DP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { - if (mSimTime>0.0) { - // calculate mechanical variables at t=k+1 with forward euler - **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); - **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); - **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); - **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - } - - // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - mStates_prev << mStates, **mVdq; - mStates = mA_inv * mB * mStates_prev + mA_inv * mC * (**mEf); - **mEdq_t << mStates(0,0), mStates(1,0); - **mIdq << mStates(2,0), mStates(3,0); - - // convert currents into the abc reference frame - mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); - (**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; -} - -void DP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); -} - -void DP::Ph1::SynchronGenerator4OrderDCIM::mnaCompPostStep(const Matrix& leftVector) { - - (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - - // convert armature voltage into dq reference frame - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix Vabc = Matrix(3,1); - Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; - - mSimTime = mSimTime + mTimeStep; -} - -Matrix DP::Ph1::SynchronGenerator4OrderDCIM::parkTransform(Real theta, const Matrix& abcVector) { - Matrix dq0Vector(3, 1); - Matrix dqVector(2, 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; - dqVector << dq0Vector(0,0), dq0Vector(1,0); - return dqVector; -} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp deleted file mode 100644 index 9076247f47..0000000000 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderDCIM.cpp +++ /dev/null @@ -1,165 +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; - -DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM - (String uid, String name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(mAttributes->create("Edq_t")), - mEdq_s(mAttributes->create("Edq_s")) { - - setTerminalNumber(1); - - // model variables - **mEdq_t = Matrix::Zero(2,1); - **mEdq_s = Matrix::Zero(2,1); - - // - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; -} - -DP::Ph1::SynchronGenerator6OrderDCIM::SynchronGenerator6OrderDCIM - (String name, Logger::Level logLevel) - : SynchronGenerator6OrderDCIM(name, name, logLevel) { -} - -SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderDCIM::clone(String name) { - - auto copy = SynchronGenerator6OrderDCIM::make(name, mLogLevel); - return copy; -} - -void DP::Ph1::SynchronGenerator6OrderDCIM::specificInitialization() { - - // initial voltage behind the transient reactance in the dq reference frame - (**mEdq_t)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); - (**mEdq_t)(1,0) = (**mEf) - (mLd - mLd_t) * (**mIdq)(0,0); - - // initial dq behind the subtransient reactance in the dq reference frame - (**mEdq_s)(0,0) = (**mVdq)(0,0) - mLq_s * (**mIdq)(1,0); - (**mEdq_s)(1,0) = (**mVdq)(1,0) + mLd_s * (**mIdq)(0,0); - - // - mStates = Matrix::Zero(6,1); - mStates << **mEdq_s, **mEdq_t, **mIdq; - mStates_prev = Matrix::Zero(8,1); - - // - mAd_t = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); - mBd_t = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); - mAq_t = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); - mBq_t = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mDq_t = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); - - // - mAd_s = mTimeStep * (mLq_t - mLq_s) / (2 * mTq0_s + mTimeStep); - mBd_s = mTimeStep / (2 * mTq0_s + mTimeStep); - mCd_s = (2 * mTq0_s - mTimeStep) / (2 * mTq0_s + mTimeStep); - mAq_s = mTimeStep * (mLd_t - mLd_s) / (2 * mTd0_s + mTimeStep); - mBq_s = mTimeStep / (2 * mTd0_s + mTimeStep); - mCq_s = (2 * mTd0_s - mTimeStep) / (2 * mTd0_s + mTimeStep); - - // Initialize matrix of state representation - mA = Matrix::Zero(6,6); - mA << 1, 0, -mBd_s, 0, 0, -mAd_s, - 0, 1, 0, -mBq_s, mAq_s, 0, - 0, 0, 1, 0, 0, -mAd_t, - 0, 0, 0, 1, -mAq_t, 0, - 0, -1, 0, 0, mLd_s, 0, - 1, 0, 0, 0, 0, mLq_s; - mA_inv = mA.inverse(); - - mB = Matrix::Zero(6,8); - mB << mCd_s, 0, mBd_s, 0, 0, mAd_s, 0, 0, - 0, mCq_s, 0, mBq_s, -mAq_s, 0, 0, 0, - 0, 0, mBd_t, 0, 0, mAd_t, 0, 0, - 0, 0, 0, mBq_t, mAq_t, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, -1, - 0, 0, 0, 0, 0, 0, 1, 0; - - mC = Matrix::Zero(6,1); - mC << 0, 0, 0, mDq_t, 0, 0; - - 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}" - "\n--- Model specific initialization finished ---", - - (**mEdq_t)(0,0), - (**mEdq_t)(1,0), - (**mEdq_s)(0,0), - (**mEdq_s)(1,0) - ); - mSLog->flush(); -} - -void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { -} - -void DP::Ph1::SynchronGenerator6OrderDCIM::stepInPerUnit() { - if (mSimTime>0.0) { - // calculate mechanical variables at t=k+1 with forward euler - **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); - **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); - **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); - **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - } - - // calculate Edq and Idq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - mStates_prev << mStates, **mVdq; - mStates = mA_inv * mB * mStates_prev + mA_inv * mC * (**mEf); - **mEdq_s << mStates(0,0), mStates(1,0); - **mEdq_t << mStates(2,0), mStates(3,0); - **mIdq << mStates(4,0), mStates(5,0); - - // convert currents into the abc reference frame - // TODO - //mDpToDq(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - //mDpToDq(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); - //(**mIntfCurrent)(0,0) = (mDpToDq * **mIdq)(0,0) * mBase_I_RMS; -} - -void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); -} - -void DP::Ph1::SynchronGenerator6OrderDCIM::mnaCompPostStep(const Matrix& leftVector) { - - (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - - // convert armature voltage into dq reference frame - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - Matrix Vabc = Matrix(3,1); - Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - **mVdq = parkTransform(**mThetaMech, Vabc) / mBase_V_RMS; - - mSimTime = mSimTime + mTimeStep; -} - -Matrix DP::Ph1::SynchronGenerator6OrderDCIM::parkTransform(Real theta, const Matrix& abcVector) { - Matrix dq0Vector(3, 1); - Matrix dqVector(2, 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; - dqVector << dq0Vector(0,0), dq0Vector(1,0); - return dqVector; -} diff --git a/dpsim/examples/cxx/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index b7662f197f..3c00cd75eb 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -75,8 +75,6 @@ set(CIRCUIT_SOURCES Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp - Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp - Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp # SMIB Reduced Order - Load step diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp deleted file mode 100644 index e64051c60c..0000000000 --- a/dpsim/examples/cxx/Circuits/DP_SynGen4OrderDCIM_SMIB_Fault.cpp +++ /dev/null @@ -1,187 +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; - -void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, - Real startTimeFault, Real endTimeFault, Real logDownSampling, Real switchOpen, - Real switchClosed, 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(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 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), 0.0, 0.0}; - std::vector initialVoltage_n2{ n2PF->voltage()(0,0), 0.0, 0.0}; - auto n1DP = SimNode::make("n1DP", PhaseType::Single, initialVoltage_n1); - auto n2DP = SimNode::make("nDSP", PhaseType::Single, initialVoltage_n2); - - // Components - auto genDP = DP::Ph1::SynchronGenerator4OrderDCIM::make("SynGen", Logger::Level::debug); - genDP->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); - genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - - //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); - - //Breaker - auto fault = DP::Ph1::Switch::make("Br_fault", logLevel); - fault->setParameters(switchOpen, switchClosed); - fault->open(); - - // Topology - genDP->connect({ n1DP }); - lineDP->connect({ n1DP, n2DP }); - extnetDP->connect({ n2DP }); - fault->connect({DP::SimNode::GND, n1DP}); - auto systemDP = SystemTopology(GridParams.nomFreq, - SystemNodeList{n1DP, n2DP}, - SystemComponentList{genDP, lineDP, extnetDP, fault}); - - // Logging - auto loggerDP = DataLogger::make(simNameDP, true, logDownSampling); - //loggerDP->logAttribute("v_slack", extnetDP->attribute("v_intf")); - //loggerDP->logAttribute("i_slack", extnetDP->attribute("i_intf")); - //loggerDP->logAttribute("v_gen", genDP->attribute("v_intf")); - //loggerDP->logAttribute("i_gen", genDP->attribute("i_intf")); - loggerDP->logAttribute("Te", genDP->attribute("Te")); - loggerDP->logAttribute("delta", genDP->attribute("delta")); - loggerDP->logAttribute("w_r", genDP->attribute("w_r")); - loggerDP->logAttribute("Edq0", genDP->attribute("Edq_t")); - loggerDP->logAttribute("Vdq0", genDP->attribute("Vdq0")); - loggerDP->logAttribute("Idq0", genDP->attribute("Idq0")); - - 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(loggerDP); - //simDP.doSystemMatrixRecomputation(true); - - // Events - auto sw1 = SwitchEvent::make(startTimeFault, fault, true); - simDP.addEvent(sw1); - - auto sw2 = SwitchEvent::make(endTimeFault, fault, false); - simDP.addEvent(sw2); - - simDP.run(); -} - -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-9; - Real H = syngenKundur.H; - Real finalTime = 2; - - // Command line args processing - CommandLineArgs args(argc, argv); - std::string stepSize_str = ""; - std::string inertia_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("Inertia") != args.options.end()) { - H = args.getOptionReal("Inertia"); - inertia_str = "_Inertia_" + std::to_string(H); - } - } - - Real logDownSampling; - if (timeStep<10e-6) - logDownSampling = floor((10e-6) / timeStep); - else - logDownSampling = 1.0; - Logger::Level logLevel = Logger::Level::off; - std::string simName = "DP_SynGen4OrderDCIM_SMIB_Fault" + stepSize_str + inertia_str; - DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, - logDownSampling, SwitchOpen, SwitchClosed, logLevel); -} diff --git a/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp deleted file mode 100644 index bd2db7a019..0000000000 --- a/dpsim/examples/cxx/Circuits/DP_SynGen6OrderDCIM_SMIB_Fault.cpp +++ /dev/null @@ -1,188 +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; - -void DP_1ph_SynGen_Fault(String simName, Real timeStep, Real finalTime, Real H, - Real startTimeFault, Real endTimeFault, Real logDownSampling, Real switchOpen, - Real switchClosed, 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(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 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), 0.0, 0.0}; - std::vector initialVoltage_n2{ n2PF->voltage()(0,0), 0.0, 0.0}; - auto n1DP = SimNode::make("n1DP", PhaseType::Single, initialVoltage_n1); - auto n2DP = SimNode::make("nDSP", PhaseType::Single, initialVoltage_n2); - - // Components - auto genDP = DP::Ph1::SynchronGenerator6OrderDCIM::make("SynGen", Logger::Level::debug); - genDP->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); - genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - - //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); - - //Breaker - auto fault = DP::Ph1::Switch::make("Br_fault", logLevel); - fault->setParameters(switchOpen, switchClosed); - fault->open(); - - // Topology - genDP->connect({ n1DP }); - lineDP->connect({ n1DP, n2DP }); - extnetDP->connect({ n2DP }); - fault->connect({DP::SimNode::GND, n1DP}); - auto systemDP = SystemTopology(GridParams.nomFreq, - SystemNodeList{n1DP, n2DP}, - SystemComponentList{genDP, lineDP, extnetDP, fault}); - - // Logging - auto loggerDP = DataLogger::make(simNameDP, true, logDownSampling); - //loggerDP->logAttribute("v_slack", extnetDP->attribute("v_intf")); - //loggerDP->logAttribute("i_slack", extnetDP->attribute("i_intf")); - //loggerDP->logAttribute("v_gen", genDP->attribute("v_intf")); - //loggerDP->logAttribute("i_gen", genDP->attribute("i_intf")); - loggerDP->logAttribute("Te", genDP->attribute("Te")); - loggerDP->logAttribute("delta", genDP->attribute("delta")); - loggerDP->logAttribute("w_r", genDP->attribute("w_r")); - loggerDP->logAttribute("Edq_t", genDP->attribute("Edq_t")); - loggerDP->logAttribute("Edq_s", genDP->attribute("Edq_s")); - loggerDP->logAttribute("Vdq", genDP->attribute("Vdq0")); - loggerDP->logAttribute("Idq", genDP->attribute("Idq0")); - - 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(loggerDP); - //simDP.doSystemMatrixRecomputation(true); - - // Events - auto sw1 = SwitchEvent::make(startTimeFault, fault, true); - simDP.addEvent(sw1); - - auto sw2 = SwitchEvent::make(endTimeFault, fault, false); - simDP.addEvent(sw2); - - simDP.run(); -} - -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-9; - Real H = syngenKundur.H; - Real finalTime = 3; - - // Command line args processing - CommandLineArgs args(argc, argv); - std::string stepSize_str = ""; - std::string inertia_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("Inertia") != args.options.end()) { - H = args.getOptionReal("Inertia"); - inertia_str = "_Inertia_" + std::to_string(H); - } - } - - Real logDownSampling; - if (timeStep<10e-6) - logDownSampling = floor((10e-6) / timeStep); - else - logDownSampling = 1.0; - Logger::Level logLevel = Logger::Level::off; - std::string simName = "DP_SynGen6OrderDCIM_SMIB_Fault" + stepSize_str + inertia_str; - DP_1ph_SynGen_Fault(simName, timeStep, finalTime, H, startTimeFault, endTimeFault, - logDownSampling, SwitchOpen, SwitchClosed, logLevel); -} From 2a9ecb462d5fa39cc8818174c17009bf07a00686 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Sat, 18 Mar 2023 12:57:33 +0100 Subject: [PATCH 52/68] apply stamp on sparse system matrices with pcm and tpm models Signed-off-by: Jan Dinkelbach --- .../dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 2 +- .../dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 2 +- .../dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 4 ++-- .../include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h | 2 +- .../dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h | 2 +- dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 527a5d81f7..6abc1035cf 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -60,7 +60,7 @@ namespace Ph1 { /// void mnaCompPostStep(const Matrix& leftVector) final; /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; protected: /// Transform from DQ to DP domain diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index 25b5591fa8..aaa8e86b52 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -97,7 +97,7 @@ namespace Ph1 { /// void mnaCompPostStep(const Matrix& leftVector) override; /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) override; + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) override; void setOperationalParametersPerUnit(Real nomPower, Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index fb649d9872..013c691066 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -59,14 +59,14 @@ namespace Ph1 { /// void mnaCompPostStep(const Matrix& leftVector) final; /// - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) final {}; + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; protected: /// Transform from DQ to DP domain Matrix mDQToDPTransform = Matrix::Zero(2,2); /// Transform from DP to DQ domain Matrix mDPToDQTransform = Matrix::Zero(2,2); - + // #### Model specific variables #### /// Subransient emf const Attribute::Ptr mEdq_s; diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h index e3924e9c28..43d97ca3af 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h @@ -41,7 +41,7 @@ namespace Ph1 { Matrix parkTransform(Real theta, const Matrix& abcVector); // ### MNA Section ### - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix); + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix); void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); void mnaCompPostStep(const Matrix& leftVector); void mnaCompInitialize(Real omega, Real timeStep, Attribute::Ptr leftVector); diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h index 5aa813f781..57a73a2513 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -97,7 +97,7 @@ namespace Ph3 { // #### MNA Functions #### void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); void mnaCompPostStep(const Matrix& leftVector); - void mnaCompApplySystemMatrixStamp(Matrix& systemMatrix){}; + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix){}; }; } } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 9f53e0d14a..e274887d66 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -126,7 +126,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateConstantConductanceMatrix() { SPDLOG_LOGGER_INFO(mSLog, "\nG_const [S]: {}", Logger::matrixToString(mConductanceMatrixConst)); } -void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(Matrix& systemMatrix) { +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { updateMatrixNodeIndices(); calculateConstantConductanceMatrix(); From 5be342aa3a805a21a622b95030f4e8aa24af7f1c Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Mon, 20 Mar 2023 16:46:23 +0100 Subject: [PATCH 53/68] add dp-dq interface class and use in tpm model Signed-off-by: Jan Dinkelbach --- .../dpsim-models/DP/DP_Ph1_DPDQInterface.h | 52 +++++++++++++++++++ .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 18 ++----- dpsim-models/src/CMakeLists.txt | 1 + dpsim-models/src/DP/DP_Ph1_DPDQInterface.cpp | 38 ++++++++++++++ .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 42 ++++----------- 5 files changed, 107 insertions(+), 44 deletions(-) create mode 100644 dpsim-models/include/dpsim-models/DP/DP_Ph1_DPDQInterface.h create mode 100644 dpsim-models/src/DP/DP_Ph1_DPDQInterface.cpp 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_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index aaa8e86b52..2a6ed9d1a3 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -10,6 +10,7 @@ #include #include +#include namespace CPS { namespace DP { @@ -23,16 +24,14 @@ namespace Ph1 { 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); - /// Transform from DQ to DP domain - Matrix mDQToDPTransform = Matrix::Zero(2,2); - /// Transform from DP to DQ domain - Matrix mDPToDQTransform = Matrix::Zero(2,2); - // #### Model specific variables #### /// Transient emf const Attribute::Ptr mEdq_t; @@ -80,14 +79,7 @@ namespace Ph1 { void updateVoltage(const Matrix& leftVector) override; /// bool requiresIteration() override; - /// - void updateDQToDPTransform(); - /// - void updateDPToDQTransform(); - /// - Complex applyDQToDPTransform(const Matrix& dqMatrix); - /// - Matrix applyDPToDQTransform(const Complex& dpComplex); + /// void initializeResistanceMatrix() final {}; diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 4393012cef..486167cb85 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -46,6 +46,7 @@ list(APPEND MODELS_SOURCES 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 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_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index e274887d66..7463e1bce3 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -61,29 +61,6 @@ void DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit(Real n Td0_t, Tq0_t); }; -void DP::Ph1::SynchronGenerator4OrderTPM::updateDQToDPTransform() { - mDQToDPTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), - sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); -} - -void DP::Ph1::SynchronGenerator4OrderTPM::updateDPToDQTransform() { - mDPToDQTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime), - -sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); -} - -Complex DP::Ph1::SynchronGenerator4OrderTPM::applyDQToDPTransform(const Matrix& dqMatrix) { - Complex dpComplex; - dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); - return dpComplex; -} - -Matrix DP::Ph1::SynchronGenerator4OrderTPM::applyDPToDQTransform(const Complex& dpComplex) { - Matrix dqMatrix = Matrix::Zero(2,1); - 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; -} - void DP::Ph1::SynchronGenerator4OrderTPM::calculateStateSpaceMatrices() { mAStateSpace << -mLq / mTq0_t / mLq_t, 0, 0, -mLd / mTd0_t / mLd_t; @@ -108,6 +85,8 @@ void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { ); calculateStateSpaceMatrices(); + + mDomainInterface.setDPShiftFrequency(mBase_OmMech); } void DP::Ph1::SynchronGenerator4OrderTPM::calculateConstantConductanceMatrix() { @@ -151,10 +130,11 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { // set number of iteratios equal to zero **mNumIter = 0; - // update DQ-DP transforms according to mThetaMech - updateDQToDPTransform(); - updateDPToDQTransform(); + // 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); @@ -183,7 +163,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { 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 = applyDQToDPTransform(mEh) * mBase_V_RMS; + **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); @@ -212,14 +192,14 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; // convert corrected currents to dp domain - Complex IdpCorrectionComplex = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + 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 = applyDQToDPTransform(mEh) * mBase_V_RMS; + **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); @@ -238,7 +218,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage to dq reference frame - **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { @@ -264,5 +244,5 @@ void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompPostStep(const Matrix& leftVect (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); // convert armature current to dq reference frame - **mIdq = applyDPToDQTransform((**mIntfCurrent)(0, 0)) / mBase_I_RMS; + **mIdq = mDomainInterface.applyDPToDQTransform((**mIntfCurrent)(0, 0)) / mBase_I_RMS; } From e6fcd7a7f8b3be9abe4783c77a51323c1fbe8579 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Mon, 20 Mar 2023 18:09:04 +0100 Subject: [PATCH 54/68] use dp-dq interface class and explicit reactance matrix expressions in dp vbr models Signed-off-by: Jan Dinkelbach --- .../DP_Ph1_ReducedOrderSynchronGeneratorVBR.h | 29 ++------ ...P_Ph1_ReducedOrderSynchronGeneratorVBR.cpp | 70 +++---------------- .../DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp | 14 ++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 2 +- .../DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp | 13 ++-- .../DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp | 12 ++-- .../DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp | 12 ++-- 7 files changed, 51 insertions(+), 101 deletions(-) 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 18fa16aa05..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; /// virtual void calculateConductanceMatrix(); - /// Calculate Ka, Kb and Kvbr - void calculateAuxiliarVariables(); /// - Matrix get_parkTransformMatrix() const; + void initializeResistanceMatrix() final {}; // ### MNA Section ### void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) override; diff --git a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp index b182e7135f..b3c808f48d 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,49 +30,24 @@ 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); + mDomainInterface.setDPShiftFrequency(mBase_OmMech); + if (mModelAsCurrentSource) { // FIXME set variable matrix entries accordingly as shown below mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 0))); @@ -158,12 +127,8 @@ 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) { @@ -173,19 +138,6 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompPostStep(const Matrix& le (**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_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 7463e1bce3..f8fff3295d 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -139,7 +139,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { 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);; + 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 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_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; } From 7452b6144721542ede1c64a89d0961eb3f2d94e2 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Mon, 20 Mar 2023 18:36:56 +0100 Subject: [PATCH 55/68] use dp-dq interface class in dp pcm models Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 15 ++------ .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 18 +++------- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 33 ++++------------- .../DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 35 ++++--------------- 4 files changed, 20 insertions(+), 81 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 6abc1035cf..e2c18be9d8 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -10,6 +10,7 @@ #include #include +#include namespace CPS { namespace DP { @@ -42,14 +43,6 @@ namespace Ph1 { /// void updateVoltage(const Matrix& leftVector) override; /// - void updateDQToDPTransform(); - /// - void updateDPToDQTransform(); - /// - Complex applyDQToDPTransform(const Matrix& dqMatrix); - /// - Matrix applyDPToDQTransform(const Complex& dpComplex); - /// bool requiresIteration() override; /// void initializeResistanceMatrix() final {}; @@ -63,10 +56,8 @@ namespace Ph1 { void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; protected: - /// Transform from DQ to DP domain - Matrix mDQToDPTransform = Matrix::Zero(2,2); - /// Transform from DP to DQ domain - Matrix mDPToDQTransform = Matrix::Zero(2,2); + /// Interface used to transform between DP and DQ vars + DPDQInterface mDomainInterface; // #### Model specific variables #### /// Transient emf diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 013c691066..46276489f6 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -10,7 +10,7 @@ #include #include - +#include namespace CPS { namespace DP { namespace Ph1 { @@ -43,14 +43,6 @@ namespace Ph1 { /// bool requiresIteration() override; /// - void updateDQToDPTransform(); - /// - void updateDPToDQTransform(); - /// - Complex applyDQToDPTransform(const Matrix& dqMatrix); - /// - Matrix applyDPToDQTransform(const Complex& dpComplex); - /// void initializeResistanceMatrix() final {}; // #### MNA Functions #### @@ -61,11 +53,9 @@ namespace Ph1 { /// void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; - protected: - /// Transform from DQ to DP domain - Matrix mDQToDPTransform = Matrix::Zero(2,2); - /// Transform from DP to DQ domain - Matrix mDPToDQTransform = Matrix::Zero(2,2); + protected: + /// Interface used to transform between DP and DQ vars + DPDQInterface mDomainInterface; // #### Model specific variables #### /// Subransient emf diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 4cf3591c82..3feb88274d 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -60,29 +60,8 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { mTolerance ); mSLog->flush(); -} - -void DP::Ph1::SynchronGenerator4OrderPCM::updateDQToDPTransform() { - mDQToDPTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), - sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); -} - -void DP::Ph1::SynchronGenerator4OrderPCM::updateDPToDQTransform() { - mDPToDQTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime), - -sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); -} - -Complex DP::Ph1::SynchronGenerator4OrderPCM::applyDQToDPTransform(const Matrix& dqMatrix) { - Complex dpComplex; - dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); - return dpComplex; -} -Matrix DP::Ph1::SynchronGenerator4OrderPCM::applyDPToDQTransform(const Complex& dpComplex) { - Matrix dqMatrix = Matrix::Zero(2,1); - 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; + mDomainInterface.setDPShiftFrequency(mBase_OmMech); } void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateSpaceMatrices() { @@ -105,8 +84,8 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { mVdqPrevStep = **mVdq; // update DQ-DP transforms according to mThetaMech - updateDQToDPTransform(); - updateDPToDQTransform(); + 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); @@ -116,7 +95,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0)) / mLq_t; // convert currents to dp domain - (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -137,7 +116,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; // convert corrected currents to dp domain - (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); @@ -152,7 +131,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage into dq reference frame - **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 0d739e4b41..8b9a856012 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -72,29 +72,8 @@ void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { mTolerance ); mSLog->flush(); -} - -void DP::Ph1::SynchronGenerator6OrderPCM::updateDQToDPTransform() { - mDQToDPTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), - sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); -} - -void DP::Ph1::SynchronGenerator6OrderPCM::updateDPToDQTransform() { - mDPToDQTransform << cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime), - -sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); -} - -Complex DP::Ph1::SynchronGenerator6OrderPCM::applyDQToDPTransform(const Matrix& dqMatrix) { - Complex dpComplex; - dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); - return dpComplex; -} -Matrix DP::Ph1::SynchronGenerator6OrderPCM::applyDPToDQTransform(const Complex& dpComplex) { - Matrix dqMatrix = Matrix::Zero(2,1); - 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; + mDomainInterface.setDPShiftFrequency(mBase_OmMech); } void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateSpaceMatrices() { @@ -128,10 +107,10 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { mEdqtsPrevStep = mEdqts; // update DQ-DP transforms according to mThetaMech - updateDQToDPTransform(); - updateDPToDQTransform(); + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); - // predict emf at t=k+1 (euler) using + // 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 @@ -139,7 +118,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdqts(2,0) ) / mLq_s; // convert currents to dp domain - (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; } void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { @@ -158,7 +137,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdqts(2,0) ) / mLq_s; // convert corrected currents to dp domain - (**mIntfCurrent)(0,0) = applyDQToDPTransform(**mIdq) * mBase_I_RMS; + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; // stamp currents mnaCompApplyRightSideVectorStamp(**mRightVector); @@ -172,7 +151,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); // convert armature voltage into dq reference frame - **mVdq = applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; } bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { From 792d448db829aa115fe022c256ed6c6bce25c80e Mon Sep 17 00:00:00 2001 From: Lennart Schumacher Date: Tue, 21 Mar 2023 15:43:15 +0100 Subject: [PATCH 56/68] updated assertions for linear solver validation notebooks Signed-off-by: Lennart Schumacher --- ...LU_KLU_using_partial_refactorization.ipynb | 73 ++++++++++++++++--- 1 file changed, 62 insertions(+), 11 deletions(-) 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..b3a51171f9 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 @@ -194,27 +194,78 @@ "metadata": {}, "outputs": [], "source": [ - "tolerance = 1e-4\n", + "relative_solver_error_tolerance = 4.56e-8\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(error_1)\n", + " print(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(error_1)\n", + " print(error_2)\n", + " if error_1 > maxerrorn:\n", + " maxerrorn = error_1\n", + " if error_2 > maxerrorn:\n", + " maxerrorn = error_2" + ] + }, + { + "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)" ] } ], From e0999c455c7183d7263e05c28dd14abcb3e1c827 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Wed, 29 Mar 2023 15:21:55 +0200 Subject: [PATCH 57/68] improve prints for linear solver validation notebook Signed-off-by: Jan Dinkelbach --- ...LU_KLU_using_partial_refactorization.ipynb | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) 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 b3a51171f9..da1b632bd3 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 @@ -221,8 +221,8 @@ " \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(error_1)\n", - " print(error_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", @@ -249,12 +249,15 @@ " \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(error_1)\n", - " print(error_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" + " maxerrorn = error_2\n", + " \n", + "print('Maximum error in all values: {}'.format(maxerror))\n", + "print('Maximum norm of errors: {}'.format(maxerrorn))" ] }, { @@ -267,6 +270,14 @@ "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": { @@ -285,7 +296,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.13" }, "vscode": { "interpreter": { From 998cd456be1dcf27e941cffedebe02c43c97723b Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 30 Mar 2023 17:12:17 +0200 Subject: [PATCH 58/68] improve logging and method naming for setting reduced-order SG models as Norton or Thevenin source Signed-off-by: Jan Dinkelbach --- .../Base/Base_ReducedOrderSynchronGenerator.h | 7 ++-- .../DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 5 +++ .../DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 5 +++ .../EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h | 4 ++ .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.h | 7 ++-- .../Base_ReducedOrderSynchronGenerator.cpp | 20 +++++++-- ...P_Ph1_ReducedOrderSynchronGeneratorVBR.cpp | 8 ++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 1 - ...T_Ph3_ReducedOrderSynchronGeneratorVBR.cpp | 8 ++-- ...P_Ph1_ReducedOrderSynchronGeneratorVBR.cpp | 8 ++-- .../cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp | 2 +- .../CIM/EMT_WSCC9bus_SGReducedOrderVBR.cpp | 2 +- .../Circuits/DP_ReducedOrderSG_SMIB_Fault.cpp | 24 +++++------ .../DP_ReducedOrderSG_VBR_Load_Fault.cpp | 34 +++++++-------- ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 1 + .../DP_SMIB_ReducedOrderSG_LoadStep.cpp | 2 +- .../EMT_ReducedOrderSG_SMIB_Fault.cpp | 38 ++++++++--------- .../EMT_ReducedOrderSG_VBR_Load_Fault.cpp | 42 +++++++++---------- .../EMT_SMIB_ReducedOrderSG_LoadStep.cpp | 30 ++++++------- .../Circuits/SP_ReducedOrderSG_SMIB_Fault.cpp | 36 ++++++++-------- .../SP_ReducedOrderSG_VBR_Load_Fault.cpp | 36 ++++++++-------- .../SP_SMIB_ReducedOrderSG_LoadStep.cpp | 24 +++++------ dpsim/src/pybind/BaseComponents.cpp | 4 +- ...LU_KLU_using_partial_refactorization.ipynb | 2 +- 24 files changed, 188 insertions(+), 162 deletions(-) diff --git a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h index e89ca27b4d..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 @@ -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/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index e2c18be9d8..35781f7a39 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -47,6 +47,11 @@ namespace Ph1 { /// 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; diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 46276489f6..0bbe06b7a7 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -45,6 +45,11 @@ namespace Ph1 { /// 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; diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h index 57a73a2513..8b9344516b 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -93,6 +93,10 @@ namespace Ph3 { /// Setters /// void useVoltageForm(bool state) {mVoltageForm = state;} + /// 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); diff --git a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h index fd1b8e8d6a..91d4683527 100644 --- a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h +++ b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h @@ -75,9 +75,10 @@ namespace Ph1 { 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!"); + + /// 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!"); } }; } diff --git a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp index dd5bcb1caa..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 diff --git a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp index b3c808f48d..2a8dc40218 100644 --- a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp @@ -48,7 +48,7 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, mDomainInterface.setDPShiftFrequency(mBase_OmMech); - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { // FIXME set variable matrix entries accordingly as shown below mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 0))); } else { @@ -86,7 +86,7 @@ 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); @@ -111,7 +111,7 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(Sp } void DP::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()); @@ -131,7 +131,7 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompPostStep(const Matrix& le **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 { diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index f8fff3295d..f10b4ff699 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -18,7 +18,6 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM mSGOrder = SGOrder::SG4Order; mPhaseType = PhaseType::Single; - setVirtualNodeNumber(2); setTerminalNumber(1); /// initialize attributes 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/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/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/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 index acc3ccb791..e3788c0bbd 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -148,6 +148,7 @@ int main(int argc, char* argv[]) { 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(false); std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp index 47047226a2..94f604c0d3 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp @@ -136,7 +136,7 @@ int main(int argc, char* argv[]) { 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->setModelAsCurrentSource(true); + genDP->setModelAsNortonSource(true); //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); 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_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp index 6de4b0c0aa..57f86ab872 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp @@ -17,7 +17,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 = "EMT_SMIB_ReducedOrderSG_LoadStep"; @@ -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/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/src/pybind/BaseComponents.cpp b/dpsim/src/pybind/BaseComponents.cpp index 05f9e62a62..ab4c81550c 100644 --- a/dpsim/src/pybind/BaseComponents.cpp +++ b/dpsim/src/pybind/BaseComponents.cpp @@ -29,12 +29,12 @@ void addBaseComponents(py::module_ mBase) { .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_current_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsCurrentSource, "model_as_current_source"_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_current_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsCurrentSource, "model_as_current_source"_a); + .def("set_model_as_norton_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsNortonSource, "model_as_norton_source"_a); } 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 da1b632bd3..9da506aa2d 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 @@ -116,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", From e1d0857fb8cef90cecc741eb80e960ba0257cdab Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Thu, 30 Mar 2023 19:17:04 +0200 Subject: [PATCH 59/68] add tpm model as norton source Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 2 + ...P_Ph1_ReducedOrderSynchronGeneratorVBR.cpp | 9 ++-- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 45 +++++++++++++------ ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 2 +- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index 2a6ed9d1a3..48794edcc9 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -39,6 +39,8 @@ namespace Ph1 { 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 /// Idp at k-1 diff --git a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp index 2a8dc40218..c9b2afe711 100644 --- a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp @@ -88,16 +88,13 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { 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); @@ -112,13 +109,13 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(Sp void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { if (mModelAsNortonSource) { - // compute equivalent northon circuit in abc reference frame + // 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); } } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index f10b4ff699..1079e63930 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -109,20 +109,25 @@ void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(SparseMa calculateConstantConductanceMatrix(); - // 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)); + 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); + // 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 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); + // 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() { @@ -175,7 +180,16 @@ void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { } void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), **mEhMod); + 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() { @@ -240,7 +254,12 @@ bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompPostStep(const Matrix& leftVector) { // update armature current - (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); + 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/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp index e3788c0bbd..b969105100 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -148,7 +148,7 @@ int main(int argc, char* argv[]) { 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(false); + genDP->setModelAsNortonSource(true); std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); From 979007b94950b1bcaa52bbc080a78f06274f8114 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 31 Mar 2023 10:34:26 +0200 Subject: [PATCH 60/68] reuse of discretized state space matrices to speed up tpm model computations Signed-off-by: Jan Dinkelbach --- .../DP/DP_Ph1_SynchronGenerator4OrderTPM.h | 6 ++++++ dpsim-models/include/dpsim-models/MathUtils.h | 5 +++++ .../DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp | 5 +++-- dpsim-models/src/MathUtils.cpp | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h index 48794edcc9..7bae3d9c9d 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -43,6 +43,12 @@ namespace Ph1 { 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 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/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp index 1079e63930..d3e9166eec 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -20,7 +20,7 @@ DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM mPhaseType = PhaseType::Single; setTerminalNumber(1); - /// initialize attributes + // initialize attributes mNumIter = mAttributes->create("NIterations", 0); // model variables @@ -67,6 +67,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::calculateStateSpaceMatrices() { 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() { @@ -198,7 +199,7 @@ void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { // correct electrical vars // calculate emf at j and k+1 (trapezoidal rule) - (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mVdq, mVdqPrevStep); + (**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; 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(); From 87826eda27621556bb0e57331b71fe25cb934756 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 31 Mar 2023 11:16:00 +0200 Subject: [PATCH 61/68] reuse discretized trapezoidal state space matrices in pcm models Signed-off-by: Jan Dinkelbach --- .../dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h | 6 ++++++ .../dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h | 6 ++++++ dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp | 7 +++++-- dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp | 5 ++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h index 35781f7a39..593435d75f 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -69,6 +69,12 @@ namespace Ph1 { 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 diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h index 0bbe06b7a7..5ec200091f 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -71,6 +71,12 @@ namespace Ph1 { 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 diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp index 3feb88274d..e28b3db6b0 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -65,13 +65,16 @@ void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { } void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateSpaceMatrices() { - // Initialize matrices of state representation of predictor step + // 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() { @@ -109,7 +112,7 @@ void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { // correct electrical vars // calculate emf at j and k+1 (trapezoidal rule) - (**mEdq_t) = Math::StateSpaceTrapezoidal(mEdqtPrevStep, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mVdq, mVdqPrevStep); + (**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; diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp index 8b9a856012..d57f183cc9 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -96,6 +96,9 @@ void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateSpaceMatrices() { 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() { @@ -130,7 +133,7 @@ void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { **mNumIter = **mNumIter + 1; // correct emf at t=k+1 (trapezoidal rule) - mEdqts = Math::StateSpaceTrapezoidal(mEdqtsPrevStep, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mIdq, mIdqPrevStep); + 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; From fc0da3bf965c49cdb4326f2a7a442fb1270834a5 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Mon, 3 Apr 2023 11:15:49 +0200 Subject: [PATCH 62/68] increase resolution of step time logging Signed-off-by: Jan Dinkelbach --- dpsim/src/Simulation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpsim/src/Simulation.cpp b/dpsim/src/Simulation.cpp index 1cfa159032..a1c94d00c0 100644 --- a/dpsim/src/Simulation.cpp +++ b/dpsim/src/Simulation.cpp @@ -411,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() { From 49d46b7e39ce3dc9b0b2e6a827ff8fff643f63e5 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Tue, 30 May 2023 11:05:55 +0200 Subject: [PATCH 63/68] fix cim reader for 3rd and 6th order models in emt Signed-off-by: Jan Dinkelbach --- dpsim-models/src/CIM/Reader.cpp | 64 +++++++++++++++++---------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/dpsim-models/src/CIM/Reader.cpp b/dpsim-models/src/CIM/Reader.cpp index 1af5994962..9686736b34 100644 --- a/dpsim-models/src/CIM/Reader.cpp +++ b/dpsim-models/src/CIM/Reader.cpp @@ -520,7 +520,7 @@ 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 @@ -540,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; @@ -548,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; @@ -573,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."); @@ -581,39 +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); + 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); + 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); + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } } @@ -629,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 @@ -646,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; @@ -654,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; @@ -679,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."); @@ -687,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; } } @@ -757,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); @@ -768,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; @@ -780,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; @@ -799,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."); @@ -808,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."); @@ -816,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."); @@ -824,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; } } From 9d485fc9042b24d6e2512613e339e037b88e0529 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Fri, 9 Jun 2023 13:44:00 +0200 Subject: [PATCH 64/68] add wscc 4th order validation against psat Signed-off-by: Jan Dinkelbach --- .../cxx/CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp | 14 +- dpsim/examples/cxx/CMakeLists.txt | 1 + ...s_dyn.ipynb => DP_WSCC9bus_SGTrStab.ipynb} | 4 +- ...pynb => DP_WSCC9bus_SGTrStab_Switch.ipynb} | 8 +- ...pynb => DP_WSCC9bus_SGVoltageSource.ipynb} | 8 +- .../Notebooks/Grids/PF_CIGRE_MV_withDG.ipynb | 2 +- ...bus_SG4thOrder_Fault_PSAT_Validation.ipynb | 369 ++++++++++++++++++ ...bus_SGTrStab_Switch_PSAT_Validation.ipynb} | 14 +- ...alidate_KLU_using_different_settings.ipynb | 23 +- ...LU_KLU_using_partial_refactorization.ipynb | 7 +- ...oupled_validate_SparseLU_DenseLU_KLU.ipynb | 4 +- 11 files changed, 419 insertions(+), 35 deletions(-) rename examples/Notebooks/Grids/{WSCC_9-bus_dyn.ipynb => DP_WSCC9bus_SGTrStab.ipynb} (98%) rename examples/Notebooks/Grids/{WSCC_9-bus_dyn_switch.ipynb => DP_WSCC9bus_SGTrStab_Switch.ipynb} (97%) rename examples/Notebooks/Grids/{WSCC_9-bus.ipynb => DP_WSCC9bus_SGVoltageSource.ipynb} (96%) create mode 100644 examples/Notebooks/Grids/SP_WSCC9bus_SG4thOrder_Fault_PSAT_Validation.ipynb rename examples/Notebooks/Grids/{SP_WSCC_9-bus_dyn_switch.ipynb => SP_WSCC9bus_SGTrStab_Switch_PSAT_Validation.ipynb} (96%) 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 3c00cd75eb..37740efee3 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -204,6 +204,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/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 9da506aa2d..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", @@ -194,7 +195,7 @@ "metadata": {}, "outputs": [], "source": [ - "relative_solver_error_tolerance = 4.56e-8\n", + "relative_solver_error_tolerance = 4.31e-9\n", "close_to_zero_tolerance = 1e-12\n", "\n", "maxerror = 0.0\n", 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, From 13a7d86fcd291a8a4140c1c889ffba9958b2a965 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Fri, 23 Jun 2023 12:00:01 +0200 Subject: [PATCH 65/68] refactor EMT_Ph3_SynchronGenerator4OrderPCM Signed-off-by: Martin Moraga --- .../EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h | 97 ++++----- .../EMT_Ph3_SynchronGenerator4OrderPCM.cpp | 184 ++++++------------ ..._SMIB_ReducedOrderSGIterative_LoadStep.cpp | 33 +++- 3 files changed, 123 insertions(+), 191 deletions(-) diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h index 8b9344516b..52acec9689 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -17,71 +17,31 @@ 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 { - protected: - - - /// sim flags - bool mVoltageForm; - - // #### Model specific variables #### - /// - Matrix mVdq0_prev; - /// previous voltage behind the transient impedance (p.u.) - const Attribute::Ptr mEdq0_t; - Matrix mEdq0_t_pred; - Matrix mEdq0_t_corr; - /// derivative voltage behind the transient impedance (p.u.) - Matrix mdEdq0_t; - Matrix mdEdq0_t_corr; - /// - Real mElecTorque_corr; - /// - Real mdOmMech = 0; - Real mdOmMech_corr = 0; - Real mOmMech_pred; - Real mOmMech_corr; - /// prediction of mechanical system angle - Real mThetaMech_pred; - Real mThetaMech_corr; - /// - Real mDelta_pred; - Real mDelta_corr; - Real mdDelta = 0; - Real mdDelta_corr = 0; - - /// State Matrix x(k+1) = Ax(k) + Bu(k) + C - /// A Matrix - Matrix mA; - /// B Matrix - Matrix mB; - /// Constant Matrix - Matrix mC; 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(); + void specificInitialization() override; /// - void calculateStateMatrix(); + void calculateStateSpaceMatrices(); /// - void stepInPerUnit(); + void stepInPerUnit() override; // - void correctorStep(); + void correctorStep() override; /// - void updateVoltage(const Matrix& leftVector); + void updateVoltage(const Matrix& leftVector) override; /// - bool requiresIteration(); + bool requiresIteration() override; /// Matrix parkTransform(Real theta, const Matrix& abcVector); /// @@ -89,19 +49,48 @@ namespace Ph3 { /// void initializeResistanceMatrix() final {}; - - /// Setters - /// - void useVoltageForm(bool state) {mVoltageForm = state;} /// 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); - void mnaCompPostStep(const Matrix& leftVector); - void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix){}; + /// + 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/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp index 4f1b612fd7..e9c3cb53ef 100644 --- a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp @@ -18,18 +18,11 @@ EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM mPhaseType = PhaseType::ABC; setTerminalNumber(1); - // model flags - mVoltageForm = false; + // Initialize attributes + mNumIter = mAttributes->create("NIterations", 0); // model variables **mEdq0_t = Matrix::Zero(3,1); - mEdq0_t_pred = Matrix::Zero(3,1); - mEdq0_t_corr = Matrix::Zero(3,1); - mdEdq0_t = Matrix::Zero(3,1); - mdEdq0_t_corr = Matrix::Zero(3,1); - - // Initialize attributes - mNumIter = mAttributes->create("NIterations", 0); } EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM @@ -37,23 +30,15 @@ EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM : SynchronGenerator4OrderPCM(name, name, logLevel) { } -SimPowerComp::Ptr EMT::Ph3::SynchronGenerator4OrderPCM::clone(const String& name) { - auto copy = SynchronGenerator4OrderPCM::make(name, mLogLevel); - - return copy; -} - void EMT::Ph3::SynchronGenerator4OrderPCM::specificInitialization() { - // Initialize matrix of state representation - mA = Matrix::Zero(3,3); - mB = Matrix::Zero(3,3); - mC = Matrix::Zero(3,1); - calculateStateMatrix(); + // 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 ---" @@ -61,6 +46,7 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::specificInitialization() { "\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), @@ -71,69 +57,42 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::specificInitialization() { mSLog->flush(); } -void EMT::Ph3::SynchronGenerator4OrderPCM::calculateStateMatrix() { - if (mVoltageForm) { - Real Td_t = mTd0_t * (mLd_t / mLd); - Real Tq_t = mTq0_t * (mLq_t / mLq); - mA << -1. / Tq_t , 0.0, 0.0, - 0.0 , -1 / Td_t, 0.0, - 0.0 , 0.0, 0.0; - mB << (1. / Tq_t) * (mLq-mLq_t) / mLq, 0.0, 0.0, - 0.0, (1. / Td_t) * (mLd-mLd_t) / mLd, 0.0, - 0.0, 0.0, 0.0; - mC << 0.0, - (1. / Td_t) * (**mEf) * (mLd_t / mLd), - 0.0; - } - else { // Currents form - mA << -1./mTq0_t , 0.0, 0.0, - 0.0 , -1/mTd0_t, 0.0, - 0.0 , 0.0, 0.0; - mB << 0.0 , (1. / mTq0_t) * (mLq-mLq_t), 0.0, - (-1. / mTd0_t) * (mLd-mLd_t) , 0.0 , 0.0, - 0.0 , 0.0 , 0.0; - mC << 0.0, - (1./mTd0_t) * (**mEf), - 0.0; - } +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 iteratios equal to zero - **mNumIter = **mNumIter + 1; + // set number of iterations equal to zero + **mNumIter = 0; // Predictor step (euler) - //predict mechanical variables at t=k+1 - if (mSimTime>0.0) { - **mElecTorque = (**mVdq0)(0,0) * (**mIdq0)(0,0) + (**mVdq0)(1,0) * (**mIdq0)(1,0); - mdOmMech = 1 / (2.* mH) * (**mMechTorque - **mElecTorque); - mOmMech_pred = **mOmMech + mTimeStep * mdOmMech; - mdDelta = (mOmMech_pred - 1.) * mBase_OmMech; - mDelta_pred = **mDelta + mTimeStep * mdDelta; - mThetaMech_pred = **mThetaMech + mTimeStep * mOmMech_pred * mBase_OmMech; - } else { - mdOmMech = 0; - mOmMech_pred = **mOmMech; - mdDelta = 0; - mDelta_pred = **mDelta; - mThetaMech_pred = **mThetaMech; - } + // store values currently at t=k for later use + mEdq0tPrevStep = **mEdq0_t; + mIdq0PrevStep = **mIdq0; + mVdq0PrevStep = **mVdq0; - //predict voltage behind transient reactance - if (mVoltageForm) - mdEdq0_t = mA * **mEdq0_t + mB * **mVdq0 + mC; - else - mdEdq0_t = mA * **mEdq0_t + mB * **mIdq0 + mC; - mEdq0_t_pred = **mEdq0_t + mTimeStep * mdEdq0_t; + // predict emf at t=k+1 (euler) using + (**mEdq0_t) = Math::StateSpaceEuler(**mEdq0_t, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mVdq0); - // predict armature currents for at t=k+1 - (**mIdq0)(0,0) = (mEdq0_t_pred(1,0) - (**mVdq0)(1,0) ) / mLd_t; - (**mIdq0)(1,0) = ((**mVdq0)(0,0) - mEdq0_t_pred(0,0) ) / mLq_t; + // 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_pred, **mIdq0); + **mIntfCurrent = inverseParkTransform(**mThetaMech, **mIdq0); **mIntfCurrent = **mIntfCurrent * mBase_I; } @@ -144,41 +103,20 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matr } void EMT::Ph3::SynchronGenerator4OrderPCM::correctorStep() { - // corrector step (trapezoidal rule) - - if (**mNumIter == 1) - return; - - //predict mechanical variables - if (mSimTime>0.0) { - mElecTorque_corr = (**mVdq0)(0,0) * (**mIdq0)(0,0) + (**mVdq0)(1,0) * (**mIdq0)(1,0); - mdOmMech_corr = 1 / (2.* mH) * (**mMechTorque - mElecTorque_corr); - mOmMech_corr = **mOmMech + mTimeStep / 2. * (mdOmMech + mdOmMech_corr); - mdDelta_corr = (mOmMech_corr - 1.) * mBase_OmMech; - mDelta_corr = **mDelta + mTimeStep / 2. * (mdDelta + mdDelta_corr); - mThetaMech_corr = **mThetaMech + mTimeStep / 2. * (**mOmMech + mOmMech_corr) * mBase_OmMech; - } else { - mElecTorque_corr = **mElecTorque; - mdOmMech_corr = 0; - mOmMech_corr = **mOmMech; - mdDelta_corr = 0; - mDelta_corr = **mDelta; - mThetaMech_corr = **mThetaMech; - } + // increase number of iterations + **mNumIter = **mNumIter + 1; - //predict voltage behind transient reactance - if (mVoltageForm) - mdEdq0_t_corr = mA * **mEdq0_t + mB * **mVdq0 + mC; - else - mdEdq0_t_corr = mA * **mEdq0_t + mB * **mIdq0 + mC; - mEdq0_t_corr = **mEdq0_t + mTimeStep / 2 * (mdEdq0_t + mdEdq0_t_corr); + // correct electrical vars + // calculate emf at j and k+1 (trapezoidal rule) + (**mEdq0_t) = Math::applyStateSpaceTrapezoidalMatrices(mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal * **mEf, mEdq0tPrevStep, **mVdq0, mVdq0PrevStep); - // armature currents for at t=k+1 - (**mIdq0)(0,0) = (mEdq0_t_corr(1,0) - (**mVdq0)(1,0) ) / mLd_t; - (**mIdq0)(1,0) = ((**mVdq0)(0,0) - mEdq0_t_corr(0,0) ) / mLq_t; + + // 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_corr, **mIdq0); + **mIntfCurrent = inverseParkTransform(**mThetaMech, **mIdq0); **mIntfCurrent = **mIntfCurrent * mBase_I; // stamp currents @@ -186,45 +124,37 @@ void EMT::Ph3::SynchronGenerator4OrderPCM::correctorStep() { } void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { - mVdq0_prev = **mVdq0; + // 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 - if (**mNumIter == 0) { - **mVdq0 = parkTransform(mThetaMech_pred, **mIntfVoltage); - } else { - **mVdq0 = parkTransform(mThetaMech_corr, **mIntfVoltage); - } + **mVdq0 = parkTransform(**mThetaMech, **mIntfVoltage); **mVdq0 = **mVdq0 / mBase_V; } bool EMT::Ph3::SynchronGenerator4OrderPCM::requiresIteration() { - if (**mNumIter == 0) { - // if no corrector step has been performed yet - **mNumIter = 1; + 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; - } - - Matrix voltageDifference = **mVdq0 - mVdq0_prev; - if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { - if (**mNumIter == mMaxIter) { - return false; - } else { + } 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; + else + return false; + } } void EMT::Ph3::SynchronGenerator4OrderPCM::mnaCompPostStep(const Matrix& leftVector) { - // update variables - **mEdq0_t = mEdq0_t_corr; - **mOmMech = mOmMech_corr; - **mThetaMech = mThetaMech_corr; - **mDelta = mDelta_corr; } Matrix EMT::Ph3::SynchronGenerator4OrderPCM::parkTransform(Real theta, const Matrix& abcVector) { diff --git a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp index abd63f67e0..d1f97617b9 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -23,25 +23,39 @@ int main(int argc, char* argv[]) { Real finalTime = 35; // Default configuration - String sgType = defaultConfig.sgType; 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) { - timeStep = args.timeStep; - finalTime = args.duration; - if (args.name != "dpsim") - simName = args.name; - if (args.options.find("sgType") != args.options.end()) - sgType = args.getOptionString("sgType"); + 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; @@ -135,8 +149,8 @@ int main(int argc, char* argv[]) { 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(defaultConfig.maxIter); - genEMT->setTolerance(defaultConfig.tolerance); + genEMT->setMaxIterations(maxIter); + genEMT->setTolerance(tolerance); //Grid bus as Slack auto extnetEMT = EMT::Ph3::NetworkInjection::make("Slack", logLevel); @@ -181,7 +195,6 @@ int main(int argc, char* argv[]) { simEMT.setDomain(Domain::EMT); simEMT.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); simEMT.addLogger(logger); - //simEMT.doSystemMatrixRecomputation(true); // Events simEMT.addEvent(loadStepEvent); From 84dc8e490b72fdb249aff5a5623c8b7f9748f963 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Fri, 23 Jun 2023 12:08:13 +0200 Subject: [PATCH 66/68] remove unused methods and variables of MNASyncGenInterface Signed-off-by: Martin Moraga --- .../dpsim-models/Solver/MNASyncGenInterface.h | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h index 0e23dc2e78..70c9e79c33 100644 --- a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -16,25 +16,6 @@ namespace CPS { /// Interface to be used by synchronous generators class MNASyncGenInterface { protected: - /// Matrix used when numerical method of predictor step = Euler - /// State Matrix backward euler: Edq(k) = mA_euler * Edq(k) + mB_euler * Idq + mC_euler * Ef - Matrix mA_euler; - Matrix mB_euler; - Matrix mC_euler; - - /// Matrixes used when numerical method of corrector step = trapezoidal rule - /// State Matrix trapezoidal rule (corrector step): x(k+1) = mAPrevIter * Edq(k-1) + mA_corr * Edq_corr(k) + B_corr * Idq_corr(k) + mC_corr * Ef - Matrix mA_prev; - Matrix mA_corr; - Matrix mB_corr; - Matrix mC_corr; - - /// Transformation matrix dp->dq - MatrixComp mDpToDq; - - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - /// Attribute::Ptr mNumIter; /// @@ -62,29 +43,6 @@ namespace CPS { protected: /// Constructor - MNASyncGenInterface() { - // Vector to convert 1phase to 3phase - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - } - - /// - Matrix parkTransform(Real theta, const Matrix& abcVector) { - Matrix dq0Vector(3, 1); - Matrix dqVector(2, 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; - dqVector << dq0Vector(0,0), dq0Vector(1,0); - - return dqVector; - } - + MNASyncGenInterface() { } }; } From c7f7f2f4cab0f9258b1be4be257cba355ba53308 Mon Sep 17 00:00:00 2001 From: Martin Moraga Date: Fri, 23 Jun 2023 12:36:38 +0200 Subject: [PATCH 67/68] remove unused variable in MNASolverDirect Signed-off-by: Martin Moraga --- dpsim/include/dpsim/MNASolverDirect.h | 1 - 1 file changed, 1 deletion(-) diff --git a/dpsim/include/dpsim/MNASolverDirect.h b/dpsim/include/dpsim/MNASolverDirect.h index 8188577845..1df04ac95a 100644 --- a/dpsim/include/dpsim/MNASolverDirect.h +++ b/dpsim/include/dpsim/MNASolverDirect.h @@ -102,7 +102,6 @@ namespace DPsim { using MnaSolver::mSolveTimes; using MnaSolver::mRecomputationTimes; using MnaSolver::mListVariableSystemMatrixEntries; - using Solver::mMaxIterations; // #### General /// Create system matrix From 8cab9eebe7feb9d00b3e8b19270795e075cacbe8 Mon Sep 17 00:00:00 2001 From: Jan Dinkelbach Date: Tue, 4 Jul 2023 11:22:11 +0200 Subject: [PATCH 68/68] remove deprecate dcim model in sp Signed-off-by: Jan Dinkelbach --- .../include/dpsim-models/Components.h | 1 - .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.h | 86 --------- dpsim-models/src/CMakeLists.txt | 1 - .../SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp | 122 ------------- dpsim/examples/cxx/CMakeLists.txt | 1 - .../SP_SynGen4OrderDCIM_SMIB_Fault.cpp | 170 ------------------ 6 files changed, 381 deletions(-) delete mode 100644 dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h delete mode 100644 dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp delete mode 100644 dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 27cc553d62..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 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 91d4683527..0000000000 --- a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h +++ /dev/null @@ -1,86 +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 - /// - Real mAd; - /// - Real mBd; - /// - Real mAq; - /// - Real mBq; - /// - Real mCq; - /// - Matrix mA; - /// - Matrix mA_inv; - /// - Matrix mB; - /// - Matrix mC; - /// - - /// Park Transformation - /// - Matrix mDqToComplexA; - /// - Matrix mComplexAToDq; - /// - Matrix get_DqToComplexATransformMatrix(); - - // #### General Functions #### - /// Specific component initialization - void specificInitialization() final; - /// - void initializeResistanceMatrix() final {}; - /// - void stepInPerUnit() final; - /// - void calculateStateMatrix(); - - // ### 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); - - /// 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!"); - } - }; -} -} -} diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 486167cb85..30639f8dad 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -118,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/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp deleted file mode 100644 index 88d35ea120..0000000000 --- a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ /dev/null @@ -1,122 +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; - - // - mAd = mTimeStep * (mLq - mLq_t) / (2 * mTq0_t + mTimeStep); - mBd = (2 * mTq0_t - mTimeStep) / (2 * mTq0_t + mTimeStep); - - mAq = - mTimeStep * (mLd - mLd_t) / (2 * mTd0_t + mTimeStep); - mBq = (2 * mTd0_t - mTimeStep) / (2 * mTd0_t + mTimeStep); - mCq = 2 * mTimeStep / (2 * mTd0_t + mTimeStep); - - // 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() { - if (mSimTime>0.0) { - // calculate mechanical variables at t=k+1 with forward euler - **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); - **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (**mMechTorque - **mElecTorque)); - **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); - **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; - } - - // 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/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 37740efee3..504490cc76 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -74,7 +74,6 @@ 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 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 50656cc91c..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 = 10e-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<10e-6) - logDownSampling = floor(10e-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("Vdq0")); - 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(); -}