Skip to content

Commit abcae09

Browse files
authored
ModelicaSystem.linearize() -> do not execute python file (#320)
* [ModelicaSystem.linearize] do not execute python file but use ast to get the data * [ModelicaSystem.linearize] remove old check / use of file in current dir * [ModelicaSystem.linearize] fix mypy * [ModelicaSystem] add spelling fix (fox codespell)
1 parent 6732bd7 commit abcae09

File tree

1 file changed

+45
-33
lines changed

1 file changed

+45
-33
lines changed

OMPython/ModelicaSystem.py

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import ast
3636
import csv
3737
from dataclasses import dataclass
38-
import importlib
3938
import logging
4039
import numbers
4140
import numpy as np
@@ -1536,14 +1535,6 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N
15361535
compatibility, because linearize() used to return `[A, B, C, D]`.
15371536
"""
15381537

1539-
# replacement for depreciated importlib.load_module()
1540-
def load_module_from_path(module_name, file_path):
1541-
spec = importlib.util.spec_from_file_location(module_name, file_path)
1542-
module_def = importlib.util.module_from_spec(spec)
1543-
spec.loader.exec_module(module_def)
1544-
1545-
return module_def
1546-
15471538
if self._xml_file is None:
15481539
raise ModelicaSystemError(
15491540
"Linearization cannot be performed as the model is not build, "
@@ -1581,38 +1572,59 @@ def load_module_from_path(module_name, file_path):
15811572
if simargs:
15821573
om_cmd.args_set(args=simargs)
15831574

1575+
# the file create by the model executable which contains the matrix and linear inputs, outputs and states
1576+
linear_file = self._tempdir / "linearized_model.py"
1577+
1578+
linear_file.unlink(missing_ok=True)
1579+
15841580
returncode = om_cmd.run()
15851581
if returncode != 0:
15861582
raise ModelicaSystemError(f"Linearize failed with return code: {returncode}")
15871583

15881584
self._simulated = True
15891585

1590-
# code to get the matrix and linear inputs, outputs and states
1591-
linearFile = self._tempdir / "linearized_model.py"
1586+
if not linear_file.exists():
1587+
raise ModelicaSystemError(f"Linearization failed: {linear_file} not found!")
15921588

1593-
# support older openmodelica versions before OpenModelica v1.16.2 where linearize() generates "linear_model_name.mo" file
1594-
if not linearFile.exists():
1595-
linearFile = pathlib.Path(f'linear_{self._model_name}.py')
1596-
1597-
if not linearFile.exists():
1598-
raise ModelicaSystemError(f"Linearization failed: {linearFile} not found!")
1599-
1600-
# this function is called from the generated python code linearized_model.py at runtime,
1601-
# to improve the performance by directly reading the matrices A, B, C and D from the julia code and avoid building the linearized modelica model
1589+
# extract data from the python file with the linearized model using the ast module - this allows to get the
1590+
# needed information without executing the created code
1591+
linear_data = {}
1592+
linear_file_content = linear_file.read_text()
16021593
try:
1603-
# do not add the linearfile directory to path, as multiple execution of linearization will always use the first added path, instead execute the file
1604-
# https://github.com/OpenModelica/OMPython/issues/196
1605-
module = load_module_from_path(module_name="linearized_model", file_path=linearFile.as_posix())
1606-
1607-
result = module.linearized_model()
1608-
(n, m, p, x0, u0, A, B, C, D, stateVars, inputVars, outputVars) = result
1609-
self._linearized_inputs = inputVars
1610-
self._linearized_outputs = outputVars
1611-
self._linearized_states = stateVars
1612-
return LinearizationResult(n, m, p, A, B, C, D, x0, u0, stateVars,
1613-
inputVars, outputVars)
1614-
except ModuleNotFoundError as ex:
1615-
raise ModelicaSystemError("No module named 'linearized_model'") from ex
1594+
# ignore possible typing errors below (mypy) - these are caught by the try .. except .. block
1595+
linear_file_ast = ast.parse(linear_file_content)
1596+
for body_part in linear_file_ast.body[0].body: # type: ignore
1597+
if not isinstance(body_part, ast.Assign):
1598+
continue
1599+
1600+
target = body_part.targets[0].id # type: ignore
1601+
value = ast.literal_eval(body_part.value)
1602+
1603+
linear_data[target] = value
1604+
except (AttributeError, IndexError, ValueError, SyntaxError, TypeError) as ex:
1605+
raise ModelicaSystemError(f"Error parsing linearization file {linear_file}!") from ex
1606+
1607+
# remove the file
1608+
linear_file.unlink()
1609+
1610+
self._linearized_inputs = linear_data["inputVars"]
1611+
self._linearized_outputs = linear_data["outputVars"]
1612+
self._linearized_states = linear_data["stateVars"]
1613+
1614+
return LinearizationResult(
1615+
n=linear_data["n"],
1616+
m=linear_data["m"],
1617+
p=linear_data["p"],
1618+
x0=linear_data["x0"],
1619+
u0=linear_data["u0"],
1620+
A=linear_data["A"],
1621+
B=linear_data["B"],
1622+
C=linear_data["C"],
1623+
D=linear_data["D"],
1624+
stateVars=linear_data["stateVars"],
1625+
inputVars=linear_data["inputVars"],
1626+
outputVars=linear_data["outputVars"],
1627+
)
16161628

16171629
def getLinearInputs(self) -> list[str]:
16181630
"""Get names of input variables of the linearized model."""

0 commit comments

Comments
 (0)