DPsim
Loading...
Searching...
No Matches
EMT_Ph3_PiecewiseLinearInductor.cpp
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 <cmath>
5#include <stdexcept>
6
7#include <dpsim-models/EMT/EMT_Ph3_PiecewiseLinearInductor.h>
8
9using namespace CPS;
10
11EMT::Ph3::PiecewiseLinearInductor::PiecewiseLinearInductor(
12 String uid, String name, Logger::Level logLevel)
13 : TwoTerminalVTypeVariableSSNComp(uid, name, logLevel) {}
14
15SimPowerComp<Real>::Ptr EMT::Ph3::PiecewiseLinearInductor::clone(String name) {
16 auto copy = SharedFactory<PiecewiseLinearInductor>::make(name, mLogLevel);
17 copy->setParameters(mFluxBreakpoints, mCurrentBreakpoints);
18 return copy;
19}
20
21void EMT::Ph3::PiecewiseLinearInductor::setParameters(
22 const std::vector<Real> &fluxBreakpoints,
23 const std::vector<Real> &currentBreakpoints) {
24 if (fluxBreakpoints.size() < 2)
25 throw std::invalid_argument("At least two flux breakpoints are required.");
26
27 if (fluxBreakpoints.size() != currentBreakpoints.size())
28 throw std::invalid_argument(
29 "Flux and current breakpoint vectors must have the same size.");
30
31 constexpr Real originTolerance = 1e-12;
32
33 if (std::abs(fluxBreakpoints.front()) > originTolerance ||
34 std::abs(currentBreakpoints.front()) > originTolerance)
35 throw std::invalid_argument(
36 "Piecewise-linear characteristic must start at the origin.");
37
38 for (size_t k = 0; k + 1 < fluxBreakpoints.size(); ++k) {
39 if (fluxBreakpoints[k + 1] <= fluxBreakpoints[k])
40 throw std::invalid_argument(
41 "Flux breakpoints must be strictly increasing.");
42 if (currentBreakpoints[k + 1] <= currentBreakpoints[k])
43 throw std::invalid_argument(
44 "Current breakpoints must be strictly increasing.");
45 }
46
47 mFluxBreakpoints = fluxBreakpoints;
48 mCurrentBreakpoints = currentBreakpoints;
49
50 Matrix aMatrix = Matrix::Zero(3, 3);
51 Matrix bMatrix = Matrix::Identity(3, 3);
52 Matrix cMatrix = Matrix::Zero(3, 3);
53 Matrix dMatrix = Matrix::Zero(3, 3);
54
55 SSNComp::setParameters(aMatrix, bMatrix, cMatrix, dMatrix);
56 setOutputOffset(Matrix::Zero(3, 1));
57
58 // Initialize C and f from the current operating point x = 0.
59 updateComponentParameters();
60}
61
62std::pair<Real, Real>
63EMT::Ph3::PiecewiseLinearInductor::slopeAndOffsetFromFlux(Real flux) const {
64 const Real sign = (flux < 0.0) ? -1.0 : 1.0;
65 const Real absFlux = std::abs(flux);
66
67 size_t segment = mFluxBreakpoints.size() - 2;
68 for (size_t k = 0; k + 1 < mFluxBreakpoints.size(); ++k) {
69 if (absFlux <= mFluxBreakpoints[k + 1]) {
70 segment = k;
71 break;
72 }
73 }
74
75 const Real dFlux = mFluxBreakpoints[segment + 1] - mFluxBreakpoints[segment];
76 const Real dCurrent =
77 mCurrentBreakpoints[segment + 1] - mCurrentBreakpoints[segment];
78
79 const Real slope = dCurrent / dFlux;
80 const Real intercept =
81 mCurrentBreakpoints[segment] - slope * mFluxBreakpoints[segment];
82
83 return std::make_pair(slope, sign * intercept);
84}
85
87 Matrix newC = Matrix::Zero(3, 3);
88 Matrix newF = Matrix::Zero(3, 1);
89
90 for (Int phase = 0; phase < 3; ++phase) {
91 const Real flux = (**mX)(phase, 0);
92 const auto [slope, offset] = slopeAndOffsetFromFlux(flux);
93
94 newC(phase, phase) = slope;
95 newF(phase, 0) = offset;
96 }
97
98 const Bool changed = (!mC.isApprox(newC)) || (!outputOffset().isApprox(newF));
99
100 if (changed) {
101 mC = newC;
102 setOutputOffset(newF);
103 }
104
105 return changed;
106}
SimPowerComp< Real >::Ptr clone(String name) override final
Returns a modified copy of the component with the given suffix added to the name and without.
Logger::Level mLogLevel
Component logger control for internal variables.