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