DPsim
Loading...
Searching...
No Matches
EMT_Ph3_Switch.cpp
Go to the documentation of this file.
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 <algorithm>
10#include <cmath>
11#include <stdexcept>
12
14
15using namespace CPS;
16
18 : MNASimPowerComp<Real>(uid, name, false, true, logLevel),
20 mOpeningRequested(mAttributes->create<Bool>("opening_requested")),
21 mPoleClosedA(mAttributes->create<Bool>("pole_closed_a")),
22 mPoleClosedB(mAttributes->create<Bool>("pole_closed_b")),
23 mPoleClosedC(mAttributes->create<Bool>("pole_closed_c")),
24 mZeroCrossingTimeA(mAttributes->create<Real>("zero_crossing_time_a")),
25 mZeroCrossingTimeB(mAttributes->create<Real>("zero_crossing_time_b")),
26 mZeroCrossingTimeC(mAttributes->create<Real>("zero_crossing_time_c")),
28 mAttributes->create<Bool>("exponential_transition_active")),
30 mAttributes->create<Bool>("exponential_transition_closing")),
31 mExponentialProgress(mAttributes->create<Real>("exponential_progress")),
33 mAttributes->create<Real>("exponential_transition_start_time")),
35 mAttributes->create<Real>("exponential_transition_end_time")),
37 mAttributes->create<Real>("effective_resistance_a")),
39 mAttributes->create<Real>("effective_resistance_b")),
41 mAttributes->create<Real>("effective_resistance_c")) {
43
44 // IMPORTANT:
45 // Keep the three-phase interface dimensions fixed from construction.
46 // DataLogger inspects matrix dimensions when attributes are registered,
47 // before the simulation starts.
48 **mIntfVoltage = Matrix::Zero(3, 1);
49 **mIntfCurrent = Matrix::Zero(3, 1);
50
51 **mOpeningRequested = false;
52 setAllPoles(false);
53 resetZeroCrossingTimes();
54
63}
64
66 auto copy = Switch::make(name, mLogLevel);
67 copy->setParameters(**mOpenResistance, **mClosedResistance, **mIsClosed);
68 copy->setSwitchingMode(mSwitchingMode);
69 copy->setZeroCrossingTolerance(mZeroCrossingTolerance);
70 copy->setExponentialSwitchingTime(mExponentialSwitchingTime);
71 return copy;
72}
73
75 mSwitchingMode = mode;
76
77 // Synchronize physical poles with the configured initial command state.
78 setAllPoles(**mIsClosed);
79 **mOpeningRequested = false;
80 resetZeroCrossingTimes();
81
82 mPreviousCurrentValid = false;
83 mResetZeroCrossingHistory = false;
84
85 resetExponentialTransition();
86 synchronizeEffectiveResistance(**mIsClosed);
87}
88
90 if (tolerance < 0.0) {
91 throw std::invalid_argument(
92 "EMT::Ph3::Switch zero-crossing tolerance must be non-negative.");
93 }
94
95 mZeroCrossingTolerance = tolerance;
96}
97
99 if (switchingTime <= 0.0) {
100 throw std::invalid_argument(
101 "EMT::Ph3::Switch exponential switching time must be positive.");
102 }
103
104 mExponentialSwitchingTime = switchingTime;
105}
106
109
110 // An opening command during a closing ramp abandons that ramp. The poles are
111 // still open at that point, so the switch simply stays open.
113 resetExponentialTransition();
114 setAllPoles(false);
115 synchronizeEffectiveResistance(false);
116 **mOpeningRequested = false;
117
118 SPDLOG_LOGGER_INFO(mSLog, "Opening command received during an exponential "
119 "closing transition. Transition abandoned.");
120 return;
121 }
122
123 if (mSwitchingMode == SwitchingMode::Ideal) {
124 **mOpeningRequested = false;
125 setAllPoles(false);
126 resetZeroCrossingTimes();
127 resetExponentialTransition();
128 synchronizeEffectiveResistance(false);
129 return;
130 }
131
132 if (allPolesOpen()) {
133 **mOpeningRequested = false;
134 return;
135 }
136
137 if (mSwitchingMode == SwitchingMode::CurrentZero) {
138 **mOpeningRequested = true;
139 resetZeroCrossingTimes();
140 resetExponentialTransition();
141 mResetZeroCrossingHistory = true;
142
143 SPDLOG_LOGGER_INFO(mSLog, "Current-zero opening command received. Waiting "
144 "for phase-current zeros.");
145 return;
146 }
147
148 validateExponentialResistanceParameters();
149 **mOpeningRequested = true;
150 resetZeroCrossingTimes();
151 resetExponentialTransition();
153 synchronizeEffectiveResistance(true);
154 setAllPoles(true);
155
156 SPDLOG_LOGGER_INFO(mSLog,
157 "Exponential ZCS-emulation opening command received. "
158 "Switching duration={:.6e}s.",
159 mExponentialSwitchingTime);
160}
161
164
165 **mOpeningRequested = false;
166 resetZeroCrossingTimes();
167 mResetZeroCrossingHistory = false;
168
169 if (mSwitchingMode != SwitchingMode::ExponentialZCSEmulation) {
170 setAllPoles(true);
171 resetExponentialTransition();
172 synchronizeEffectiveResistance(true);
173
174 SPDLOG_LOGGER_INFO(mSLog,
175 "Switch closing command: all three poles closed.");
176 return;
177 }
178
179 if (allPolesClosed() && !**mExponentialTransitionActive) {
180 resetExponentialTransition();
181 synchronizeEffectiveResistance(true);
182 return;
183 }
184
185 // Energising a branch is the mirror image of interrupting it: the same
186 // resistance path is traversed from R_open towards R_closed. The poles stay
187 // open until the ramp completes.
188 validateExponentialResistanceParameters();
189 resetExponentialTransition();
192 **mExponentialProgress = 1.0;
193 synchronizeEffectiveResistance(false);
194 setAllPoles(false);
195
196 SPDLOG_LOGGER_INFO(mSLog,
197 "Exponential ZCS-emulation closing command received. "
198 "Switching duration={:.6e}s.",
199 mExponentialSwitchingTime);
200}
201
203 Matrix impedance = (**mIsClosed) ? **mClosedResistance : **mOpenResistance;
204
205 MatrixComp vInitABC = MatrixComp::Zero(3, 1);
206 vInitABC(0, 0) = initialSingleVoltage(1) - initialSingleVoltage(0);
207 vInitABC(1, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_B;
208 vInitABC(2, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_C;
209
210 **mIntfVoltage = vInitABC.real();
211 **mIntfCurrent = (impedance.inverse() * vInitABC).real();
212
213 // Power-flow initialization defines the initial physical breaker state.
214 setAllPoles(**mIsClosed);
215 **mOpeningRequested = false;
216 resetZeroCrossingTimes();
217 resetExponentialTransition();
218 synchronizeEffectiveResistance(**mIsClosed);
219
220 SPDLOG_LOGGER_INFO(mSLog,
221 "\n--- Initialization from powerflow ---"
222 "\nVoltage across: {:s}"
223 "\nCurrent: {:s}"
224 "\nTerminal 0 voltage: {:s}"
225 "\nTerminal 1 voltage: {:s}"
226 "\nSwitching mode: {:s}"
227 "\nInitial breaker state: {:s}"
228 "\n--- Initialization from powerflow finished ---",
233 mSwitchingMode == SwitchingMode::Ideal
234 ? "Ideal"
235 : (mSwitchingMode == SwitchingMode::CurrentZero
236 ? "CurrentZero"
237 : "ExponentialZCSEmulation"),
238 **mIsClosed ? "closed" : "open");
239}
240
242 Attribute<Matrix>::Ptr leftVector) {
244 mTimeStep = timeStep;
245 **mRightVector = Matrix::Zero(0, 0);
246
247 // setParameters() belongs to the shared base class. Synchronize here as a
248 // final safeguard in case setSwitchingMode() was called before setParameters().
249 setAllPoles(**mIsClosed);
250 synchronizeEffectiveResistance(**mIsClosed);
251 resetExponentialTransition();
252
253 mPoleClosedPrev = {
254 **mPoleClosedA,
255 **mPoleClosedB,
256 **mPoleClosedC,
257 };
258
259 if ((**mIntfCurrent).rows() == 3) {
260 mPreviousCurrent = **mIntfCurrent;
261 mPreviousCurrentValid = true;
262 } else {
263 mPreviousCurrent = Matrix::Zero(3, 1);
264 mPreviousCurrentValid = false;
265 }
266
267 mEffectiveResistancePrev = {{
271 }};
272
273 mPreviousCurrentTime = 0.0;
274 mResetZeroCrossingHistory = false;
275}
276
278 if (mSwitchingMode == SwitchingMode::Ideal)
279 return **mIsClosed;
280
281 return allPolesClosed();
282}
283
287
289 SparseMatrixRow &systemMatrix) {
290 const MatrixFixedSize<3, 3> conductance = currentConductanceMatrix();
291
293 conductance, systemMatrix, matrixNodeIndex(0), matrixNodeIndex(1),
295
296 SPDLOG_LOGGER_TRACE(mSLog, "\nConductance matrix: {:s}",
297 Logger::matrixToString(conductance));
298}
299
301 Bool closed, SparseMatrixRow &systemMatrix, Int freqIdx) {
302 // This method is used by the conventional precomputed two-state switch path.
303 // CurrentZero mode advertises supportsPrecomputedSystemMatrices() == false
304 // and therefore uses mnaCompApplySystemMatrixStamp() through the variable
305 // matrix recomputation path instead.
306 MatrixFixedSize<3, 3> conductance =
307 closed ? (**mClosedResistance).inverse() : (**mOpenResistance).inverse();
308
310 conductance, systemMatrix, matrixNodeIndex(0), matrixNodeIndex(1),
312
313 SPDLOG_LOGGER_TRACE(mSLog, "\nConductance matrix: {:s}",
314 Logger::matrixToString(conductance));
315}
316
318
320 AttributeBase::List &prevStepDependencies,
321 AttributeBase::List &attributeDependencies,
322 AttributeBase::List &modifiedAttributes,
323 Attribute<Matrix>::Ptr &leftVector) {
324 attributeDependencies.push_back(leftVector);
325
326 modifiedAttributes.push_back(mIntfVoltage);
327 modifiedAttributes.push_back(mIntfCurrent);
328
329 modifiedAttributes.push_back(mOpeningRequested);
330 modifiedAttributes.push_back(mPoleClosedA);
331 modifiedAttributes.push_back(mPoleClosedB);
332 modifiedAttributes.push_back(mPoleClosedC);
333 modifiedAttributes.push_back(mZeroCrossingTimeA);
334 modifiedAttributes.push_back(mZeroCrossingTimeB);
335 modifiedAttributes.push_back(mZeroCrossingTimeC);
336
337 modifiedAttributes.push_back(mExponentialTransitionActive);
338 modifiedAttributes.push_back(mExponentialTransitionClosing);
339 modifiedAttributes.push_back(mExponentialProgress);
340 modifiedAttributes.push_back(mExponentialTransitionStartTime);
341 modifiedAttributes.push_back(mExponentialTransitionEndTime);
342 modifiedAttributes.push_back(mEffectiveResistanceA);
343 modifiedAttributes.push_back(mEffectiveResistanceB);
344 modifiedAttributes.push_back(mEffectiveResistanceC);
345}
346
348 Attribute<Matrix>::Ptr &leftVector) {
349 mnaCompUpdateVoltage(**leftVector);
350 mnaCompUpdateCurrent(**leftVector);
351
352 if (mSwitchingMode == SwitchingMode::CurrentZero) {
353 updateZeroCrossingState(time);
354 } else if (mSwitchingMode == SwitchingMode::ExponentialZCSEmulation) {
355 updateExponentialTransition(time);
356 }
357}
358
360 // Voltage across component is defined as V1 - V0.
361 **mIntfVoltage = Matrix::Zero(3, 1);
362
363 if (terminalNotGrounded(1)) {
364 (**mIntfVoltage)(0, 0) =
366 (**mIntfVoltage)(1, 0) =
368 (**mIntfVoltage)(2, 0) =
370 }
371
372 if (terminalNotGrounded(0)) {
373 (**mIntfVoltage)(0, 0) -=
375 (**mIntfVoltage)(1, 0) -=
377 (**mIntfVoltage)(2, 0) -=
379 }
380}
381
383 **mIntfCurrent = currentConductanceMatrix() * **mIntfVoltage;
384}
385
387 if (mSwitchingMode == SwitchingMode::Ideal) {
388 // Preserve the previous DPsim switch behavior.
389 if (mIsClosedPrev != mnaIsClosed()) {
391 return true;
392 }
393
394 return false;
395 }
396
397 if (mSwitchingMode == SwitchingMode::ExponentialZCSEmulation) {
398 const std::array<Real, 3> resistanceNow{{
402 }};
403
404 Bool changed = false;
405 for (UInt phase = 0; phase < 3; ++phase) {
406 if (resistanceNow[phase] != mEffectiveResistancePrev[phase]) {
407 mEffectiveResistancePrev[phase] = resistanceNow[phase];
408 changed = true;
409 }
410 }
411 return changed;
412 }
413
414 const std::array<Bool, 3> poleClosedNow{{
415 **mPoleClosedA,
416 **mPoleClosedB,
417 **mPoleClosedC,
418 }};
419
420 Bool changed = false;
421
422 for (UInt phase = 0; phase < 3; ++phase) {
423 if (poleClosedNow[phase] != mPoleClosedPrev[phase]) {
424 changed = true;
425 mPoleClosedPrev[phase] = poleClosedNow[phase];
426 }
427 }
428
429 return changed;
430}
431
432Bool EMT::Ph3::Switch::poleClosed(UInt phase) const {
433 switch (phase) {
434 case 0:
435 return **mPoleClosedA;
436 case 1:
437 return **mPoleClosedB;
438 case 2:
439 return **mPoleClosedC;
440 default:
441 throw std::out_of_range("EMT::Ph3::Switch phase index must be 0, 1, or 2.");
442 }
443}
444
445void EMT::Ph3::Switch::setPoleClosed(UInt phase, Bool closed) {
446 switch (phase) {
447 case 0:
448 **mPoleClosedA = closed;
449 break;
450 case 1:
451 **mPoleClosedB = closed;
452 break;
453 case 2:
454 **mPoleClosedC = closed;
455 break;
456 default:
457 throw std::out_of_range("EMT::Ph3::Switch phase index must be 0, 1, or 2.");
458 }
459
460 // Keep the resistance diagnostics synchronized with the discrete
461 // CurrentZero pole state. Exponential mode manages R(t) independently.
462 if (mSwitchingMode == SwitchingMode::CurrentZero &&
463 (**mClosedResistance).rows() >= 3 && (**mOpenResistance).rows() >= 3) {
464 setEffectiveResistance(phase, closed ? (**mClosedResistance)(phase, phase)
465 : (**mOpenResistance)(phase, phase));
466 }
467}
468
469void EMT::Ph3::Switch::setAllPoles(Bool closed) {
470 **mPoleClosedA = closed;
471 **mPoleClosedB = closed;
472 **mPoleClosedC = closed;
473}
474
475Bool EMT::Ph3::Switch::allPolesClosed() const {
476 return **mPoleClosedA && **mPoleClosedB && **mPoleClosedC;
477}
478
479Bool EMT::Ph3::Switch::allPolesOpen() const {
480 return !**mPoleClosedA && !**mPoleClosedB && !**mPoleClosedC;
481}
482
483void EMT::Ph3::Switch::resetZeroCrossingTimes() {
484 **mZeroCrossingTimeA = -1.0;
485 **mZeroCrossingTimeB = -1.0;
486 **mZeroCrossingTimeC = -1.0;
487}
488
489Real EMT::Ph3::Switch::effectiveResistance(UInt phase) const {
490 switch (phase) {
491 case 0:
492 return **mEffectiveResistanceA;
493 case 1:
494 return **mEffectiveResistanceB;
495 case 2:
496 return **mEffectiveResistanceC;
497 default:
498 throw std::out_of_range("EMT::Ph3::Switch phase index must be 0, 1, or 2.");
499 }
500}
501
502void EMT::Ph3::Switch::setEffectiveResistance(UInt phase, Real resistance) {
503 switch (phase) {
504 case 0:
505 **mEffectiveResistanceA = resistance;
506 return;
507 case 1:
508 **mEffectiveResistanceB = resistance;
509 return;
510 case 2:
511 **mEffectiveResistanceC = resistance;
512 return;
513 default:
514 throw std::out_of_range("EMT::Ph3::Switch phase index must be 0, 1, or 2.");
515 }
516}
517
518void EMT::Ph3::Switch::synchronizeEffectiveResistance(Bool closed) {
519 if ((**mClosedResistance).rows() < 3 || (**mOpenResistance).rows() < 3)
520 return;
521
522 for (UInt phase = 0; phase < 3; ++phase) {
523 setEffectiveResistance(phase, closed ? (**mClosedResistance)(phase, phase)
524 : (**mOpenResistance)(phase, phase));
525 }
526}
527
528void EMT::Ph3::Switch::resetExponentialTransition() {
529 **mExponentialTransitionActive = false;
530 **mExponentialTransitionClosing = false;
531 **mExponentialProgress = 0.0;
532 **mExponentialTransitionStartTime = -1.0;
533 **mExponentialTransitionEndTime = -1.0;
534 mExponentialTransitionStarted = false;
535}
536
537void EMT::Ph3::Switch::validateExponentialResistanceParameters() const {
538 if ((**mClosedResistance).rows() < 3 || (**mOpenResistance).rows() < 3) {
539 throw std::invalid_argument(
540 "EMT::Ph3::Switch exponential mode requires 3x3 resistance matrices.");
541 }
542
543 if (mExponentialSwitchingTime <= 0.0) {
544 throw std::invalid_argument(
545 "EMT::Ph3::Switch exponential switching time must be positive.");
546 }
547
548 for (UInt phase = 0; phase < 3; ++phase) {
549 const Real rClosed = (**mClosedResistance)(phase, phase);
550 const Real rOpen = (**mOpenResistance)(phase, phase);
551
552 if (rClosed <= 0.0 || rOpen <= 0.0) {
553 throw std::invalid_argument(
554 "EMT::Ph3::Switch exponential mode requires positive diagonal "
555 "open and closed resistances.");
556 }
557 }
558}
559
560Real EMT::Ph3::Switch::exponentialResistance(UInt phase, Real alpha) const {
561 const Real rClosed = (**mClosedResistance)(phase, phase);
562 const Real rOpen = (**mOpenResistance)(phase, phase);
563 const Real boundedAlpha = std::max(0.0, std::min(1.0, alpha));
564
565 return std::exp(std::log(rClosed) +
566 boundedAlpha * (std::log(rOpen) - std::log(rClosed)));
567}
568
569void EMT::Ph3::Switch::updateExponentialTransition(Real time) {
570 if (!**mExponentialTransitionActive)
571 return;
572
573 if (!mExponentialTransitionStarted) {
574 mExponentialTransitionStarted = true;
575 **mExponentialTransitionStartTime = time;
576 **mExponentialTransitionEndTime = time + mExponentialSwitchingTime;
577 }
578
579 // Prepare R(t+dt) in post-step so that the next MNA solve at t+dt stamps
580 // the intended resistance trajectory without requiring a time-aware event API.
581 const Real targetTime = time + mTimeStep;
582 const Real elapsed = (targetTime - **mExponentialTransitionStartTime) /
583 mExponentialSwitchingTime;
584 const Real boundedElapsed = std::max(0.0, std::min(1.0, elapsed));
585
586 const Bool closing = **mExponentialTransitionClosing;
587
588 // alpha is the position on the resistance path: 0 at R_closed, 1 at R_open.
589 const Real alpha = closing ? 1.0 - boundedElapsed : boundedElapsed;
590
591 **mExponentialProgress = alpha;
592
593 for (UInt phase = 0; phase < 3; ++phase)
594 setEffectiveResistance(phase, exponentialResistance(phase, alpha));
595
596 if (boundedElapsed >= 1.0) {
597 synchronizeEffectiveResistance(closing);
598 setAllPoles(closing);
599 **mOpeningRequested = false;
600 **mExponentialTransitionActive = false;
601 **mExponentialTransitionClosing = false;
602
603 SPDLOG_LOGGER_INFO(
604 mSLog,
605 "Exponential ZCS-emulation {:s} completed: start={:.9f}s, "
606 "end={:.9f}s, duration={:.6e}s.",
607 closing ? "closing" : "opening", **mExponentialTransitionStartTime,
608 **mExponentialTransitionEndTime, mExponentialSwitchingTime);
609 }
610}
611
612MatrixFixedSize<3, 3> EMT::Ph3::Switch::currentConductanceMatrix() const {
613 if (mSwitchingMode == SwitchingMode::Ideal) {
614 MatrixFixedSize<3, 3> conductance = (**mIsClosed)
615 ? (**mClosedResistance).inverse()
616 : (**mOpenResistance).inverse();
617 return conductance;
618 }
619
620 MatrixFixedSize<3, 3> conductance = MatrixFixedSize<3, 3>::Zero();
621
622 for (UInt phase = 0; phase < 3; ++phase) {
623 Real resistance = 0.0;
624
625 if (mSwitchingMode == SwitchingMode::CurrentZero) {
626 resistance = poleClosed(phase) ? (**mClosedResistance)(phase, phase)
627 : (**mOpenResistance)(phase, phase);
628 } else {
629 resistance = effectiveResistance(phase);
630 }
631
632 conductance(phase, phase) = 1.0 / resistance;
633 }
634
635 return conductance;
636}
637
638Bool EMT::Ph3::Switch::currentCrossedZero(Real previousCurrent,
639 Real current) const {
640 if (std::abs(current) <= mZeroCrossingTolerance)
641 return true;
642
643 return (previousCurrent > mZeroCrossingTolerance &&
644 current < -mZeroCrossingTolerance) ||
645 (previousCurrent < -mZeroCrossingTolerance &&
646 current > mZeroCrossingTolerance);
647}
648
649Real EMT::Ph3::Switch::interpolateZeroCrossingTime(Real previousCurrent,
650 Real current,
651 Real previousTime,
652 Real currentTime) const {
653 const Real denominator = std::abs(previousCurrent) + std::abs(current);
654
655 if (denominator <= mZeroCrossingTolerance)
656 return currentTime;
657
658 const Real fraction = std::abs(previousCurrent) / denominator;
659
660 return previousTime + fraction * (currentTime - previousTime);
661}
662
663void EMT::Ph3::Switch::setZeroCrossingTime(UInt phase, Real time) {
664 switch (phase) {
665 case 0:
666 **mZeroCrossingTimeA = time;
667 return;
668 case 1:
669 **mZeroCrossingTimeB = time;
670 return;
671 case 2:
672 **mZeroCrossingTimeC = time;
673 return;
674 default:
675 throw std::out_of_range("EMT::Ph3::Switch phase index must be 0, 1, or 2.");
676 }
677}
678
679void EMT::Ph3::Switch::updateZeroCrossingState(Real time) {
680 const Matrix current = **mIntfCurrent;
681
682 if (current.rows() != 3)
683 return;
684
685 // While no opening request is active, continuously maintain a clean
686 // current history for diagnostics and for a later opening command.
687 if (!**mOpeningRequested) {
688 mPreviousCurrent = current;
689 mPreviousCurrentTime = time;
690 mPreviousCurrentValid = true;
691 return;
692 }
693
694 // The first sample after the opening command must not use a sample from
695 // before the command for a sign-change test. It may still open a pole if
696 // that first post-command sample itself is already at zero.
697 if (mResetZeroCrossingHistory || !mPreviousCurrentValid) {
698 for (UInt phase = 0; phase < 3; ++phase) {
699 if (poleClosed(phase) &&
700 std::abs(current(phase, 0)) <= mZeroCrossingTolerance) {
701 setPoleClosed(phase, false);
702 setZeroCrossingTime(phase, time);
703
704 SPDLOG_LOGGER_INFO(mSLog,
705 "Phase {} opened at sampled current zero: "
706 "t={:.9f}s, i={:.6e}A",
707 phase, time, current(phase, 0));
708 }
709 }
710
711 mPreviousCurrent = current;
712 mPreviousCurrentTime = time;
713 mPreviousCurrentValid = true;
714 mResetZeroCrossingHistory = false;
715
716 if (allPolesOpen())
717 **mOpeningRequested = false;
718
719 return;
720 }
721
722 for (UInt phase = 0; phase < 3; ++phase) {
723 if (!poleClosed(phase))
724 continue;
725
726 const Real previous = mPreviousCurrent(phase, 0);
727 const Real present = current(phase, 0);
728
729 if (!currentCrossedZero(previous, present))
730 continue;
731
732 const Real zeroTime = interpolateZeroCrossingTime(
733 previous, present, mPreviousCurrentTime, time);
734
735 setPoleClosed(phase, false);
736 setZeroCrossingTime(phase, zeroTime);
737
738 SPDLOG_LOGGER_INFO(mSLog,
739 "Phase {} current zero detected: "
740 "t_z={:.9f}s, i_prev={:.6e}A, i={:.6e}A. "
741 "Pole opened.",
742 phase, zeroTime, previous, present);
743 }
744
745 mPreviousCurrent = current;
746 mPreviousCurrentTime = time;
747 mPreviousCurrentValid = true;
748
749 if (allPolesOpen()) {
750 **mOpeningRequested = false;
751
752 SPDLOG_LOGGER_INFO(mSLog, "Current-zero interruption completed: "
753 "all three breaker poles are open.");
754 }
755}
std::vector< Ptr > List
Definition Attribute.h:123
AttributePointer< Attribute< T > > Ptr
Definition Attribute.h:249
const CPS::Attribute< Matrix >::Ptr mClosedResistance
Resistance if switch is closed [ohm].
virtual void closeSwitch()
const CPS::Attribute< Matrix >::Ptr mOpenResistance
Resistance if switch is open [ohm].
virtual void openSwitch()
const CPS::Attribute< Bool >::Ptr mIsClosed
Commanded/global switch state.
void mnaCompUpdateCurrent(const Matrix &leftVector) override
const Attribute< Bool >::Ptr mPoleClosedB
void setSwitchingMode(SwitchingMode mode)
Select switching model. Configure before simulation initialization.
const Attribute< Real >::Ptr mExponentialTransitionStartTime
const Attribute< Real >::Ptr mExponentialProgress
Bool hasParameterChanged() override
Returns true if one of the element paramters has changed.
const Attribute< Real >::Ptr mEffectiveResistanceC
SimPowerComp< Real >::Ptr clone(String name) override
Returns a modified copy of the component with the given suffix added to the name and without.
const Attribute< Bool >::Ptr mOpeningRequested
const Attribute< Bool >::Ptr mPoleClosedC
const Attribute< Real >::Ptr mExponentialTransitionEndTime
void mnaCompApplySwitchSystemMatrixStamp(Bool closed, SparseMatrixRow &systemMatrix, Int freqIdx) override
const Attribute< Real >::Ptr mEffectiveResistanceB
void closeSwitch() override
void mnaCompPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
const Attribute< Real >::Ptr mZeroCrossingTimeB
Switch(String uid, String name, Logger::Level logLevel=Logger::Level::off)
void setExponentialSwitchingTime(Real switchingTime)
Set total transition time for ExponentialZCSEmulation [s].
void mnaCompUpdateVoltage(const Matrix &leftVector) override
void mnaCompInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
void initializeFromNodesAndTerminals(Real frequency) override
Initializes Component variables according to power flow data stored in Nodes.
Bool supportsPrecomputedSystemMatrices() const override
Only Ideal is representable by the conventional two precomputed matrices.
void openSwitch() override
const Attribute< Bool >::Ptr mExponentialTransitionClosing
True while the active exponential transition is a closing transition.
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override
void setZeroCrossingTolerance(Real tolerance)
Absolute current tolerance used by CurrentZero mode [A].
const Attribute< Real >::Ptr mEffectiveResistanceA
Effective phase resistances currently stamped into the MNA matrix [ohm].
const Attribute< Bool >::Ptr mExponentialTransitionActive
Exponential-transition diagnostics.
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
void mnaCompApplyRightSideVectorStamp(Matrix &rightVector) override
const Attribute< Bool >::Ptr mPoleClosedA
Physical pole states.
const Attribute< Real >::Ptr mZeroCrossingTimeC
Bool mnaIsClosed() override
Check if switch is closed.
const Attribute< Real >::Ptr mZeroCrossingTimeA
Estimated physical zero-crossing times [s], -1 if none has been detected.
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
spdlog::level::level_enum Level
Definition Logger.h:33
static String matrixToString(const Matrix &mat)
Definition Logger.cpp:31
static String phasorToString(const Complex &num)
Definition Logger.cpp:57
MNASimPowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
Attribute< Matrix >::Ptr mRightVector
static void stampConductanceMatrix(const Matrix &conductanceMat, SparseMatrixRow &mat, UInt node1Index, UInt node2Index, Bool isTerminal1NotGrounded, Bool isTerminal2NotGrounded, const Logger::Log &mSLog)
static Real realFromVectorElement(const Matrix &mat, Matrix::Index row)
UInt matrixNodeIndex(UInt nodeIndex)
const Attribute< MatrixVar< Real > >::Ptr mIntfCurrent
const Attribute< MatrixVar< Real > >::Ptr mIntfVoltage
std::shared_ptr< SimPowerComp< VarType > > Ptr
Bool terminalNotGrounded(UInt index)
Complex initialSingleVoltage(UInt index)
void setTerminalNumber(UInt num)
Logger::Level mLogLevel
Component logger control for internal variables.
Logger::Log mSLog
Component logger.
static std::shared_ptr< Switch > make(Args &&...args)
Definition PtrFactory.h:19
#define SHIFT_TO_PHASE_C
Definition Definitions.h:47
#define SHIFT_TO_PHASE_B
Definition Definitions.h:46
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
Dense matrix for real numbers.
Definition Definitions.h:81
std::string String
Definition Definitions.h:65
Eigen::Matrix< Real, rows, cols, Eigen::ColMajor > MatrixFixedSize
Dense matrix for real numbers with fixed dimension.
double Real
Definition Definitions.h:62
int Int
Definition Definitions.h:61
Eigen::Matrix< Complex, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > MatrixComp
Dense matrix for complex numbers.
Definition Definitions.h:84
bool Bool
Definition Definitions.h:64
unsigned int UInt
Definition Definitions.h:60
Eigen::SparseMatrix< Real, Eigen::RowMajor > SparseMatrixRow
Sparse matrix for real numbers (row major).
Definition Definitions.h:74