Skip to content

Commit 0b63a2b

Browse files
authored
Make ModelicaSystem.linearize() return all values (#266)
1 parent 1c50417 commit 0b63a2b

File tree

3 files changed

+98
-10
lines changed

3 files changed

+98
-10
lines changed

OMPython/ModelicaSystem.py

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import numpy as np
4444
import importlib
4545
import pathlib
46+
from dataclasses import dataclass
47+
from typing import Optional
4648

4749
from OMPython.OMCSession import OMCSessionBase, OMCSessionZMQ
4850

@@ -54,6 +56,57 @@ class ModelicaSystemError(Exception):
5456
pass
5557

5658

59+
@dataclass
60+
class LinearizationResult:
61+
"""Modelica model linearization results.
62+
63+
Attributes:
64+
n: number of states
65+
m: number of inputs
66+
p: number of outputs
67+
A: state matrix (n x n)
68+
B: input matrix (n x m)
69+
C: output matrix (p x n)
70+
D: feedthrough matrix (p x m)
71+
x0: fixed point
72+
u0: input corresponding to the fixed point
73+
stateVars: names of state variables
74+
inputVars: names of inputs
75+
outputVars: names of outputs
76+
"""
77+
78+
n: int
79+
m: int
80+
p: int
81+
82+
A: list
83+
B: list
84+
C: list
85+
D: list
86+
87+
x0: list[float]
88+
u0: list[float]
89+
90+
stateVars: list[str]
91+
inputVars: list[str]
92+
outputVars: list[str]
93+
94+
def __iter__(self):
95+
"""Allow unpacking A, B, C, D = result."""
96+
yield self.A
97+
yield self.B
98+
yield self.C
99+
yield self.D
100+
101+
def __getitem__(self, index: int):
102+
"""Allow accessing A, B, C, D via result[0] through result[3].
103+
104+
This is needed for backwards compatibility, because
105+
ModelicaSystem.linearize() used to return [A, B, C, D].
106+
"""
107+
return {0: self.A, 1: self.B, 2: self.C, 3: self.D}[index]
108+
109+
57110
class ModelicaSystem:
58111
def __init__(self, fileName=None, modelName=None, lmodel=None, commandLineOptions=None,
59112
variableFilter=None, customBuildDirectory=None, verbose=True, raiseerrors=False,
@@ -967,13 +1020,22 @@ def optimize(self): # 21
9671020

9681021
return optimizeResult
9691022

970-
# to linearize model
971-
def linearize(self, lintime=None, simflags=None): # 22
972-
"""
973-
This method linearizes model according to the linearized options. This will generate a linear model that consists of matrices A, B, C and D. It can be called:
974-
only without any arguments
975-
usage
976-
>>> linearize()
1023+
def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = None) -> LinearizationResult:
1024+
"""Linearize the model according to linearOptions.
1025+
1026+
Args:
1027+
lintime: Override linearOptions["stopTime"] value.
1028+
simflags: A string of extra command line flags for the model
1029+
binary.
1030+
1031+
Returns:
1032+
A LinearizationResult object is returned. This allows several
1033+
uses:
1034+
* `(A, B, C, D) = linearize()` to get just the matrices,
1035+
* `result = linearize(); result.A` to get everything and access the
1036+
attributes one by one,
1037+
* `result = linearize(); A = result[0]` mostly just for backwards
1038+
compatibility, because linearize() used to return `[A, B, C, D]`.
9771039
"""
9781040

9791041
if self.xmlFile is None:
@@ -1043,7 +1105,8 @@ def linearize(self, lintime=None, simflags=None): # 22
10431105
self.linearinputs = inputVars
10441106
self.linearoutputs = outputVars
10451107
self.linearstates = stateVars
1046-
return [A, B, C, D]
1108+
return LinearizationResult(n, m, p, A, B, C, D, x0, u0, stateVars,
1109+
inputVars, outputVars)
10471110
except ModuleNotFoundError:
10481111
raise Exception("ModuleNotFoundError: No module named 'linearized_model'")
10491112

OMPython/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import logging
4040

4141
from OMPython.OMCSession import OMCSessionBase, OMCSessionZMQ
42-
from OMPython.ModelicaSystem import ModelicaSystem, ModelicaSystemError
42+
from OMPython.ModelicaSystem import ModelicaSystem, ModelicaSystemError, LinearizationResult
4343

4444
# Logger Defined
4545
logger = logging.getLogger('OMPython')
@@ -61,6 +61,7 @@
6161
__all__ = [
6262
'ModelicaSystem',
6363
'ModelicaSystemError',
64+
'LinearizationResult',
6465

6566
'OMCSessionZMQ',
6667
'OMCSessionBase',

tests/test_linearization.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def test_getters(self):
7171
mod.setLinearizationOptions("stopTime=0.02")
7272
assert mod.getLinearizationOptions("stopTime") == ["0.02"]
7373

74-
mod.setInputs(["u1=0", "u2=0"])
74+
mod.setInputs(["u1=10", "u2=0"])
7575
[A, B, C, D] = mod.linearize()
7676
g = float(mod.getParameters("g")[0])
7777
l = float(mod.getParameters("l")[0])
@@ -82,3 +82,27 @@ def test_getters(self):
8282
assert np.isclose(B, [[0, 0], [0, 1]]).all()
8383
assert np.isclose(C, [[0.5, 1], [0, 1]]).all()
8484
assert np.isclose(D, [[1, 0], [1, 0]]).all()
85+
86+
# test LinearizationResult
87+
result = mod.linearize()
88+
assert result[0] == A
89+
assert result[1] == B
90+
assert result[2] == C
91+
assert result[3] == D
92+
with self.assertRaises(KeyError):
93+
result[4]
94+
95+
A2, B2, C2, D2 = result
96+
assert A2 == A
97+
assert B2 == B
98+
assert C2 == C
99+
assert D2 == D
100+
101+
assert result.n == 2
102+
assert result.m == 2
103+
assert result.p == 2
104+
assert np.isclose(result.x0, [0, np.pi]).all()
105+
assert np.isclose(result.u0, [10, 0]).all()
106+
assert result.stateVars == ["omega", "phi"]
107+
assert result.inputVars == ["u1", "u2"]
108+
assert result.outputVars == ["y1", "y2"]

0 commit comments

Comments
 (0)