DPsim
Loading...
Searching...
No Matches
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>
10using namespace CPS;
11
12DP::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
30SimPowerComp<Complex>::Ptr
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}",
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}",
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}",
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
152void 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
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
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);
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
230void 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
288void 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
304void 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
316void 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
323void 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
330void 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 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 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)
void mnaParentPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
Retrieves calculated voltage from simulation for next step.
std::shared_ptr< Inductor > mSubInductor
Inner inductor that represents the generator impedance.
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.
Complex mImpedance
Equivalent impedance for loadflow calculation.
const Attribute< Complex >::Ptr mEp
emf behind transient reactance
void mnaParentAddPreStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes) override
SimPowerComp< Complex >::Ptr clone(String name) override
Returns a modified copy of the component with the given suffix added to the name and without.
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.
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.
Bool mConvertWithOmegaMech
Flag for usage of actual mechanical speed for torque conversion (otherwise mNomOmega is used)
void setFundamentalParametersPU(Real nomPower, Real nomVolt, Real nomFreq, Real Ll, Real Lmd, Real Llfd, Real inertia, Real D=0)
Initializes the machine parameters.
std::shared_ptr< VoltageSource > mSubVoltageSource
Inner voltage source that represents the generator.
void mnaParentPreStep(Real time, Int timeStepCount) override
Real mLpd
Absolute d-axis transient inductance.
const Attribute< String >::Ptr mName
Human readable name.
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.