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 if (mSubCompCreated)
166 return;
167 mSubCompCreated = true;
168
169 mSubVoltageSource = SP::Ph1::VoltageSource::make(**mName + "_src", mLogLevel);
170 mSubVoltageSource->connect({SimNode::GND, mVirtualNodes[0]});
171 mSubVoltageSource->setVirtualNodeAt(mVirtualNodes[1], 0);
172 mSubVoltageSource->initialize(mFrequencies);
174 MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
175 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, false);
176
177 mSubInductor = SP::Ph1::Inductor::make(**mName + "_ind", mLogLevel);
178 mSubInductor->setParameters(mLpd);
179 mSubInductor->connect({mVirtualNodes[0], terminal(0)->node()});
180 mSubInductor->initialize(mFrequencies);
181 addMNASubComponent(mSubInductor, MNA_SUBCOMP_TASK_ORDER::TASK_AFTER_PARENT,
182 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, false);
183}
184
186 Real frequency) {
187 // Initialize omega mech with nominal system frequency
188 **mOmMech = mNomOmega;
189
190 // Static calculation based on load flow
191 (**mIntfVoltage)(0, 0) = initialSingleVoltage(0);
192 mInitElecPower = (mInitElecPower == Complex(0, 0))
193 ? -terminal(0)->singlePower()
194 : mInitElecPower;
195 mInitMechPower = (mInitElecPower == Complex(0, 0)) ? mInitElecPower.real()
196 : mInitMechPower;
197
198 //I_intf is the current which is flowing into the Component, while mInitElecPower is flowing out of it
199 (**mIntfCurrent)(0, 0) = std::conj(-mInitElecPower / (**mIntfVoltage)(0, 0));
200
201 mImpedance = Complex(**mRs, mXpd);
202
203 // Calculate initial emf behind reactance from power flow results
204 **mEp = (**mIntfVoltage)(0, 0) - mImpedance * (**mIntfCurrent)(0, 0);
205
206 // The absolute value of Ep is constant, only delta_p changes every step
207 **mEp_abs = Math::abs(**mEp);
208 // Delta_p is the angular position of mEp with respect to the synchronously rotating reference
209 **mDelta_p = Math::phase(**mEp);
210
211 // Update electrical power
212 // TODO: review for Rs != 0
214 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
216 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).imag();
217
218 // Start in steady state so that electrical and mech. power are the same
219 // because of the initial condition mOmMech = mNomOmega the damping factor is not considered at the initialisation
220 // TODO: review for Rs != 0
222
223 // Initialize node between X'd and Ep
224 mVirtualNodes[0]->setInitialVoltage(**mEp);
225
226 // Set emf on the already-created voltage source; the framework's generic
227 // sub-init loop will initialize it after this hook returns.
228 mSubVoltageSource->setParameters(**mEp);
229
230 SPDLOG_LOGGER_INFO(mSLog,
231 "\n--- Initialize according to powerflow ---"
232 "\nTerminal 0 voltage: {:e}<{:e}"
233 "\nVoltage behind reactance: {:e}<{:e}"
234 "\ninitial electrical power: {:e}+j{:e}"
235 "\nactive electrical power: {:e}"
236 "\nreactive electrical power: {:e}"
237 "\nmechanical power: {:e}"
238 "\n--- End of powerflow initialization ---",
239 Math::abs((**mIntfVoltage)(0, 0)),
240 Math::phaseDeg((**mIntfVoltage)(0, 0)), Math::abs(**mEp),
241 Math::phaseDeg(**mEp), mInitElecPower.real(),
242 mInitElecPower.imag(), **mElecActivePower,
244}
245
246void SP::Ph1::SynchronGeneratorTrStab::step(Real time) {
247
248 // #### Calculations based on values from time step k ####
249 // Electrical power at time step k
250 // TODO: review for Rs != 0
251 **mElecActivePower =
252 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
253 **mElecReactivePower =
254 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).imag();
255
256 // Mechanical speed derivative at time step k
257 // convert torque to power with actual rotor angular velocity or nominal omega
258 Real dOmMech;
259 if (mConvertWithOmegaMech)
260 dOmMech =
261 mNomOmega * mNomOmega / (2. * **mInertia * mNomPower * **mOmMech) *
262 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
263 else
264 dOmMech =
265 mNomOmega / (2. * **mInertia * mNomPower) *
266 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
267
268 // #### Calculate states for time step k+1 applying semi-implicit Euler ####
269 // Mechanical speed at time step k+1 applying Euler forward
270 if (mBehaviour == Behaviour::MNASimulation)
271 **mOmMech = **mOmMech + mTimeStep * dOmMech;
272
273 // Derivative of rotor angle at time step k + 1
274 // if reference omega is set, calculate delta with respect to reference
275 Real dDelta_p = **mOmMech - (mUseOmegaRef ? **mRefOmega : mNomOmega);
276
277 // Rotor angle at time step k + 1 applying Euler backward
278 // Update emf - only phase changes
279 if (mBehaviour == Behaviour::MNASimulation) {
280 **mDelta_p = **mDelta_p + mTimeStep * dDelta_p;
281 **mEp = Complex(**mEp_abs * cos(**mDelta_p), **mEp_abs * sin(**mDelta_p));
282 }
283
284 mStates << Math::abs(**mEp), Math::phaseDeg(**mEp), **mElecActivePower,
285 **mMechPower, **mDelta_p, **mOmMech, dOmMech, dDelta_p,
286 (**mIntfVoltage)(0, 0).real(), (**mIntfVoltage)(0, 0).imag();
287 SPDLOG_LOGGER_DEBUG(mSLog, "\nStates, time {:f}: \n{:s}", time,
288 Logger::matrixToString(mStates));
289}
290
292 Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
293 mTimeStep = timeStep;
294
295 mMnaTasks.push_back(std::make_shared<AddBStep>(*this));
296}
297
299 AttributeBase::List &prevStepDependencies,
300 AttributeBase::List &attributeDependencies,
301 AttributeBase::List &modifiedAttributes) {
302 // other attributes generally also influence the pre step,
303 // but aren't marked as writable anyway
305 prevStepDependencies.push_back(mIntfVoltage);
306};
307
308void SP::Ph1::SynchronGeneratorTrStab::mnaParentAddPostStepDependencies(
309 AttributeBase::List &prevStepDependencies,
310 AttributeBase::List &attributeDependencies,
311 AttributeBase::List &modifiedAttributes,
312 Attribute<Matrix>::Ptr &leftVector) {
313 attributeDependencies.push_back(leftVector);
314 modifiedAttributes.push_back(mIntfVoltage);
315};
316
318 Int timeStepCount) {
319 step(time);
320 //change V_ref of subvoltage source
321 mSubVoltageSource->mVoltageRef->set(**mEp);
322}
323
324void SP::Ph1::SynchronGeneratorTrStab::AddBStep::execute(Real time,
325 Int timeStepCount) {
326 **mGenerator.mRightVector = mGenerator.mSubInductor->mRightVector->get() +
327 mGenerator.mSubVoltageSource->mRightVector->get();
328}
329
330void SP::Ph1::SynchronGeneratorTrStab::mnaParentPostStep(
331 Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
332 mnaCompUpdateVoltage(**leftVector);
333 mnaCompUpdateCurrent(**leftVector);
334}
335
336void SP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateVoltage(
337 const Matrix &leftVector) {
338 SPDLOG_LOGGER_DEBUG(mSLog, "Read voltage from {:d}", matrixNodeIndex(0));
339 (**mIntfVoltage)(0, 0) =
340 Math::complexFromVectorElement(leftVector, matrixNodeIndex(0));
341}
342
343void SP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateCurrent(
344 const Matrix &leftVector) {
345 SPDLOG_LOGGER_DEBUG(mSLog, "Read current from {:d}", matrixNodeIndex(0));
346 //Current flowing out of component
347 **mIntfCurrent = mSubInductor->mIntfCurrent->get();
348}
349
350void SP::Ph1::SynchronGeneratorTrStab::setReferenceOmega(
351 Attribute<Real>::Ptr refOmegaPtr, Attribute<Real>::Ptr refDeltaPtr) {
352 mRefOmega->setReference(refOmegaPtr);
353 mRefDelta->setReference(refDeltaPtr);
354 mUseOmegaRef = true;
355
356 SPDLOG_LOGGER_INFO(mSLog, "Use of reference omega.");
357}
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 initializeParentFromNodesAndTerminals(Real frequency) override
void setModelFlags(Bool convertWithOmegaMech)
Flags to modify model behavior.
void createSubComponents() override
Constructs and registers MNA subcomponents without emf value; idempotent.
std::shared_ptr< VoltageSource > mSubVoltageSource
Inner voltage source that represents the generator.
const Attribute< Complex >::Ptr mEp
True after createSubComponents() runs; prevents double-construction.
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.