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