DPsim
SP_Ph1_SynchronGeneratorTrStab.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/SP/SP_Ph1_SynchronGeneratorTrStab.h>
10 using namespace CPS;
11 
12 SP::Ph1::SynchronGeneratorTrStab::SynchronGeneratorTrStab(
13  String uid, String name, Logger::Level logLevel)
14  : Base::SynchronGenerator(mAttributes),
15  CompositePowerComp<Complex>(uid, name, true, true, logLevel),
16  mEp(mAttributes->create<Complex>("Ep")),
17  mEp_abs(mAttributes->create<Real>("Ep_mag")),
18  mEp_phase(mAttributes->create<Real>("Ep_phase")),
19  mDelta_p(mAttributes->create<Real>("delta_r")),
20  mRefOmega(mAttributes->createDynamic<Real>("w_ref")),
21  mRefDelta(mAttributes->createDynamic<Real>("delta_ref")) {
22  setVirtualNodeNumber(2);
23  setTerminalNumber(1);
24  **mIntfVoltage = MatrixComp::Zero(1, 1);
25  **mIntfCurrent = MatrixComp::Zero(1, 1);
26 
27  mStates = Matrix::Zero(10, 1);
28 }
29 
32 SP::Ph1::SynchronGeneratorTrStab::clone(String name) {
33  auto copy = SynchronGeneratorTrStab::make(name, mLogLevel);
34  copy->setStandardParametersPU(mNomPower, mNomVolt, mNomFreq, mXpd / mBase_Z,
35  **mInertia, **mRs, mKd);
36  return copy;
37 }
38 
40  Real nomPower, Real nomVolt, Real nomFreq, Real Ll, Real Lmd, Real Llfd,
41  Real inertia, Real D) {
42  setBaseParameters(nomPower, nomVolt, nomFreq);
43  SPDLOG_LOGGER_INFO(mSLog,
44  "\n--- Base Parameters ---"
45  "\nnomPower: {:f}"
46  "\nnomVolt: {:f}"
47  "\nnomFreq: {:f}",
48  mNomPower, mNomVolt, mNomFreq);
49 
50  // Input is in per unit but all values are converted to absolute values.
51  mParameterType = ParameterType::statorReferred;
52  mStateType = StateType::statorReferred;
53 
54  **mLl = Ll;
55  mLmd = Lmd;
56  **mLd = **mLl + mLmd;
57  mLlfd = Llfd;
58  mLfd = mLlfd + mLmd;
59  // M = 2*H where H = inertia
60  **mInertia = inertia;
61  // X'd in absolute values
62  mXpd = mNomOmega * (**mLd - mLmd * mLmd / mLfd) * mBase_L;
63  mLpd = (**mLd - mLmd * mLmd / mLfd) * mBase_L;
64 
65  //The units of D are per unit power divided by per unit speed deviation.
66  // D is transformed to an absolute value to obtain Kd, which will be used in the swing equation
67  mKd = D * mNomPower / mNomOmega;
68 
69  SPDLOG_LOGGER_INFO(mSLog,
70  "\n--- Parameters ---"
71  "\nimpedance: {:f}"
72  "\ninductance: {:f}"
73  "\ninertia: {:f}"
74  "\ndamping: {:f}",
75  mXpd, mLpd, **mInertia, mKd);
76 }
77 
79  Real nomPower, Real nomVolt, Real nomFreq, Int polePairNumber, Real Rs,
80  Real Lpd, Real inertiaJ, Real Kd) {
81  setBaseParameters(nomPower, nomVolt, nomFreq);
82  SPDLOG_LOGGER_INFO(mSLog,
83  "\n--- Base Parameters ---"
84  "\nnomPower: {:f}"
85  "\nnomVolt: {:f}"
86  "\nnomFreq: {:f}",
87  mNomPower, mNomVolt, mNomFreq);
88 
89  mParameterType = ParameterType::statorReferred;
90  mStateType = StateType::statorReferred;
91 
92  // M = 2*H where H = inertia
93  // H = J * 0.5 * omegaNom^2 / polePairNumber
94  **mInertia = calcHfromJ(inertiaJ, 2 * PI * nomFreq, polePairNumber);
95  // X'd in absolute values
96  mXpd = mNomOmega * Lpd;
97  mLpd = Lpd;
98 
99  SPDLOG_LOGGER_INFO(mSLog,
100  "\n--- Parameters ---"
101  "\nimpedance: {:f}"
102  "\ninductance: {:f}"
103  "\ninertia: {:f}"
104  "\ndamping: {:f}",
105  mXpd, mLpd, **mInertia, mKd);
106 }
107 
109  Real nomPower, Real nomVolt, Real nomFreq, Real Xpd, Real inertia, Real Rs,
110  Real D) {
111  setBaseParameters(nomPower, nomVolt, nomFreq);
112  SPDLOG_LOGGER_INFO(mSLog,
113  "\n--- Base Parameters ---"
114  "\nnomPower: {:f}"
115  "\nnomVolt: {:f}"
116  "\nnomFreq: {:f}",
117  mNomPower, mNomVolt, mNomFreq);
118 
119  SPDLOG_LOGGER_INFO(mSLog,
120  "\n--- Parameters Per-Unit ---"
121  "\n Xpd: {:f} [p.u.]",
122  Xpd);
123 
124  // Input is in per unit but all values are converted to absolute values.
125  mParameterType = ParameterType::statorReferred;
126  mStateType = StateType::statorReferred;
127 
128  // M = 2*H where H = inertia
129  **mInertia = inertia;
130  // X'd in absolute values
131  mXpd = Xpd * mBase_Z;
132  mLpd = Xpd * mBase_L;
133 
134  **mRs = Rs;
135  //The units of D are per unit power divided by per unit speed deviation.
136  // D is transformed to an absolute value to obtain Kd, which will be used in the swing equation
137  mKd = D * mNomPower / mNomOmega;
138 
139  SPDLOG_LOGGER_INFO(mSLog,
140  "\n--- Parameters ---"
141  "\nXpd: {:f} [Ohm]"
142  "\nLpd: {:f} [H]"
143  "\nInertia: {:f} [s]"
144  "\nDamping: {:f}",
145  mXpd, mLpd, **mInertia, mKd);
146 }
147 
149  Bool convertWithOmegaMech) {
150  mConvertWithOmegaMech = convertWithOmegaMech;
151 
152  SPDLOG_LOGGER_INFO(mSLog,
153  "\n--- Model flags ---"
154  "\nconvertWithOmegaMech: {:s}",
155  std::to_string(mConvertWithOmegaMech));
156 }
157 
158 void SP::Ph1::SynchronGeneratorTrStab::setInitialValues(Complex elecPower,
159  Real mechPower) {
160  mInitElecPower = elecPower;
161  mInitMechPower = mechPower;
162 }
163 
165  Real frequency) {
166 
167  // Initialize omega mech with nominal system frequency
168  **mOmMech = mNomOmega;
169 
170  // Static calculation based on load flow
171  (**mIntfVoltage)(0, 0) = initialSingleVoltage(0);
172  mInitElecPower = (mInitElecPower == Complex(0, 0))
173  ? -terminal(0)->singlePower()
174  : mInitElecPower;
175  mInitMechPower = (mInitElecPower == Complex(0, 0)) ? mInitElecPower.real()
176  : mInitMechPower;
177 
178  //I_intf is the current which is flowing into the Component, while mInitElecPower is flowing out of it
179  (**mIntfCurrent)(0, 0) = std::conj(-mInitElecPower / (**mIntfVoltage)(0, 0));
180 
181  mImpedance = Complex(**mRs, mXpd);
182 
183  // Calculate initial emf behind reactance from power flow results
184  **mEp = (**mIntfVoltage)(0, 0) - mImpedance * (**mIntfCurrent)(0, 0);
185 
186  // The absolute value of Ep is constant, only delta_p changes every step
187  **mEp_abs = Math::abs(**mEp);
188  // Delta_p is the angular position of mEp with respect to the synchronously rotating reference
189  **mDelta_p = Math::phase(**mEp);
190 
191  // Update electrical power
192  // TODO: review for Rs != 0
193  **mElecActivePower =
194  ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
195  **mElecReactivePower =
196  ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).imag();
197 
198  // Start in steady state so that electrical and mech. power are the same
199  // because of the initial condition mOmMech = mNomOmega the damping factor is not considered at the initialisation
200  // TODO: review for Rs != 0
201  **mMechPower = **mElecActivePower;
202 
203  // Initialize node between X'd and Ep
204  mVirtualNodes[0]->setInitialVoltage(**mEp);
205 
206  // Create sub voltage source for emf
207  mSubVoltageSource = SP::Ph1::VoltageSource::make(**mName + "_src", mLogLevel);
208  mSubVoltageSource->setParameters(**mEp);
209  mSubVoltageSource->connect({SimNode::GND, mVirtualNodes[0]});
210  mSubVoltageSource->setVirtualNodeAt(mVirtualNodes[1], 0);
211  mSubVoltageSource->initialize(mFrequencies);
212  mSubVoltageSource->initializeFromNodesAndTerminals(frequency);
213  addMNASubComponent(mSubVoltageSource,
214  MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
215  MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, false);
216 
217  // Create sub inductor as Xpd
218  mSubInductor = SP::Ph1::Inductor::make(**mName + "_ind", mLogLevel);
219  mSubInductor->setParameters(mLpd);
220  mSubInductor->connect({mVirtualNodes[0], terminal(0)->node()});
221  mSubInductor->initialize(mFrequencies);
222  mSubInductor->initializeFromNodesAndTerminals(frequency);
223  addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
224  MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, false);
225 
226  SPDLOG_LOGGER_INFO(mSLog,
227  "\n--- Initialize according to powerflow ---"
228  "\nTerminal 0 voltage: {:e}<{:e}"
229  "\nVoltage behind reactance: {:e}<{:e}"
230  "\ninitial electrical power: {:e}+j{:e}"
231  "\nactive electrical power: {:e}"
232  "\nreactive electrical power: {:e}"
233  "\nmechanical power: {:e}"
234  "\n--- End of powerflow initialization ---",
235  Math::abs((**mIntfVoltage)(0, 0)),
236  Math::phaseDeg((**mIntfVoltage)(0, 0)), Math::abs(**mEp),
237  Math::phaseDeg(**mEp), mInitElecPower.real(),
238  mInitElecPower.imag(), **mElecActivePower,
239  **mElecReactivePower, **mMechPower);
240 }
241 
242 void SP::Ph1::SynchronGeneratorTrStab::step(Real time) {
243 
244  // #### Calculations based on values from time step k ####
245  // Electrical power at time step k
246  // TODO: review for Rs != 0
247  **mElecActivePower =
248  ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
249  **mElecReactivePower =
250  ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).imag();
251 
252  // Mechanical speed derivative at time step k
253  // convert torque to power with actual rotor angular velocity or nominal omega
254  Real dOmMech;
255  if (mConvertWithOmegaMech)
256  dOmMech =
257  mNomOmega * mNomOmega / (2. * **mInertia * mNomPower * **mOmMech) *
258  (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
259  else
260  dOmMech =
261  mNomOmega / (2. * **mInertia * mNomPower) *
262  (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
263 
264  // #### Calculate states for time step k+1 applying semi-implicit Euler ####
265  // Mechanical speed at time step k+1 applying Euler forward
266  if (mBehaviour == Behaviour::MNASimulation)
267  **mOmMech = **mOmMech + mTimeStep * dOmMech;
268 
269  // Derivative of rotor angle at time step k + 1
270  // if reference omega is set, calculate delta with respect to reference
271  Real dDelta_p = **mOmMech - (mUseOmegaRef ? **mRefOmega : mNomOmega);
272 
273  // Rotor angle at time step k + 1 applying Euler backward
274  // Update emf - only phase changes
275  if (mBehaviour == Behaviour::MNASimulation) {
276  **mDelta_p = **mDelta_p + mTimeStep * dDelta_p;
277  **mEp = Complex(**mEp_abs * cos(**mDelta_p), **mEp_abs * sin(**mDelta_p));
278  }
279 
280  mStates << Math::abs(**mEp), Math::phaseDeg(**mEp), **mElecActivePower,
281  **mMechPower, **mDelta_p, **mOmMech, dOmMech, dDelta_p,
282  (**mIntfVoltage)(0, 0).real(), (**mIntfVoltage)(0, 0).imag();
283  SPDLOG_LOGGER_DEBUG(mSLog, "\nStates, time {:f}: \n{:s}", time,
284  Logger::matrixToString(mStates));
285 }
286 
288  Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
289  mTimeStep = timeStep;
290 
291  mMnaTasks.push_back(std::make_shared<AddBStep>(*this));
292 }
293 
295  AttributeBase::List &prevStepDependencies,
296  AttributeBase::List &attributeDependencies,
297  AttributeBase::List &modifiedAttributes) {
298  // other attributes generally also influence the pre step,
299  // but aren't marked as writable anyway
301  prevStepDependencies.push_back(mIntfVoltage);
302 };
303 
304 void SP::Ph1::SynchronGeneratorTrStab::mnaParentAddPostStepDependencies(
305  AttributeBase::List &prevStepDependencies,
306  AttributeBase::List &attributeDependencies,
307  AttributeBase::List &modifiedAttributes,
308  Attribute<Matrix>::Ptr &leftVector) {
309  attributeDependencies.push_back(leftVector);
310  modifiedAttributes.push_back(mIntfVoltage);
311 };
312 
314  Int timeStepCount) {
315  step(time);
316  //change V_ref of subvoltage source
317  mSubVoltageSource->mVoltageRef->set(**mEp);
318 }
319 
320 void SP::Ph1::SynchronGeneratorTrStab::AddBStep::execute(Real time,
321  Int timeStepCount) {
322  **mGenerator.mRightVector = mGenerator.mSubInductor->mRightVector->get() +
323  mGenerator.mSubVoltageSource->mRightVector->get();
324 }
325 
326 void SP::Ph1::SynchronGeneratorTrStab::mnaParentPostStep(
327  Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
328  mnaCompUpdateVoltage(**leftVector);
329  mnaCompUpdateCurrent(**leftVector);
330 }
331 
332 void SP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateVoltage(
333  const Matrix &leftVector) {
334  SPDLOG_LOGGER_DEBUG(mSLog, "Read voltage from {:d}", matrixNodeIndex(0));
335  (**mIntfVoltage)(0, 0) =
336  Math::complexFromVectorElement(leftVector, matrixNodeIndex(0));
337 }
338 
339 void SP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateCurrent(
340  const Matrix &leftVector) {
341  SPDLOG_LOGGER_DEBUG(mSLog, "Read current from {:d}", matrixNodeIndex(0));
342  //Current flowing out of component
343  **mIntfCurrent = mSubInductor->mIntfCurrent->get();
344 }
345 
346 void SP::Ph1::SynchronGeneratorTrStab::setReferenceOmega(
347  Attribute<Real>::Ptr refOmegaPtr, Attribute<Real>::Ptr refDeltaPtr) {
348  mRefOmega->setReference(refOmegaPtr);
349  mRefDelta->setReference(refDeltaPtr);
350  mUseOmegaRef = true;
351 
352  SPDLOG_LOGGER_INFO(mSLog, "Use of reference omega.");
353 }
Real mNomFreq
nominal frequency fn [Hz]
Real mNomVolt
nominal voltage Vn [V] (phase-to-phase RMS)
Real mBase_Z
base stator impedance
const Attribute< Real >::Ptr mRs
stator resistance Rs [Ohm]
Real mNomPower
nominal power Pn [VA]
const Attribute< Real >::Ptr mInertia
inertia constant H [s] for per unit or moment of inertia J [kg*m^2]
Base class for composite power components.
void mnaParentPreStep(Real time, Int timeStepCount) override
MNA pre and post step operations.
void setModelFlags(Bool convertWithOmegaMech)
Flags to modify model behavior.
void initializeFromNodesAndTerminals(Real frequency) override
Initializes Component variables according to power flow data stored in Nodes.
void setStandardParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Xpd, Real inertia, Real Rs=0, Real D=0)
Initializes the machine parameters.
void mnaParentInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
Initializes variables of component.
void setStandardParametersSI(Real nomPower, Real nomVolt, Real nomFreq, Int polePairNumber, Real Rs, Real Lpd, Real inertiaJ, Real Kd=0)
Initializes the machine parameters.
Real mXpd
Absolute d-axis transient reactance X'd.
void mnaParentAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override
void setFundamentalParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Ll, Real Lmd, Real Llfd, Real inertia, Real D=0)
Initializes the machine parameters.
Base class for all components that are transmitting power.
Definition: SimPowerComp.h:17
Logger::Level mLogLevel
Component logger control for internal variables.