DPsim
DP_Ph3_SynchronGeneratorDQ.cpp
1 /* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2  * EONERC, RWTH Aachen University
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7  *********************************************************************************/
8 
9 #include <dpsim-models/DP/DP_Ph3_SynchronGeneratorDQ.h>
10 
11 //Added for testing/printing purposes:
12 #include <fstream>
13 #include <iostream>
14 
15 using namespace CPS;
16 using namespace std;
17 
19  Logger::Level logLevel)
20  : MNASimPowerComp<Complex>(uid, name, true, true, logLevel),
21  Base::SynchronGenerator(mAttributes) {
22  mPhaseType = PhaseType::ABC;
23  setTerminalNumber(1);
24  **mIntfVoltage = MatrixComp::Zero(3, 1);
25  **mIntfCurrent = MatrixComp::Zero(3, 1);
26 }
27 
29  Logger::Level logLevel)
30  : SynchronGeneratorDQ(name, name, logLevel) {}
31 
32 DP::Ph3::SynchronGeneratorDQ::~SynchronGeneratorDQ() {}
33 
35  Real nomPower, Real nomVolt, Real nomFreq, Int poleNumber, Real nomFieldCur,
36  Real Rs, Real Ll, Real Lmd, Real Lmq, Real Rfd, Real Llfd, Real Rkd,
37  Real Llkd, Real Rkq1, Real Llkq1, Real Rkq2, Real Llkq2, Real inertia,
38  Real initActivePower, Real initReactivePower, Real initTerminalVolt,
39  Real initVoltAngle, Real initMechPower) {
40 
42  nomPower, nomVolt, nomFreq, nomFieldCur, poleNumber, Rs, Ll, Lmd, Lmq,
43  Rfd, Llfd, Rkd, Llkd, Rkq1, Llkq1, Rkq2, Llkq2, inertia);
44  Base::SynchronGenerator::setInitialValues(initActivePower, initReactivePower,
45  initTerminalVolt, initVoltAngle,
46  initMechPower);
47 }
48 
49 void DP::Ph3::SynchronGeneratorDQ::initialize(Matrix frequencies) {
51  mSystemOmega = frequencies(0, 0);
52 
53  // #### Compensation ####
54  mCompensationOn = false;
55  mCompensationCurrent = MatrixComp::Zero(3, 1);
56  // Calculate real compensation resistance from per unit
57  mRcomp = mRcomp * mBase_Z;
58 
59  // #### General setup ####
60  // Create matrices for state space representation
61  calcStateSpaceMatrixDQ();
62 
63  // steady state per unit initial value
64  initPerUnitStates();
65 
66  mVdq0 = Matrix::Zero(3, 1);
67  mIdq0 = Matrix::Zero(3, 1);
68  if (mNumDampingWindings == 2) {
69  mVdq0 << mVsr(0, 0), mVsr(3, 0), mVsr(6, 0);
70  mIdq0 << mIsr(0, 0), mIsr(3, 0), mIsr(6, 0);
71  } else {
72  mVdq0 << mVsr(0, 0), mVsr(3, 0), mVsr(5, 0);
73  mIdq0 << mIsr(0, 0), mIsr(3, 0), mIsr(5, 0);
74  }
75 
76  **mIntfVoltage = mBase_V * dq0ToAbcTransform(mThetaMech, mVdq0);
77  **mIntfCurrent = mBase_I * dq0ToAbcTransform(mThetaMech, mIdq0);
78 }
79 
80 void DP::Ph3::SynchronGeneratorDQ::mnaCompApplySystemMatrixStamp(
81  SparseMatrixRow &systemMatrix) {
82  if (!mCompensationOn)
83  return;
84 
85  Real conductance = 1. / mRcomp;
86 
87  if (terminalNotGrounded(0)) {
88  Math::addToMatrixElement(systemMatrix, matrixNodeIndices(0)[0],
89  matrixNodeIndices(0)[0], Complex(conductance, 0));
90  Math::addToMatrixElement(systemMatrix, matrixNodeIndices(0)[1],
91  matrixNodeIndices(0)[1], Complex(conductance, 0));
92  Math::addToMatrixElement(systemMatrix, matrixNodeIndices(0)[2],
93  matrixNodeIndices(0)[2], Complex(conductance, 0));
94  SPDLOG_LOGGER_INFO(mSLog, "Add {} to {}, {}", conductance,
95  matrixNodeIndices(0)[0], matrixNodeIndices(0)[0]);
96  SPDLOG_LOGGER_INFO(mSLog, "Add {} to {}, {}", conductance,
97  matrixNodeIndices(0)[1], matrixNodeIndices(0)[1]);
98  SPDLOG_LOGGER_INFO(mSLog, "Add {} to {}, {}", conductance,
99  matrixNodeIndices(0)[2], matrixNodeIndices(0)[2]);
100  }
101 }
102 
103 void DP::Ph3::SynchronGeneratorDQ::mnaCompApplyRightSideVectorStamp(
104  Matrix &rightVector) {
105  if (mCompensationOn)
106  mCompensationCurrent = **mIntfVoltage / mRcomp;
107 
108  // If the interface current is positive, it is flowing out of the connected node and into ground.
109  // Therefore, the generator is interfaced as a consumer but since the currents are reversed the equations
110  // are in generator mode.
111  if (terminalNotGrounded(0)) {
112  Math::setVectorElement(rightVector, matrixNodeIndex(0, 0),
113  -(**mIntfCurrent)(0, 0) +
114  mCompensationCurrent(0, 0));
115  Math::setVectorElement(rightVector, matrixNodeIndex(0, 1),
116  -(**mIntfCurrent)(1, 0) +
117  mCompensationCurrent(1, 0));
118  Math::setVectorElement(rightVector, matrixNodeIndex(0, 2),
119  -(**mIntfCurrent)(2, 0) +
120  mCompensationCurrent(2, 0));
121  }
122 }
123 
125  const Matrix &leftVector) {
126  (**mIntfVoltage)(0, 0) =
127  Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0));
128  (**mIntfVoltage)(1, 0) =
129  Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 1));
130  (**mIntfVoltage)(2, 0) =
131  Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 2));
132 }
133 
135  AttributeBase::List &prevStepDependencies,
136  AttributeBase::List &attributeDependencies,
137  AttributeBase::List &modifiedAttributes,
138  Attribute<Matrix>::Ptr &leftVector) {
139  attributeDependencies.push_back(leftVector);
140  modifiedAttributes.push_back(mIntfVoltage);
141 }
142 
143 void DP::Ph3::SynchronGeneratorDQ::mnaCompPostStep(
144  Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
145  mnaCompUpdateVoltage(**leftVector);
146 }
147 
148 Real DP::Ph3::SynchronGeneratorDQ::electricalTorque() const {
149  return **mElecTorque * mBase_T;
150 }
151 
152 Real DP::Ph3::SynchronGeneratorDQ::rotationalSpeed() const {
153  return **mOmMech * mBase_OmMech;
154 }
155 
156 Real DP::Ph3::SynchronGeneratorDQ::rotorPosition() const { return mThetaMech; }
157 
159  MatrixComp &abcVector) {
160  // Balanced case because we do not return the zero sequence component
161  Complex alpha(cos(2. / 3. * PI), sin(2. / 3. * PI));
162  Complex thetaCompInv(cos(-theta), sin(-theta));
163 
164  MatrixComp abcToPnz(3, 3);
165  abcToPnz << 1, 1, 1, 1, alpha, pow(alpha, 2), 1, pow(alpha, 2), alpha;
166  abcToPnz = (1. / 3.) * abcToPnz;
167 
168  MatrixComp pnzVector(3, 1);
169  pnzVector = abcToPnz * abcVector * thetaCompInv;
170 
171  Matrix dq0Vector(3, 1);
172  dq0Vector << pnzVector(1, 0).real(), pnzVector(1, 0).imag(), 0;
173 
174  return dq0Vector;
175 }
176 
178  Matrix &dq0) {
179  // Balanced case because we do not consider the zero sequence component
180  Complex alpha(cos(2. / 3. * PI), sin(2. / 3. * PI));
181  Complex thetaComp(cos(theta), sin(theta));
182  // Matrix to transform from symmetrical components to ABC
183  MatrixComp pnzToAbc(3, 3);
184  pnzToAbc << 1, 1, 1, 1, pow(alpha, 2), alpha, 1, alpha, pow(alpha, 2);
185  // Symmetrical components vector
186  MatrixComp pnzVector(3, 1);
187  // Picking only d and q for positive sequence component
188  pnzVector << 0, Complex(dq0(0, 0), dq0(1, 0)), 0;
189  // ABC vector
190  MatrixComp abcCompVector(3, 1);
191  abcCompVector = pnzToAbc * pnzVector * thetaComp;
192 
193  return abcCompVector;
194 }
195 
197  /*
198  // Calculation of rotational speed with euler
199  if (mHasTurbineGovernor == true)
200  mMechTorque = mTurbineGovernor.step(mOmMech, 1, 300e6 / 555e6, mTimeStep);
201 
202  mElecTorque = (mPsid*mIq - mPsiq*mId);
203  mOmMech = mOmMech + mTimeStep * (1 / (2 * mH) * (mMechTorque - mElecTorque));
204 
205  //Calculation of flux
206  Matrix A = (mResistanceMat*mReactanceMat - mOmMech*mOmegaFluxMat);
207  Matrix B = Matrix::Identity(7, 7);
208 
209  if (numMethod == NumericalMethod::Trapezoidal_flux)
210  Fluxes = Math::StateSpaceTrapezoidal(Fluxes, A, B, mTimeStep*mBase_OmElec, dqVoltages);
211  else
212  Fluxes = Math::StateSpaceEuler(Fluxes, A, B, mTimeStep*mBase_OmElec, dqVoltages);
213 
214  // Calculation of currents based on inverse of inductance matrix
215  mId = ((mLlfd*mLlkd + mLmd*(mLlfd + mLlkd))*mPsid - mLmd*mLlkd*mPsifd - mLlfd*mLmd*mPsikd) / detLd;
216  mIfd = (mLlkd*mLmd*mPsid - (mLl*mLlkd + mLmd*(mLl + mLlkd))*mPsifd + mLmd*mLl*mPsikd) / detLd;
217  mIkd = (mLmd*mLlfd*mPsid + mLmd*mLl*mPsifd - (mLmd*(mLlfd + mLl) + mLl*mLlfd)*mPsikd) / detLd;
218  mIq = ((mLlkq1*mLlkq2 + mLmq*(mLlkq1 + mLlkq2))*mPsiq - mLmq*mLlkq2*mPsikq1 - mLmq*mLlkq1*mPsikq2) / detLq;
219  mIkq1 = (mLmq*mLlkq2*mPsiq - (mLmq*(mLlkq2 + mLl) + mLl*mLlkq2)*mPsikq1 + mLmq*mLl*mPsikq2) / detLq;
220  mIkq2 = (mLmq*mLlkq1*mPsiq + mLmq*mLl*mPsikq1 - (mLmq*(mLlkq1 + mLl) + mLl*mLlkq1)*mPsikq2) / detLq;
221  mI0 = -mPsi0 / mLl;
222  */
223 }
224 
225 void DP::Ph3::SynchronGeneratorDQ::trapezoidalCurrentStates() {
226  /*
227  Matrix A = (mReactanceMat*mResistanceMat);
228  Matrix B = mReactanceMat;
229  Matrix C = Matrix::Zero(7, 1);
230  C(0, 0) = -mOmMech*mPsid;
231  C(1, 0) = mOmMech*mPsiq;
232  C = mReactanceMat*C;
233 
234  dqCurrents = Math::StateSpaceTrapezoidal(dqCurrents, A, B, C, mTimeStep*mOmMech, dqVoltages);
235 
236  //Calculation of currents based on inverse of inductance matrix
237  mPsiq = -(mLl + mLmq)*mIq + mLmq*mIkq1 + mLmq*mIkq2;
238  mPsid = -(mLl + mLmd)*mId + mLmd*mIfd + mLmd*mIkd;
239  mPsi0 = -mLl*mI0;
240  mPsikq1 = -mLmq*mIq + (mLlkq1 + mLmq)*mIkq1 + mLmq*mIkq2;
241  mPsikq2 = -mLmq*mIq + mLmq*mIkq1 + (mLlkq2 + mLmq)*mIkq2;
242  mPsifd = -mLmd*mId + (mLlfd + mLmd)*mIfd + mLmd*mIkd;
243  mPsikd = -mLmd*mId + mLmd*mIfd + (mLlkd + mLmd)*mIkd;
244  */
245 }
void setBaseAndFundamentalPerUnitParameters(Real nomPower, Real nomVolt, Real nomFreq, Real nomFieldCur, Int poleNumber, Real Rs, Real Ll, Real Lmd, Real Lmq, Real Rfd, Real Llfd, Real Rkd, Real Llkd, Real Rkq1, Real Llkq1, Real Rkq2, Real Llkq2, Real inertia)
Initializes the base and fundamental machine parameters in per unit.
Synchronous generator model in dq-reference frame.
void initialize(Real omega, Real timeStep)
SynchronGeneratorDQ(String name, String uid, Logger::Level logLevel=Logger::Level::off)
Defines UID, name and logging level.
void trapezoidalFluxStates()
calculate flux states using trapezoidal rule - depcrecated
virtual void mnaCompUpdateVoltage(const Matrix &leftVector) override
Retrieves calculated voltage from simulation for next step.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
Add MNA post step dependencies.
Matrix abcToDq0Transform(Real theta, MatrixComp &abc)
Park transform as described in Krause.
void setParametersFundamentalPerUnit(Real nomPower, Real nomVolt, Real nomFreq, Int poleNumber, Real nomFieldCur, Real Rs, Real Ll, Real Lmd, Real Lmq, Real Rfd, Real Llfd, Real Rkd, Real Llkd, Real Rkq1, Real Llkq1, Real Rkq2, Real Llkq2, Real inertia, Real initActivePower, Real initReactivePower, Real initTerminalVolt, Real initVoltAngle, Real initMechPower)
MatrixComp dq0ToAbcTransform(Real theta, Matrix &dq0)
Inverse Park transform as described in Krause.
Base class for all MNA components that are transmitting power.
virtual void initialize(Matrix frequencies)
Initialize components with correct network frequencies.
const Attribute< MatrixVar< Complex > >::Ptr mIntfCurrent
Current through component.
Definition: SimPowerComp.h:47
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
Voltage between terminals.
Definition: SimPowerComp.h:45