DPsim
Loading...
Searching...
No Matches
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>
10using namespace CPS;
11
12SP::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
31SimPowerComp<Complex>::Ptr
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}",
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}",
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}",
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
158void 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
194 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
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
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);
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,
240}
241
242void 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
304void 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
320void SP::Ph1::SynchronGeneratorTrStab::AddBStep::execute(Real time,
321 Int timeStepCount) {
322 **mGenerator.mRightVector = mGenerator.mSubInductor->mRightVector->get() +
323 mGenerator.mSubVoltageSource->mRightVector->get();
324}
325
326void SP::Ph1::SynchronGeneratorTrStab::mnaParentPostStep(
327 Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
328 mnaCompUpdateVoltage(**leftVector);
329 mnaCompUpdateCurrent(**leftVector);
330}
331
332void 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
339void 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
346void 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 mTimeStep
Simulation time step.
Real mLmd
d-axis mutual inductance Lmd [H]
Real mLlfd
field leakage inductance Llfd [H]
const Attribute< Real >::Ptr mMechPower
mechanical Power Pm [W]
Real mNomVolt
nominal voltage Vn [V] (phase-to-phase RMS)
Real mBase_L
base stator inductance
Real mBase_Z
base stator impedance
const Attribute< Real >::Ptr mRs
stator resistance Rs [Ohm]
const Attribute< Real >::Ptr mElecReactivePower
Reactive part of the electrical power.
const Attribute< Real >::Ptr mElecActivePower
Active part of the electrical power.
StateType mStateType
specifies if the machine parameters are transformed to per unit
Real mNomOmega
nominal angular frequency wn [Hz]
const Attribute< Real >::Ptr mLl
leakage inductance Ll [H]
Real mLfd
field inductance Lfd [H]
const Attribute< Real >::Ptr mLd
d-axis inductance Ld [H]
Real mNomPower
nominal power Pn [VA]
const Attribute< Real >::Ptr mOmMech
rotor speed omega_r
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 addMNASubComponent(typename SimPowerComp< Complex >::Ptr subc, MNA_SUBCOMP_TASK_ORDER preStepOrder, MNA_SUBCOMP_TASK_ORDER postStepOrder, Bool contributeToRightVector)
const Attribute< String >::Ptr mName
Human readable name.
Complex mImpedance
Equivalent impedance for loadflow calculation.
void mnaParentPreStep(Real time, Int timeStepCount) override
MNA pre and post step operations.
Bool mConvertWithOmegaMech
Flag for usage of actual mechanical speed for torque conversion (otherwise mNomOmega is used)
SimPowerComp< Complex >::Ptr clone(String name) override
DEPRECATED: Delete method.
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.
std::shared_ptr< VoltageSource > mSubVoltageSource
Inner voltage source that represents the generator.
const Attribute< Complex >::Ptr mEp
emf behind transient reactance
void setStandardParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Xpd, Real inertia, Real Rs=0, Real D=0)
Initializes the machine parameters.
const Attribute< Real >::Ptr mDelta_p
Angle by which the emf Ep is leading the terminal voltage.
std::shared_ptr< Inductor > mSubInductor
Inner inductor that represents the generator impedance.
Real mLpd
Absolute d-axis transient inductance.
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.
const Attribute< MatrixVar< Complex > >::Ptr mIntfCurrent
SimTerminal< Complex >::Ptr terminal(UInt index)
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
SimNode< Complex >::List mVirtualNodes
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.