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 VTypeVariableSSNComp::setParameters(aMatrix, bMatrix, cMatrix, dMatrix);
56
57 // Initialize C and f from the current operating point x = 0.
58 updateComponentParameters();
59}
60
61std::pair<Real, Real>
62EMT::Ph3::PiecewiseLinearInductor::slopeAndOffsetFromFlux(Real flux) const {
63 const Real sign = (flux < 0.0) ? -1.0 : 1.0;
64 const Real absFlux = std::abs(flux);
65
66 size_t segment = mFluxBreakpoints.size() - 2;
67 for (size_t k = 0; k + 1 < mFluxBreakpoints.size(); ++k) {
68 if (absFlux <= mFluxBreakpoints[k + 1]) {
69 segment = k;
70 break;
71 }
72 }
73
74 const Real dFlux = mFluxBreakpoints[segment + 1] - mFluxBreakpoints[segment];
75 const Real dCurrent =
76 mCurrentBreakpoints[segment + 1] - mCurrentBreakpoints[segment];
77
78 const Real slope = dCurrent / dFlux;
79 const Real intercept =
80 mCurrentBreakpoints[segment] - slope * mFluxBreakpoints[segment];
81
82 return std::make_pair(slope, sign * intercept);
83}
84
86 Matrix newC = Matrix::Zero(3, 3);
87 Matrix newF = Matrix::Zero(3, 1);
88
89 for (Int phase = 0; phase < 3; ++phase) {
90 const Real flux = (**mX)(phase, 0);
91 const auto [slope, offset] = slopeAndOffsetFromFlux(flux);
92
93 newC(phase, phase) = slope;
94 newF(phase, 0) = offset;
95 }
96
97 const Bool changed = (!mC.isApprox(newC)) || (!outputOffset().isApprox(newF));
98
99 if (changed) {
100 mC = newC;
101 setOutputOffset(newF);
102 }
103
104 return changed;
105}
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.