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);
172 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT,
173 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
174
175 // Create the inner inductor representing X'd.
176 mSubInductor = DP::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_BEFORE_PARENT,
180 MNA_SUBCOMP_TASK_ORDER::TASK_BEFORE_PARENT, true);
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 active electrical power that is compared with the mechanical power
211 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
212
213 // Start in steady state so that electrical and mech. power are the same
214 // because of the initial condition mOmMech = mNomOmega the damping factor is not considered at the initialisation
216
217 // Initialize node between X'd and Ep
218 mVirtualNodes[0]->setInitialVoltage(**mEp);
219
220 // Set emf on the already-created voltage source; the framework's generic
221 // sub-init loop will initialize it after this hook returns.
222 mSubVoltageSource->setParameters(**mEp);
223
224 SPDLOG_LOGGER_INFO(mSLog,
225 "\n--- Initialize according to powerflow ---"
226 "\nTerminal 0 voltage: {:e}<{:e}"
227 "\nVoltage behind reactance: {:e}<{:e}"
228 "\ninitial electrical power: {:e}+j{:e}"
229 "\nactive electrical power: {:e}"
230 "\nmechanical power: {:e}"
231 "\n--- End of powerflow initialization ---",
232 Math::abs((**mIntfVoltage)(0, 0)),
233 Math::phaseDeg((**mIntfVoltage)(0, 0)), Math::abs(**mEp),
234 Math::phaseDeg(**mEp), mInitElecPower.real(),
235 mInitElecPower.imag(), **mElecActivePower, **mMechPower);
236}
237
238void DP::Ph1::SynchronGeneratorTrStab::step(Real time) {
239
240 // #### Calculations based on values from time step k ####
241 // Electrical power at time step k
242 **mElecActivePower =
243 ((**mIntfVoltage)(0, 0) * std::conj(-(**mIntfCurrent)(0, 0))).real();
244
245 // Mechanical speed derivative at time step k
246 // convert torque to power with actual rotor angular velocity or nominal omega
247 Real dOmMech;
248 if (mConvertWithOmegaMech)
249 dOmMech =
250 mNomOmega * mNomOmega / (2. * **mInertia * mNomPower * **mOmMech) *
251 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
252 else
253 dOmMech =
254 mNomOmega / (2. * **mInertia * mNomPower) *
255 (**mMechPower - **mElecActivePower - mKd * (**mOmMech - mNomOmega));
256
257 // #### Calculate states for time step k+1 applying semi-implicit Euler ####
258 // Mechanical speed at time step k+1 applying Euler forward
259 if (mBehaviour == Behaviour::MNASimulation)
260 **mOmMech = **mOmMech + mTimeStep * dOmMech;
261
262 // Derivative of rotor angle at time step k + 1
263 // if reference omega is set, calculate delta with respect to reference
264 Real dDelta_p = **mOmMech - (mUseOmegaRef ? **mRefOmega : mNomOmega);
265
266 // Rotor angle at time step k + 1 applying Euler backward
267 // Update emf - only phase changes
268 if (mBehaviour == Behaviour::MNASimulation) {
269 **mDelta_p = **mDelta_p + mTimeStep * dDelta_p;
270 **mEp = Complex(**mEp_abs * cos(**mDelta_p), **mEp_abs * sin(**mDelta_p));
271 }
272
273 mStates << Math::abs(**mEp), Math::phaseDeg(**mEp), **mElecActivePower,
274 **mMechPower, **mDelta_p, **mOmMech, dOmMech, dDelta_p,
275 (**mIntfVoltage)(0, 0).real(), (**mIntfVoltage)(0, 0).imag();
276 SPDLOG_LOGGER_DEBUG(mSLog, "\nStates, time {:f}: \n{:s}", time,
277 Logger::matrixToString(mStates));
278}
279
281 Real omega, Real timeStep, Attribute<Matrix>::Ptr leftVector) {
282 mTimeStep = timeStep;
283 mMnaTasks.push_back(std::make_shared<AddBStep>(*this));
284}
285
287 AttributeBase::List &prevStepDependencies,
288 AttributeBase::List &attributeDependencies,
289 AttributeBase::List &modifiedAttributes) {
290 // other attributes generally also influence the pre step,
291 // but aren't marked as writable anyway
293 prevStepDependencies.push_back(mIntfVoltage);
294}
295
296void DP::Ph1::SynchronGeneratorTrStab::mnaParentAddPostStepDependencies(
297 AttributeBase::List &prevStepDependencies,
298 AttributeBase::List &attributeDependencies,
299 AttributeBase::List &modifiedAttributes,
300 Attribute<Matrix>::Ptr &leftVector) {
301 attributeDependencies.push_back(leftVector);
302 modifiedAttributes.push_back(mIntfVoltage);
303}
304
306 Int timeStepCount) {
307 step(time);
308 //change V_ref of subvoltage source
309 mSubVoltageSource->mVoltageRef->set(**mEp);
310}
311
312void DP::Ph1::SynchronGeneratorTrStab::AddBStep::execute(Real time,
313 Int timeStepCount) {
314 **mGenerator.mRightVector = mGenerator.mSubInductor->mRightVector->get() +
315 mGenerator.mSubVoltageSource->mRightVector->get();
316}
317
319 Real time, Int timeStepCount, Attribute<Matrix>::Ptr &leftVector) {
320 mnaCompUpdateVoltage(**leftVector);
321 mnaCompUpdateCurrent(**leftVector);
322}
323
324void DP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateVoltage(
325 const Matrix &leftVector) {
326 SPDLOG_LOGGER_DEBUG(mSLog, "Read voltage from {:d}", matrixNodeIndex(0));
327 (**mIntfVoltage)(0, 0) =
328 Math::complexFromVectorElement(leftVector, matrixNodeIndex(0));
329}
330
331void DP::Ph1::SynchronGeneratorTrStab::mnaCompUpdateCurrent(
332 const Matrix &leftVector) {
333 SPDLOG_LOGGER_DEBUG(mSLog, "Read current from {:d}", matrixNodeIndex(0));
334 //Current flowing out of component
335 **mIntfCurrent = mSubInductor->mIntfCurrent->get();
336}
337
338void DP::Ph1::SynchronGeneratorTrStab::setReferenceOmega(
339 Attribute<Real>::Ptr refOmegaPtr, Attribute<Real>::Ptr refDeltaPtr) {
340 mRefOmega->setReference(refOmegaPtr);
341 mRefDelta->setReference(refDeltaPtr);
342 mUseOmegaRef = true;
343
344 SPDLOG_LOGGER_INFO(mSLog, "Use of reference omega.");
345}
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.