DPsim
Loading...
Searching...
No Matches
DP_Ph3_Switch.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Institute for Automation of Complex Power Systems, EONERC, RWTH Aachen University
2// SPDX-License-Identifier: MPL-2.0
3
4#include <algorithm>
5#include <cmath>
6#include <stdexcept>
7
9
10using namespace CPS;
11
13 : MNASimPowerComp<Complex>(uid, name, false, true, logLevel),
15 mOpeningRequested(mAttributes->create<Bool>("opening_requested")),
16 mPoleClosedA(mAttributes->create<Bool>("pole_closed_a")),
17 mPoleClosedB(mAttributes->create<Bool>("pole_closed_b")),
18 mPoleClosedC(mAttributes->create<Bool>("pole_closed_c")),
19 mInstantCurrentA(mAttributes->create<Real>("i_instantaneous_a")),
20 mInstantCurrentB(mAttributes->create<Real>("i_instantaneous_b")),
21 mInstantCurrentC(mAttributes->create<Real>("i_instantaneous_c")),
22 mZeroCrossingTimeA(mAttributes->create<Real>("zero_crossing_time_a")),
23 mZeroCrossingTimeB(mAttributes->create<Real>("zero_crossing_time_b")),
24 mZeroCrossingTimeC(mAttributes->create<Real>("zero_crossing_time_c")),
26 mAttributes->create<Bool>("exponential_transition_active")),
28 mAttributes->create<Bool>("exponential_transition_closing")),
29 mExponentialProgress(mAttributes->create<Real>("exponential_progress")),
31 mAttributes->create<Real>("exponential_transition_start_time")),
33 mAttributes->create<Real>("exponential_transition_end_time")),
35 mAttributes->create<Real>("effective_resistance_a")),
37 mAttributes->create<Real>("effective_resistance_b")),
39 mAttributes->create<Real>("effective_resistance_c")) {
42
43 **mIntfVoltage = MatrixComp::Zero(3, 1);
44 **mIntfCurrent = MatrixComp::Zero(3, 1);
45
46 **mOpeningRequested = false;
47 setAllPoles(false);
48
49 **mInstantCurrentA = 0.0;
50 **mInstantCurrentB = 0.0;
51 **mInstantCurrentC = 0.0;
52
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 setAllPoles(**mIsClosed);
78 **mOpeningRequested = false;
79 resetZeroCrossingTimes();
80
81 mPreviousCurrentValid = false;
82 mResetZeroCrossingHistory = false;
83
84 resetExponentialTransition();
85 synchronizeEffectiveResistance(**mIsClosed);
86}
87
89 if (tolerance < 0.0) {
90 throw std::invalid_argument(
91 "DP::Ph3::Switch zero-crossing tolerance must be non-negative.");
92 }
93
94 mZeroCrossingTolerance = tolerance;
95}
96
98 if (switchingTime <= 0.0) {
99 throw std::invalid_argument(
100 "DP::Ph3::Switch exponential switching time must be positive.");
101 }
102
103 mExponentialSwitchingTime = switchingTime;
104}
105
108
109 // An opening command during a closing ramp abandons that ramp. The poles are
110 // still open at that point, so the switch simply stays open.
112 resetExponentialTransition();
113 setAllPoles(false);
114 synchronizeEffectiveResistance(false);
115 **mOpeningRequested = false;
116
117 SPDLOG_LOGGER_INFO(mSLog,
118 "DP 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(
144 mSLog, "DP current-zero opening command received. "
145 "Waiting for reconstructed physical phase-current zeros.");
146 return;
147 }
148
149 validateExponentialResistanceParameters();
150 **mOpeningRequested = true;
151 resetZeroCrossingTimes();
152 resetExponentialTransition();
154 synchronizeEffectiveResistance(true);
155 setAllPoles(true);
156
157 SPDLOG_LOGGER_INFO(mSLog,
158 "DP exponential ZCS-emulation opening command received. "
159 "Switching duration={:.6e}s.",
160 mExponentialSwitchingTime);
161}
162
165
166 **mOpeningRequested = false;
167 resetZeroCrossingTimes();
168 mResetZeroCrossingHistory = false;
169
170 if (mSwitchingMode != SwitchingMode::ExponentialZCSEmulation) {
171 setAllPoles(true);
172 resetExponentialTransition();
173 synchronizeEffectiveResistance(true);
174
175 SPDLOG_LOGGER_INFO(
176 mSLog, "DP switch closing command: all three physical poles closed.");
177 return;
178 }
179
180 if (allPolesClosed() && !**mExponentialTransitionActive) {
181 resetExponentialTransition();
182 synchronizeEffectiveResistance(true);
183 return;
184 }
185
186 // Energising a branch is the mirror image of interrupting it: the same
187 // resistance path is traversed from R_open towards R_closed. The DP envelope
188 // cannot represent the voltage step across a capacitance any more than it can
189 // represent an interrupted inductive current, so closing is ramped as well.
190 validateExponentialResistanceParameters();
191 resetExponentialTransition();
194 **mExponentialProgress = 1.0;
195 synchronizeEffectiveResistance(false);
196 setAllPoles(false);
197
198 SPDLOG_LOGGER_INFO(mSLog,
199 "DP exponential ZCS-emulation closing command received. "
200 "Switching duration={:.6e}s.",
201 mExponentialSwitchingTime);
202}
203
205 mTerminals[0]->setPhaseType(PhaseType::ABC);
206 mTerminals[1]->setPhaseType(PhaseType::ABC);
207
208 Matrix impedance = (**mIsClosed) ? **mClosedResistance : **mOpenResistance;
209
210 // DP::Ph3 MNA quantities are phase-peak complex envelopes. Power-flow
211 // node voltages are line-line RMS, so convert the A-phase voltage to
212 // phase peak and construct the balanced B/C envelopes explicitly.
213 MatrixComp vInitABC = MatrixComp::Zero(3, 1);
214 vInitABC(0, 0) =
216 vInitABC(1, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_B;
217 vInitABC(2, 0) = vInitABC(0, 0) * SHIFT_TO_PHASE_C;
218
219 **mIntfVoltage = vInitABC;
220 **mIntfCurrent = impedance.inverse() * vInitABC;
221
222 setAllPoles(**mIsClosed);
223 **mOpeningRequested = false;
224 resetZeroCrossingTimes();
225 resetExponentialTransition();
226 synchronizeEffectiveResistance(**mIsClosed);
227
228 SPDLOG_LOGGER_INFO(mSLog,
229 "\n--- Initialization from powerflow ---"
230 "\nVoltage envelope:"
231 "\n{:s}"
232 "\nCurrent envelope:"
233 "\n{:s}"
234 "\nTerminal 0 voltage envelope:"
235 "\n{:s}"
236 "\nTerminal 1 voltage envelope:"
237 "\n{:s}"
238 "\nSwitching mode: {:s}"
239 "\nInitial breaker state: {:s}"
240 "\n--- Initialization from powerflow finished ---",
245 mSwitchingMode == SwitchingMode::Ideal
246 ? "Ideal"
247 : (mSwitchingMode == SwitchingMode::CurrentZero
248 ? "CurrentZero"
249 : "ExponentialZCSEmulation"),
250 **mIsClosed ? "closed" : "open");
251}
252
254 Attribute<Matrix>::Ptr leftVector) {
256 mTimeStep = timeStep;
257 **mRightVector = Matrix::Zero(0, 0);
258
259 // omega is the DP reference-frame angular frequency supplied by the solver.
260 mShiftOmega = omega;
261
262 // Final safeguard in case setSwitchingMode() was called before setParameters().
263 setAllPoles(**mIsClosed);
264 synchronizeEffectiveResistance(**mIsClosed);
265 resetExponentialTransition();
266
267 mPoleClosedPrev = {{
268 **mPoleClosedA,
269 **mPoleClosedB,
270 **mPoleClosedC,
271 }};
272
274
275 const Matrix instantaneous = reconstructInstantaneousCurrent(0.0);
276
277 setInstantaneousCurrentAttributes(instantaneous);
278
279 mPreviousInstantaneousCurrent = instantaneous;
280 mEffectiveResistancePrev = {{
284 }};
285
286 mPreviousCurrentTime = 0.0;
287 mPreviousCurrentValid = true;
288 mResetZeroCrossingHistory = false;
289}
290
292 if (mSwitchingMode == SwitchingMode::Ideal)
293 return **mIsClosed;
294
295 return allPolesClosed();
296}
297
301
303 SparseMatrixRow &systemMatrix) {
304 const MatrixFixedSizeComp<3, 3> conductance = currentConductanceMatrix();
305
307 conductance, systemMatrix, matrixNodeIndex(0), matrixNodeIndex(1),
309}
310
312 Bool closed, SparseMatrixRow &systemMatrix, Int freqIdx) {
313 // Conventional precomputed two-state path. CurrentZero mode bypasses this
314 // by returning supportsPrecomputedSystemMatrices() == false.
316
317 conductance.real() =
318 closed ? (**mClosedResistance).inverse() : (**mOpenResistance).inverse();
319
321 conductance, systemMatrix, matrixNodeIndex(0), matrixNodeIndex(1),
323}
324
326 AttributeBase::List &prevStepDependencies,
327 AttributeBase::List &attributeDependencies,
328 AttributeBase::List &modifiedAttributes,
329 Attribute<Matrix>::Ptr &leftVector) {
330 attributeDependencies.push_back(leftVector);
331
332 modifiedAttributes.push_back(mIntfVoltage);
333 modifiedAttributes.push_back(mIntfCurrent);
334
335 modifiedAttributes.push_back(mOpeningRequested);
336 modifiedAttributes.push_back(mPoleClosedA);
337 modifiedAttributes.push_back(mPoleClosedB);
338 modifiedAttributes.push_back(mPoleClosedC);
339
340 modifiedAttributes.push_back(mInstantCurrentA);
341 modifiedAttributes.push_back(mInstantCurrentB);
342 modifiedAttributes.push_back(mInstantCurrentC);
343
344 modifiedAttributes.push_back(mZeroCrossingTimeA);
345 modifiedAttributes.push_back(mZeroCrossingTimeB);
346 modifiedAttributes.push_back(mZeroCrossingTimeC);
347
348 modifiedAttributes.push_back(mExponentialTransitionActive);
349 modifiedAttributes.push_back(mExponentialTransitionClosing);
350 modifiedAttributes.push_back(mExponentialProgress);
351 modifiedAttributes.push_back(mExponentialTransitionStartTime);
352 modifiedAttributes.push_back(mExponentialTransitionEndTime);
353 modifiedAttributes.push_back(mEffectiveResistanceA);
354 modifiedAttributes.push_back(mEffectiveResistanceB);
355 modifiedAttributes.push_back(mEffectiveResistanceC);
356}
357
359 Attribute<Matrix>::Ptr &leftVector) {
360 mnaCompUpdateVoltage(**leftVector);
361 mnaCompUpdateCurrent(**leftVector);
362
363 const Matrix instantaneous = reconstructInstantaneousCurrent(time);
364
365 setInstantaneousCurrentAttributes(instantaneous);
366
367 if (mSwitchingMode == SwitchingMode::CurrentZero) {
368 updateZeroCrossingState(time);
369 } else if (mSwitchingMode == SwitchingMode::ExponentialZCSEmulation) {
370 updateExponentialTransition(time);
371 }
372}
373
375 **mIntfVoltage = MatrixComp::Zero(3, 1);
376
377 if (terminalNotGrounded(1)) {
378 (**mIntfVoltage)(0, 0) =
380 (**mIntfVoltage)(1, 0) =
382 (**mIntfVoltage)(2, 0) =
384 }
385
386 if (terminalNotGrounded(0)) {
387 (**mIntfVoltage)(0, 0) -=
389 (**mIntfVoltage)(1, 0) -=
391 (**mIntfVoltage)(2, 0) -=
393 }
394}
395
397 **mIntfCurrent = currentConductanceMatrix() * **mIntfVoltage;
398}
399
401 if (mSwitchingMode == SwitchingMode::Ideal) {
402 if (mIsClosedPrev != **mIsClosed) {
404 return true;
405 }
406
407 return false;
408 }
409
410 if (mSwitchingMode == SwitchingMode::ExponentialZCSEmulation) {
411 const std::array<Real, 3> resistanceNow{{
415 }};
416
417 Bool changed = false;
418 for (UInt phase = 0; phase < 3; ++phase) {
419 if (resistanceNow[phase] != mEffectiveResistancePrev[phase]) {
420 mEffectiveResistancePrev[phase] = resistanceNow[phase];
421 changed = true;
422 }
423 }
424 return changed;
425 }
426
427 const std::array<Bool, 3> poleClosedNow{{
428 **mPoleClosedA,
429 **mPoleClosedB,
430 **mPoleClosedC,
431 }};
432
433 Bool changed = false;
434
435 for (UInt phase = 0; phase < 3; ++phase) {
436 if (poleClosedNow[phase] != mPoleClosedPrev[phase]) {
437 mPoleClosedPrev[phase] = poleClosedNow[phase];
438 changed = true;
439 }
440 }
441
442 return changed;
443}
444
445Bool DP::Ph3::Switch::poleClosed(UInt phase) const {
446 switch (phase) {
447 case 0:
448 return **mPoleClosedA;
449 case 1:
450 return **mPoleClosedB;
451 case 2:
452 return **mPoleClosedC;
453 default:
454 throw std::out_of_range("DP::Ph3::Switch phase index must be 0, 1, or 2.");
455 }
456}
457
458void DP::Ph3::Switch::setPoleClosed(UInt phase, Bool closed) {
459 switch (phase) {
460 case 0:
461 **mPoleClosedA = closed;
462 break;
463 case 1:
464 **mPoleClosedB = closed;
465 break;
466 case 2:
467 **mPoleClosedC = closed;
468 break;
469 default:
470 throw std::out_of_range("DP::Ph3::Switch phase index must be 0, 1, or 2.");
471 }
472
473 // Keep the resistance diagnostics synchronized with the discrete
474 // CurrentZero pole state. Exponential mode manages R(t) independently.
475 if (mSwitchingMode == SwitchingMode::CurrentZero &&
476 (**mClosedResistance).rows() >= 3 && (**mOpenResistance).rows() >= 3) {
477 setEffectiveResistance(phase, closed ? (**mClosedResistance)(phase, phase)
478 : (**mOpenResistance)(phase, phase));
479 }
480}
481
482void DP::Ph3::Switch::setAllPoles(Bool closed) {
483 **mPoleClosedA = closed;
484 **mPoleClosedB = closed;
485 **mPoleClosedC = closed;
486}
487
488Bool DP::Ph3::Switch::allPolesClosed() const {
489 return **mPoleClosedA && **mPoleClosedB && **mPoleClosedC;
490}
491
492Bool DP::Ph3::Switch::allPolesOpen() const {
493 return !**mPoleClosedA && !**mPoleClosedB && !**mPoleClosedC;
494}
495
496void DP::Ph3::Switch::resetZeroCrossingTimes() {
497 **mZeroCrossingTimeA = -1.0;
498 **mZeroCrossingTimeB = -1.0;
499 **mZeroCrossingTimeC = -1.0;
500}
501
502Real DP::Ph3::Switch::effectiveResistance(UInt phase) const {
503 switch (phase) {
504 case 0:
505 return **mEffectiveResistanceA;
506 case 1:
507 return **mEffectiveResistanceB;
508 case 2:
509 return **mEffectiveResistanceC;
510 default:
511 throw std::out_of_range("DP::Ph3::Switch phase index must be 0, 1, or 2.");
512 }
513}
514
515void DP::Ph3::Switch::setEffectiveResistance(UInt phase, Real resistance) {
516 switch (phase) {
517 case 0:
518 **mEffectiveResistanceA = resistance;
519 return;
520 case 1:
521 **mEffectiveResistanceB = resistance;
522 return;
523 case 2:
524 **mEffectiveResistanceC = resistance;
525 return;
526 default:
527 throw std::out_of_range("DP::Ph3::Switch phase index must be 0, 1, or 2.");
528 }
529}
530
531void DP::Ph3::Switch::synchronizeEffectiveResistance(Bool closed) {
532 if ((**mClosedResistance).rows() < 3 || (**mOpenResistance).rows() < 3)
533 return;
534
535 for (UInt phase = 0; phase < 3; ++phase) {
536 setEffectiveResistance(phase, closed ? (**mClosedResistance)(phase, phase)
537 : (**mOpenResistance)(phase, phase));
538 }
539}
540
541void DP::Ph3::Switch::resetExponentialTransition() {
542 **mExponentialTransitionActive = false;
543 **mExponentialTransitionClosing = false;
544 **mExponentialProgress = 0.0;
545 **mExponentialTransitionStartTime = -1.0;
546 **mExponentialTransitionEndTime = -1.0;
547 mExponentialTransitionStarted = false;
548}
549
550void DP::Ph3::Switch::validateExponentialResistanceParameters() const {
551 if ((**mClosedResistance).rows() < 3 || (**mOpenResistance).rows() < 3) {
552 throw std::invalid_argument(
553 "DP::Ph3::Switch exponential mode requires 3x3 resistance matrices.");
554 }
555
556 if (mExponentialSwitchingTime <= 0.0) {
557 throw std::invalid_argument(
558 "DP::Ph3::Switch exponential switching time must be positive.");
559 }
560
561 for (UInt phase = 0; phase < 3; ++phase) {
562 const Real rClosed = (**mClosedResistance)(phase, phase);
563 const Real rOpen = (**mOpenResistance)(phase, phase);
564
565 if (rClosed <= 0.0 || rOpen <= 0.0) {
566 throw std::invalid_argument(
567 "DP::Ph3::Switch exponential mode requires positive diagonal "
568 "open and closed resistances.");
569 }
570 }
571}
572
573Real DP::Ph3::Switch::exponentialResistance(UInt phase, Real alpha) const {
574 const Real rClosed = (**mClosedResistance)(phase, phase);
575 const Real rOpen = (**mOpenResistance)(phase, phase);
576 const Real boundedAlpha = std::max(0.0, std::min(1.0, alpha));
577
578 return std::exp(std::log(rClosed) +
579 boundedAlpha * (std::log(rOpen) - std::log(rClosed)));
580}
581
582void DP::Ph3::Switch::updateExponentialTransition(Real time) {
583 if (!**mExponentialTransitionActive)
584 return;
585
586 if (!mExponentialTransitionStarted) {
587 mExponentialTransitionStarted = true;
588 **mExponentialTransitionStartTime = time;
589 **mExponentialTransitionEndTime = time + mExponentialSwitchingTime;
590 }
591
592 const Real targetTime = time + mTimeStep;
593 const Real elapsed = (targetTime - **mExponentialTransitionStartTime) /
594 mExponentialSwitchingTime;
595 const Real boundedElapsed = std::max(0.0, std::min(1.0, elapsed));
596
597 const Bool closing = **mExponentialTransitionClosing;
598
599 // alpha is the position on the resistance path: 0 at R_closed, 1 at R_open.
600 const Real alpha = closing ? 1.0 - boundedElapsed : boundedElapsed;
601
602 **mExponentialProgress = alpha;
603
604 for (UInt phase = 0; phase < 3; ++phase)
605 setEffectiveResistance(phase, exponentialResistance(phase, alpha));
606
607 if (boundedElapsed >= 1.0) {
608 synchronizeEffectiveResistance(closing);
609 setAllPoles(closing);
610 **mOpeningRequested = false;
611 **mExponentialTransitionActive = false;
612 **mExponentialTransitionClosing = false;
613
614 SPDLOG_LOGGER_INFO(
615 mSLog,
616 "DP exponential ZCS-emulation {:s} completed: start={:.9f}s, "
617 "end={:.9f}s, duration={:.6e}s.",
618 closing ? "closing" : "opening", **mExponentialTransitionStartTime,
619 **mExponentialTransitionEndTime, mExponentialSwitchingTime);
620 }
621}
622
623MatrixFixedSizeComp<3, 3> DP::Ph3::Switch::currentConductanceMatrix() const {
624 MatrixFixedSizeComp<3, 3> conductance = MatrixFixedSizeComp<3, 3>::Zero();
625
626 if (mSwitchingMode == SwitchingMode::Ideal) {
627 conductance.real() = (**mIsClosed) ? (**mClosedResistance).inverse()
628 : (**mOpenResistance).inverse();
629 return conductance;
630 }
631
632 for (UInt phase = 0; phase < 3; ++phase) {
633 Real resistance = 0.0;
634
635 if (mSwitchingMode == SwitchingMode::CurrentZero) {
636 resistance = poleClosed(phase) ? (**mClosedResistance)(phase, phase)
637 : (**mOpenResistance)(phase, phase);
638 } else {
639 resistance = effectiveResistance(phase);
640 }
641
642 conductance(phase, phase) = Complex(1.0 / resistance, 0.0);
643 }
644
645 return conductance;
646}
647
648Matrix DP::Ph3::Switch::reconstructInstantaneousCurrent(Real time) const {
649 Matrix instantaneous = Matrix::Zero(3, 1);
650
651 // DP::Ph3 quantities are phase-peak complex envelopes. Restore the removed
652 // reference-frequency carrier; no additional RMS-to-peak scaling is needed.
653 const Complex carrier = std::polar<Real>(1.0, mShiftOmega * time);
654
655 for (UInt phase = 0; phase < 3; ++phase) {
656 instantaneous(phase, 0) = std::real((**mIntfCurrent)(phase, 0) * carrier);
657 }
658
659 return instantaneous;
660}
661
662void DP::Ph3::Switch::setInstantaneousCurrentAttributes(const Matrix &current) {
663 **mInstantCurrentA = current(0, 0);
664 **mInstantCurrentB = current(1, 0);
665 **mInstantCurrentC = current(2, 0);
666}
667
668Bool DP::Ph3::Switch::currentCrossedZero(Real previousCurrent,
669 Real current) const {
670 if (std::abs(current) <= mZeroCrossingTolerance)
671 return true;
672
673 return (previousCurrent > mZeroCrossingTolerance &&
674 current < -mZeroCrossingTolerance) ||
675 (previousCurrent < -mZeroCrossingTolerance &&
676 current > mZeroCrossingTolerance);
677}
678
679Real DP::Ph3::Switch::interpolateZeroCrossingTime(Real previousCurrent,
680 Real current,
681 Real previousTime,
682 Real currentTime) const {
683 const Real denominator = std::abs(previousCurrent) + std::abs(current);
684
685 if (denominator <= mZeroCrossingTolerance)
686 return currentTime;
687
688 const Real fraction = std::abs(previousCurrent) / denominator;
689
690 return previousTime + fraction * (currentTime - previousTime);
691}
692
693void DP::Ph3::Switch::setZeroCrossingTime(UInt phase, Real time) {
694 switch (phase) {
695 case 0:
696 **mZeroCrossingTimeA = time;
697 return;
698 case 1:
699 **mZeroCrossingTimeB = time;
700 return;
701 case 2:
702 **mZeroCrossingTimeC = time;
703 return;
704 default:
705 throw std::out_of_range("DP::Ph3::Switch phase index must be 0, 1, or 2.");
706 }
707}
708
709void DP::Ph3::Switch::updateZeroCrossingState(Real time) {
710 const Matrix current = reconstructInstantaneousCurrent(time);
711
712 // Keep a current history while no opening command is active.
713 if (!**mOpeningRequested) {
714 mPreviousInstantaneousCurrent = current;
715 mPreviousCurrentTime = time;
716 mPreviousCurrentValid = true;
717 return;
718 }
719
720 // First post-command sample: do not compare against the pre-command sample.
721 if (mResetZeroCrossingHistory || !mPreviousCurrentValid) {
722 for (UInt phase = 0; phase < 3; ++phase) {
723 if (poleClosed(phase) &&
724 std::abs(current(phase, 0)) <= mZeroCrossingTolerance) {
725 setPoleClosed(phase, false);
726 setZeroCrossingTime(phase, time);
727
728 SPDLOG_LOGGER_INFO(
729 mSLog,
730 "DP phase {} opened at sampled physical current zero: "
731 "t={:.9f}s, i={:.6e}A",
732 phase, time, current(phase, 0));
733 }
734 }
735
736 mPreviousInstantaneousCurrent = current;
737 mPreviousCurrentTime = time;
738 mPreviousCurrentValid = true;
739 mResetZeroCrossingHistory = false;
740
741 if (allPolesOpen())
742 **mOpeningRequested = false;
743
744 return;
745 }
746
747 for (UInt phase = 0; phase < 3; ++phase) {
748 if (!poleClosed(phase))
749 continue;
750
751 const Real previous = mPreviousInstantaneousCurrent(phase, 0);
752 const Real present = current(phase, 0);
753
754 if (!currentCrossedZero(previous, present))
755 continue;
756
757 const Real zeroTime = interpolateZeroCrossingTime(
758 previous, present, mPreviousCurrentTime, time);
759
760 setPoleClosed(phase, false);
761 setZeroCrossingTime(phase, zeroTime);
762
763 SPDLOG_LOGGER_INFO(mSLog,
764 "DP phase {} physical current zero detected: "
765 "t_z={:.9f}s, i_prev={:.6e}A, i={:.6e}A. "
766 "Pole opened.",
767 phase, zeroTime, previous, present);
768 }
769
770 mPreviousInstantaneousCurrent = current;
771 mPreviousCurrentTime = time;
772 mPreviousCurrentValid = true;
773
774 if (allPolesOpen()) {
775 **mOpeningRequested = false;
776
777 SPDLOG_LOGGER_INFO(mSLog, "DP current-zero interruption completed: "
778 "all three physical breaker poles are open.");
779 }
780}
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.
const Attribute< Bool >::Ptr mPoleClosedA
Bool hasParameterChanged() override
Returns true if one of the element paramters has changed.
void closeSwitch() override
const Attribute< Bool >::Ptr mPoleClosedB
const Attribute< Real >::Ptr mInstantCurrentA
Reconstructed physical instantaneous currents [A].
void mnaCompUpdateCurrent(const Matrix &leftVector) override
void setZeroCrossingTolerance(Real tolerance)
void setExponentialSwitchingTime(Real switchingTime)
const Attribute< Bool >::Ptr mExponentialTransitionActive
Exponential-transition diagnostics.
const Attribute< Real >::Ptr mEffectiveResistanceA
Effective phase resistances currently stamped into the MNA matrix [ohm].
const Attribute< Real >::Ptr mExponentialTransitionStartTime
const Attribute< Bool >::Ptr mExponentialTransitionClosing
True while the active exponential transition is a closing transition.
void mnaCompUpdateVoltage(const Matrix &leftVector) override
Switch(String uid, String name, Logger::Level loglevel=Logger::Level::off)
const Attribute< Real >::Ptr mZeroCrossingTimeB
Bool mnaIsClosed() override
Check if switch is closed.
void setSwitchingMode(SwitchingMode mode)
const Attribute< Real >::Ptr mExponentialTransitionEndTime
const Attribute< Real >::Ptr mZeroCrossingTimeA
const Attribute< Bool >::Ptr mPoleClosedC
void openSwitch() override
Bool supportsPrecomputedSystemMatrices() const 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 mnaCompPostStep(Real time, Int timeStepCount, Attribute< Matrix >::Ptr &leftVector) override
const Attribute< Real >::Ptr mInstantCurrentC
const Attribute< Real >::Ptr mEffectiveResistanceB
const Attribute< Real >::Ptr mInstantCurrentB
void initializeFromNodesAndTerminals(Real frequency) override
Initializes Component variables according to power flow data stored in Nodes.
void mnaCompApplySystemMatrixStamp(SparseMatrixRow &systemMatrix) override
const Attribute< Bool >::Ptr mOpeningRequested
void mnaCompApplySwitchSystemMatrixStamp(Bool closed, SparseMatrixRow &systemMatrix, Int freqIdx) override
void mnaCompAddPostStepDependencies(AttributeBase::List &prevStepDependencies, AttributeBase::List &attributeDependencies, AttributeBase::List &modifiedAttributes, Attribute< Matrix >::Ptr &leftVector) override
void mnaCompInitialize(Real omega, Real timeStep, Attribute< Matrix >::Ptr leftVector) override
const Attribute< Real >::Ptr mEffectiveResistanceC
const Attribute< Real >::Ptr mExponentialProgress
const Attribute< Real >::Ptr mZeroCrossingTimeC
String uid()
Returns unique id.
AttributeList::Ptr mAttributes
Attribute List.
spdlog::level::level_enum Level
Definition Logger.h:33
static String phasorMatrixToString(const MatrixComp &mat)
Definition Logger.cpp:51
MNASimPowerComp(String uid, String name, Bool hasPreStep, Bool hasPostStep, Logger::Level logLevel)
Attribute< Matrix >::Ptr mRightVector
static void stampAdmittanceMatrix(const MatrixComp &admittanceMat, SparseMatrixRow &mat, UInt node1Index, UInt node2Index, Bool isTerminal1NotGrounded, Bool isTerminal2NotGrounded, const Logger::Log &mSLog, Int maxFreq=1, Int freqIdx=0)
static Complex complexFromVectorElement(const Matrix &mat, Matrix::Index row, Int maxFreq=1, Int freqIdx=0)
Definition MathUtils.cpp:94
UInt matrixNodeIndex(UInt nodeIndex)
const Attribute< MatrixVar< Complex > >::Ptr mIntfCurrent
SimTerminal< Complex >::List mTerminals
const Attribute< MatrixVar< Complex > >::Ptr mIntfVoltage
MatrixComp initialVoltage(UInt index)
std::shared_ptr< SimPowerComp< VarType > > Ptr
Bool terminalNotGrounded(UInt index)
Complex initialSingleVoltage(UInt index)
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 RMS3PH_TO_PEAK1PH
Definition Definitions.h:50
#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
double Real
Definition Definitions.h:62
int Int
Definition Definitions.h:61
std::complex< Real > Complex
Definition Definitions.h:63
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::Matrix< Complex, rows, cols, Eigen::ColMajor > MatrixFixedSizeComp
Dense matrix for complex numbers with fixed dimension.
Eigen::SparseMatrix< Real, Eigen::RowMajor > SparseMatrixRow
Sparse matrix for real numbers (row major).
Definition Definitions.h:74