DPsim
EMT_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/EMT/EMT_Ph3_SynchronGeneratorDQ.h>
10 
11 using namespace CPS;
12 
13 // !!! TODO: Adaptions to use in EMT_Ph3 models phase-to-ground peak variables
14 // !!! with initialization from phase-to-phase RMS variables
15 
17  Logger::Level logLevel)
18  : MNASimPowerComp<Real>(uid, name, true, true, logLevel),
19  Base::SynchronGenerator(mAttributes) {
20  mPhaseType = PhaseType::ABC;
21  setTerminalNumber(1);
22  **mIntfVoltage = Matrix::Zero(3, 1);
23  **mIntfCurrent = Matrix::Zero(3, 1);
24 }
25 
27  Logger::Level logLevel)
28  : SynchronGeneratorDQ(name, name, logLevel) {}
29 
30 EMT::Ph3::SynchronGeneratorDQ::~SynchronGeneratorDQ() {}
31 
32 Real EMT::Ph3::SynchronGeneratorDQ::electricalTorque() const {
33  return **mElecTorque * mBase_T;
34 }
35 
36 Real EMT::Ph3::SynchronGeneratorDQ::rotationalSpeed() const {
37  return **mOmMech * mBase_OmMech;
38 }
39 
40 Real EMT::Ph3::SynchronGeneratorDQ::rotorPosition() const { return mThetaMech; }
41 
43  Real nomPower, Real nomVolt, Real nomFreq, Int poleNumber, Real nomFieldCur,
44  Real Rs, Real Ll, Real Lmd, Real Lmq, Real Rfd, Real Llfd, Real Rkd,
45  Real Llkd, Real Rkq1, Real Llkq1, Real Rkq2, Real Llkq2, Real inertia,
46  Real initActivePower, Real initReactivePower, Real initTerminalVolt,
47  Real initVoltAngle, Real initMechPower) {
48 
50  nomPower, nomVolt, nomFreq, nomFieldCur, poleNumber, Rs, Ll, Lmd, Lmq,
51  Rfd, Llfd, Rkd, Llkd, Rkq1, Llkq1, Rkq2, Llkq2, inertia);
52 
53  SPDLOG_LOGGER_INFO(mSLog,
54  "Set base and fundamental parameters in per unit: \n"
55  "nomPower: {:e}\nnomVolt: {:e}\nnomFreq: "
56  "{:e}\npoleNumber: {:d}\nnomFieldCur: {:e}\n"
57  "Rs: {:e}\nLl: {:e}\nLmd: {:e}\nLmq: {:e}\nRfd: "
58  "{:e}\nLlfd: {:e}\nRkd: {:e}\n"
59  "Llkd: {:e}\nRkq1: {:e}\nLlkq1: {:e}\nRkq2: {:e}\nLlkq2: "
60  "{:e}\ninertia: {:e}",
61  nomPower, nomVolt, nomFreq, poleNumber, nomFieldCur, Rs,
62  Ll, Lmd, Lmq, Rfd, Llfd, Rkd, Llkd, Rkq1, Llkq1, Rkq2,
63  Llkq2, inertia);
64 
65  Base::SynchronGenerator::setInitialValues(initActivePower, initReactivePower,
66  initTerminalVolt, initVoltAngle,
67  initMechPower);
68 
69  SPDLOG_LOGGER_INFO(
70  mSLog,
71  "Set initial values: \n"
72  "initActivePower: {:e} [W]\n"
73  "initReactivePower: {:e} [VAr]\n"
74  "initTerminalVolt: {:e} [V] (initTerminalVolt: {:e} [pu])\n"
75  "initVoltAngle: {:e} [deg] \n"
76  "initMechPower: {:e} [W]",
77  mInitElecPower.real(), mInitElecPower.imag(), mInitTerminalVoltage,
78  mInitTerminalVoltage / mBase_V, CPS::Math::radtoDeg(mInitVoltAngle),
79  mInitMechPower);
80 }
81 
82 void EMT::Ph3::SynchronGeneratorDQ::setParametersOperationalPerUnit(
83  Real nomPower, Real nomVolt, Real nomFreq, Int poleNumber, Real nomFieldCur,
84  Real Rs, Real Ld, Real Lq, Real Ld_t, Real Lq_t, Real Ld_s, Real Lq_s,
85  Real Ll, Real Td0_t, Real Tq0_t, Real Td0_s, Real Tq0_s, Real inertia) {
86 
87  setBaseParameters(nomPower, nomVolt, nomFreq, nomFieldCur);
88  SPDLOG_LOGGER_INFO(
89  mSLog,
90  "Set base parameters: \n"
91  "nomPower: {:e}\nnomVolt: {:e}\nnomFreq: {:e}\n nomFieldCur: {:e}\n",
92  nomPower, nomVolt, nomFreq, nomFieldCur);
93 
94  setOperationalPerUnitParameters(poleNumber, inertia, Rs, Ld, Lq, Ll, Ld_t,
95  Lq_t, Ld_s, Lq_s, Td0_t, Tq0_t, Td0_s, Tq0_s);
96  SPDLOG_LOGGER_INFO(mSLog,
97  "Set operational parameters in per unit: \n"
98  "poleNumber: {:d}\ninertia: {:e}\n"
99  "Rs: {:e}\nLd: {:e}\nLq: {:e}\nLl: {:e}\n"
100  "Ld_t: {:e}\nLq_t: {:e}\nLd_s: {:e}\nLq_s: {:e}\n"
101  "Td0_t: {:e}\nTq0_t: {:e}\nTd0_s: {:e}\nTq0_s: {:e}\n",
102  poleNumber, inertia, Rs, Ld, Lq, Ll, Ld_t, Lq_t, Ld_s,
103  Lq_s, Td0_t, Tq0_t, Td0_s, Tq0_s);
104 
105  Base::SynchronGenerator::calculateFundamentalFromOperationalParameters();
106  SPDLOG_LOGGER_INFO(
107  mSLog,
108  "Set fundamental parameters in per unit: \n"
109  "Rs: {:e}\nLl: {:e}\nLmd: {:e}\nLmq: {:e}\nRfd: {:e}\nLlfd: {:e}\nRkd: "
110  "{:e}\n"
111  "Llkd: {:e}\nRkq1: {:e}\nLlkq1: {:e}\nRkq2: {:e}\nLlkq2: {:e}\n",
112  **mRs, **mLl, mLmd, mLmq, mRfd, mLlfd, mRkd, mLlkd, mRkq1, mLlkq1, mRkq2,
113  mLlkq2);
114 }
115 
117  Real initReactivePower,
118  Real initTerminalVolt,
119  Real initVoltAngle,
120  Real initMechPower) {
121 
122  Base::SynchronGenerator::setInitialValues(initActivePower, initReactivePower,
123  initTerminalVolt, initVoltAngle,
124  initMechPower);
125 
126  SPDLOG_LOGGER_INFO(
127  mSLog,
128  "Set initial values: \n"
129  "initActivePower: {:e} [W]\n"
130  "initReactivePower: {:e} [VAr]\n"
131  "initTerminalVolt: {:e} [V] (initTerminalVolt: {:e} [pu])\n"
132  "initVoltAngle: {:e} [deg] \n"
133  "initMechPower: {:e} [W]",
134  mInitElecPower.real(), mInitElecPower.imag(), mInitTerminalVoltage,
135  mInitTerminalVoltage / mBase_V, CPS::Math::radtoDeg(mInitVoltAngle),
136  mInitMechPower);
137 }
138 
140 
141  SPDLOG_LOGGER_INFO(mSLog,
142  "Apply operational parameters in per unit: \n"
143  "poleNumber: {:d}\ninertia: {:e}\n"
144  "Rs: {:e}\nLd: {:e}\nLq: {:e}\nLl: {:e}\n"
145  "Ld_t: {:e}\nLq_t: {:e}\nLd_s: {:e}\nLq_s: {:e}\n"
146  "Td0_t: {:e}\nTq0_t: {:e}\nTd0_s: {:e}\nTq0_s: {:e}\n",
147  mPoleNumber, **mInertia, **mRs, **mLd, **mLq, **mLl,
148  **mLd_t, **mLq_t, **mLd_s, **mLq_s, **mTd0_t, **mTq0_t,
149  **mTd0_s, **mTq0_s);
150 
151  Base::SynchronGenerator::calculateFundamentalFromOperationalParameters();
152  SPDLOG_LOGGER_INFO(
153  mSLog,
154  "Updated fundamental parameters in per unit: \n"
155  "Rs: {:e}\nLl: {:e}\nLmd: {:e}\nLmq: {:e}\nRfd: {:e}\nLlfd: {:e}\nRkd: "
156  "{:e}\n"
157  "Llkd: {:e}\nRkq1: {:e}\nLlkq1: {:e}\nRkq2: {:e}\nLlkq2: {:e}\n",
158  **mRs, **mLl, mLmd, mLmq, mRfd, mLlfd, mRkd, mLlkd, mRkq1, mLlkq1, mRkq2,
159  mLlkq2);
160 }
161 
163  Real frequency) {
164  if (!mInitialValuesSet) {
165  SPDLOG_LOGGER_INFO(mSLog, "--- Initialization from powerflow ---");
166 
167  // terminal powers in consumer system -> convert to generator system
168  Real activePower = -terminal(0)->singlePower().real();
169  Real reactivePower = -terminal(0)->singlePower().imag();
170 
171  // voltage magnitude in phase-to-phase RMS -> convert to phase-to-ground peak expected by setInitialValues
172  Real voltMagnitude = RMS3PH_TO_PEAK1PH * Math::abs(initialSingleVoltage(0));
173 
174  this->setInitialValues(activePower, reactivePower, voltMagnitude,
175  Math::phase(initialSingleVoltage(0)), activePower);
176 
177  SPDLOG_LOGGER_INFO(mSLog,
178  "\nTerminal 0 voltage: {:s}"
179  "\nTerminal 0 power: {:s}"
180  "\n--- Initialization from powerflow finished ---",
181  Logger::phasorToString(initialSingleVoltage(0)),
182  Logger::complexToString(terminal(0)->singlePower()));
183  } else {
184  SPDLOG_LOGGER_INFO(mSLog, "Initial values already set, skipping "
185  "initializeFromNodesAndTerminals.");
186  }
187  mSLog->flush();
188 }
189 
191  SimPowerComp<Real>::initialize(frequencies);
192 }
193 
195  // #### Compensation ####
196  mCompensationOn = false;
197  mCompensationCurrent = Matrix::Zero(3, 1);
198  // Calculate real compensation resistance from per unit
199  mRcomp = mRcomp * mBase_Z;
200 
201  // #### General setup ####
202  // Create matrices for state space representation
203  calcStateSpaceMatrixDQ();
204 
205  // steady state per unit initial value
206  initPerUnitStates();
207 
208  mVdq0 = Matrix::Zero(3, 1);
209  mIdq0 = Matrix::Zero(3, 1);
210  if (mNumDampingWindings == 2) {
211  mVdq0 << mVsr(0, 0), mVsr(3, 0), mVsr(6, 0);
212  mIdq0 << mIsr(0, 0), mIsr(3, 0), mIsr(6, 0);
213  } else {
214  mVdq0 << mVsr(0, 0), mVsr(3, 0), mVsr(5, 0);
215  mIdq0 << mIsr(0, 0), mIsr(3, 0), mIsr(5, 0);
216  }
217 
218  **mIntfVoltage = mBase_V * dq0ToAbcTransform(mThetaMech, mVdq0);
219  **mIntfCurrent = mBase_I * dq0ToAbcTransform(mThetaMech, mIdq0);
220 }
221 
222 void EMT::Ph3::SynchronGeneratorDQ::mnaCompApplySystemMatrixStamp(
223  SparseMatrixRow &systemMatrix) {
224  if (!mCompensationOn)
225  return;
226 
227  Real conductance = 1. / mRcomp;
228 
229  if (terminalNotGrounded(0)) {
230  Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0),
231  matrixNodeIndex(0, 0), conductance);
232  Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 1),
233  matrixNodeIndex(0, 1), conductance);
234  Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 2),
235  matrixNodeIndex(0, 2), conductance);
236  SPDLOG_LOGGER_INFO(mSLog, "Add {} to {}, {}", conductance,
237  matrixNodeIndex(0, 0), matrixNodeIndex(0, 0));
238  SPDLOG_LOGGER_INFO(mSLog, "Add {} to {}, {}", conductance,
239  matrixNodeIndex(0, 1), matrixNodeIndex(0, 1));
240  SPDLOG_LOGGER_INFO(mSLog, "Add {} to {}, {}", conductance,
241  matrixNodeIndex(0, 2), matrixNodeIndex(0, 2));
242  }
243 }
244 
245 void EMT::Ph3::SynchronGeneratorDQ::mnaCompApplyRightSideVectorStamp(
246  Matrix &rightVector) {
247  if (mCompensationOn)
248  mCompensationCurrent = **mIntfVoltage / mRcomp;
249 
250  // If the interface current is positive, it is flowing out of the connected node and into ground.
251  // Therefore, the generator is interfaced as a consumer but since the currents are reversed the equations
252  // are in generator mode.
253  if (terminalNotGrounded(0)) {
254  Math::setVectorElement(rightVector, matrixNodeIndex(0, 0),
255  -(**mIntfCurrent)(0, 0) +
256  mCompensationCurrent(0, 0));
257  Math::setVectorElement(rightVector, matrixNodeIndex(0, 1),
258  -(**mIntfCurrent)(1, 0) +
259  mCompensationCurrent(1, 0));
260  Math::setVectorElement(rightVector, matrixNodeIndex(0, 2),
261  -(**mIntfCurrent)(2, 0) +
262  mCompensationCurrent(2, 0));
263  }
264 }
265 
267  const Matrix &leftVector) {
268  (**mIntfVoltage)(0, 0) =
269  Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0));
270  (**mIntfVoltage)(1, 0) =
271  Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 1));
272  (**mIntfVoltage)(2, 0) =
273  Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 2));
274 }
275 
277  AttributeBase::List &prevStepDependencies,
278  AttributeBase::List &attributeDependencies,
279  AttributeBase::List &modifiedAttributes,
280  Attribute<Matrix>::Ptr &leftVector) {
281  attributeDependencies.push_back(leftVector);
282  modifiedAttributes.push_back(mIntfVoltage);
283 }
284 
285 void EMT::Ph3::SynchronGeneratorDQ::mnaCompPostStep(
286  Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
287  mnaCompUpdateVoltage(**leftVector);
288 }
289 
291  Matrix &abcVector) {
292  Matrix dq0Vector(3, 1);
293  Matrix abcToDq0(3, 3);
294 
295  // Park transform according to Kundur
296  abcToDq0 << 2. / 3. * cos(theta), 2. / 3. * cos(theta - 2. * PI / 3.),
297  2. / 3. * cos(theta + 2. * PI / 3.), -2. / 3. * sin(theta),
298  -2. / 3. * sin(theta - 2. * PI / 3.),
299  -2. / 3. * sin(theta + 2. * PI / 3.), 1. / 3., 1. / 3., 1. / 3.;
300 
301  dq0Vector = abcToDq0 * abcVector;
302 
303  return dq0Vector;
304 }
305 
307  Matrix &dq0Vector) {
308  Matrix abcVector(3, 1);
309  Matrix dq0ToAbc(3, 3);
310 
311  // Park transform according to Kundur
312  dq0ToAbc << cos(theta), -sin(theta), 1., cos(theta - 2. * PI / 3.),
313  -sin(theta - 2. * PI / 3.), 1., cos(theta + 2. * PI / 3.),
314  -sin(theta + 2. * PI / 3.), 1.;
315 
316  abcVector = dq0ToAbc * dq0Vector;
317 
318  return abcVector;
319 }
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(Matrix frequencies) override
Initialize components with correct network frequencies.
Matrix dq0ToAbcTransform(Real theta, Matrix &dq0)
Inverse Park transform as described in Krause.
virtual void mnaCompUpdateVoltage(const Matrix &leftVector) override
Retrieves calculated voltage from simulation for next step.
void applyParametersOperationalPerUnit()
Calculates fundamental from operational parameters and applies them to the model.
void initializeMatrixAndStates()
Initializes internal states and matrix.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
Add MNA post step dependencies.
void setInitialValues(Real initActivePower, Real initReactivePower, Real initTerminalVolt, Real initVoltAngle, Real initMechPower)
Initialize states according to desired initial electrical powerflow and mechanical input power.
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)
Matrix abcToDq0Transform(Real theta, Matrix &abc)
Park transform as described in Krause.
void initializeFromNodesAndTerminals(Real frequency) override
Initializes component from power flow data.
SynchronGeneratorDQ(String name, String uid, Logger::Level logLevel=Logger::Level::off)
Defines UID, name and logging level.
Base class for all MNA components that are transmitting power.
virtual void initialize(Matrix frequencies)
Initialize components with correct network frequencies.
const Attribute< MatrixVar< Real > >::Ptr mIntfCurrent
Current through component.
Definition: SimPowerComp.h:47
const Attribute< MatrixVar< Real > >::Ptr mIntfVoltage
Voltage between terminals.
Definition: SimPowerComp.h:45