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 if (mSubCompCreated)
160 return;
161 mSubCompCreated = true;
162
163 // Create the inner voltage source (Ep). Parameters (mEp) depend on the
164 // power-flow solution and are not yet known here; they are applied in
165 // initializeFromNodesAndTerminals after the static calculation.
166 mSubVoltageSource = DP::Ph1::VoltageSource::make(**mName + "_src", mLogLevel);
167 mSubVoltageSource->connect({SimNode::GND, mVirtualNodes[0]});
168 // Alias the sub-source's virtual node to parent VN1 so the KCL row in the
169 // system matrix uses the same node index as the parent's VN1.
170 mSubVoltageSource->setVirtualNodeAt(mVirtualNodes[1], 0);
171 mSubVoltageSource->initialize(mFrequencies);
173 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
174 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
175
176 // Create the inner inductor representing X'd.
177 mSubInductor = DP::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_BEFORE_PARENT,
182 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
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 active electrical power that is compared with the mechanical power
213 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
214
215 // Start in steady state so that electrical and mech. power are the same
216 // because of the initial condition mOmMech = mNomOmega the damping factor is not considered at the initialisation
218
219 // Initialize node between X'd and Ep
220 mVirtualNodes[0]->setInitialVoltage(**mEp);
221
222 // Set emf on the already-created voltage source; the framework's generic
223 // sub-init loop will initialize it after this hook returns.
224 mSubVoltageSource->setParameters(**mEp);
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 "\nmechanical power: {:e}"
233 "\n--- End of powerflow initialization ---",
234 Math::abs((**mIntfVoltage)(0, 0)),
235 Math::phaseDeg((**mIntfVoltage)(0, 0)), Math::abs(**mEp),
236 Math::phaseDeg(**mEp), mInitElecPower.real(),
237 mInitElecPower.imag(), **mElecActivePower, **mMechPower);
238}
239
240void DP::Ph1::SynchronGeneratorTrStab::step(Real time) {
241
242 // #### Calculations based on values from time step k ####
243 // Electrical power at time step k
244 **mElecActivePower =
245 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
246
247 // Mechanical speed derivative at time step k
248 // convert torque to power with actual rotor angular velocity or nominal omega
249 Real dOmMech;
250 if (mConvertWithOmegaMech)
251 dOmMech =
252 mNomOmega * mNomOmega / (2. * **mInertia * mNomPower * **mOmMech) *
253 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
254 else
255 dOmMech =
256 mNomOmega / (2. * **mInertia * mNomPower) *
257 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
258
259 // #### Calculate states for time step k+1 applying semi-implicit Euler ####
260 // Mechanical speed at time step k+1 applying Euler forward
261 if (mBehaviour == Behaviour::MNASimulation)
262 **mOmMech = **mOmMech + mTimeStep * dOmMech;
263
264 // Derivative of rotor angle at time step k + 1
265 // if reference omega is set, calculate delta with respect to reference
266 Real dDelta_p = **mOmMech - (mUseOmegaRef ? **mRefOmega : mNomOmega);
267
268 // Rotor angle at time step k + 1 applying Euler backward
269 // Update emf - only phase changes
270 if (mBehaviour == Behaviour::MNASimulation) {
271 **mDelta_p = **mDelta_p + mTimeStep * dDelta_p;
272 **mEp = Complex(**mEp_abs * cos(**mDelta_p), **mEp_abs * sin(**mDelta_p));
273 }
274
275 mStates << Math::abs(**mEp), Math::phaseDeg(**mEp), **mElecActivePower,
276 **mMechPower, **mDelta_p, **mOmMech, dOmMech, dDelta_p,
277 (**mIntfVoltage)(0, 0).real(), (**mIntfVoltage)(0, 0).imag();
278 SPDLOG_LOGGER_DEBUG(mSLog, "\nStates, time {:f}: \n{:s}", time,
279 Logger::matrixToString(mStates));
280}
281
283 Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
284 mTimeStep = timeStep;
285 mMnaTasks.push_back(std::make_shared<AddBStep>(*this));
286}
287
289 AttributeBase::List &prevStepDependencies,
290 AttributeBase::List &attributeDependencies,
291 AttributeBase::List &modifiedAttributes) {
292 // other attributes generally also influence the pre step,
293 // but aren't marked as writable anyway
295 prevStepDependencies.push_back(mIntfVoltage);
296}
297
298void DP::Ph1::SynchronGeneratorTrStab::mnaParentAddPostStepDependencies(
299 AttributeBase::List &prevStepDependencies,
300 AttributeBase::List &attributeDependencies,
301 AttributeBase::List &modifiedAttributes,
302 Attribute<Matrix>::Ptr &leftVector) {
303 attributeDependencies.push_back(leftVector);
304 modifiedAttributes.push_back(mIntfVoltage);
305}
306
308 Int timeStepCount) {
309 step(time);
310 //change V_ref of subvoltage source
311 mSubVoltageSource->mVoltageRef->set(**mEp);
312}
313
314void DP::Ph1::SynchronGeneratorTrStab::AddBStep::execute(Real time,
315 Int timeStepCount) {
316 **mGenerator.mRightVector = mGenerator.mSubInductor->mRightVector->get() +
317 mGenerator.mSubVoltageSource->mRightVector->get();
318}
319
321 Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
322 mnaCompUpdateVoltage(**leftVector);
323 mnaCompUpdateCurrent(**leftVector);
324}
325
326void DP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateVoltage(
327 const Matrix &leftVector) {
328 SPDLOG_LOGGER_DEBUG(mSLog, "Read voltage from {:d}", matrixNodeIndex(0));
329 (**mIntfVoltage)(0, 0) =
330 Math::complexFromVectorElement(leftVector, matrixNodeIndex(0));
331}
332
333void DP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateCurrent(
334 const Matrix &leftVector) {
335 SPDLOG_LOGGER_DEBUG(mSLog, "Read current from {:d}", matrixNodeIndex(0));
336 //Current flowing out of component
337 **mIntfCurrent = mSubInductor->mIntfCurrent->get();
338}
339
340void DP::Ph1::SynchronGeneratorTrStab::setReferenceOmega(
341 Attribute<Real>::Ptr refOmegaPtr, Attribute<Real>::Ptr refDeltaPtr) {
342 mRefOmega->setReference(refOmegaPtr);
343 mRefDelta->setReference(refDeltaPtr);
344 mUseOmegaRef = true;
345
346 SPDLOG_LOGGER_INFO(mSLog, "Use of reference omega.");
347}
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.
Complex mImpedance
Equivalent impedance for loadflow calculation.
const Attribute< Complex >::Ptr mEp
True after createSubComponents() runs; prevents double-construction.
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.
void createSubComponents() override
Constructs and registers MNA subcomponents without emf value; idempotent.
void initializeParentFromNodesAndTerminals(Real frequency) override
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.