|
35 | 35 | import ast |
36 | 36 | import csv |
37 | 37 | from dataclasses import dataclass |
38 | | -import importlib |
39 | 38 | import logging |
40 | 39 | import numbers |
41 | 40 | import numpy as np |
@@ -1551,13 +1550,6 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N |
1551 | 1550 | compatibility, because linearize() used to return `[A, B, C, D]`. |
1552 | 1551 | """ |
1553 | 1552 |
|
1554 | | - # replacement for depreciated importlib.load_module() |
1555 | | - def load_module_from_path(module_name, file_path): |
1556 | | - spec = importlib.util.spec_from_file_location(module_name, file_path) |
1557 | | - module_def = importlib.util.module_from_spec(spec) |
1558 | | - spec.loader.exec_module(module_def) |
1559 | | - |
1560 | | - return module_def |
1561 | 1553 |
|
1562 | 1554 | if len(self._quantities) == 0: |
1563 | 1555 | # if self._quantities has no content, the xml file was not parsed; see self._xmlparse() |
@@ -1597,38 +1589,59 @@ def load_module_from_path(module_name, file_path): |
1597 | 1589 | if simargs: |
1598 | 1590 | om_cmd.args_set(args=simargs) |
1599 | 1591 |
|
| 1592 | + # the file create by the model executable which contains the matrix and linear inputs, outputs and states |
| 1593 | + linear_file = self._tempdir / "linearized_model.py" |
| 1594 | + |
| 1595 | + linear_file.unlink(missing_ok=True) |
| 1596 | + |
1600 | 1597 | returncode = om_cmd.run() |
1601 | 1598 | if returncode != 0: |
1602 | 1599 | raise ModelicaSystemError(f"Linearize failed with return code: {returncode}") |
1603 | 1600 |
|
1604 | 1601 | self._simulated = True |
1605 | 1602 |
|
1606 | | - # code to get the matrix and linear inputs, outputs and states |
1607 | | - linearFile = self._tempdir / "linearized_model.py" |
| 1603 | + if not linear_file.exists(): |
| 1604 | + raise ModelicaSystemError(f"Linearization failed: {linear_file} not found!") |
1608 | 1605 |
|
1609 | | - # support older openmodelica versions before OpenModelica v1.16.2 where linearize() generates "linear_model_name.mo" file |
1610 | | - if not linearFile.exists(): |
1611 | | - linearFile = pathlib.Path(f'linear_{self._model_name}.py') |
1612 | | - |
1613 | | - if not linearFile.exists(): |
1614 | | - raise ModelicaSystemError(f"Linearization failed: {linearFile} not found!") |
1615 | | - |
1616 | | - # this function is called from the generated python code linearized_model.py at runtime, |
1617 | | - # 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 |
| 1606 | + # extract data from the python file with the linearized model using the ast module - this allows to get the |
| 1607 | + # needed information without executing the created code |
| 1608 | + linear_data = {} |
| 1609 | + linear_file_content = linear_file.read_text() |
1618 | 1610 | try: |
1619 | | - # do not add the linearfile directory to path, as multiple execution of linearization will always use the first added path, instead execute the file |
1620 | | - # https://github.com/OpenModelica/OMPython/issues/196 |
1621 | | - module = load_module_from_path(module_name="linearized_model", file_path=linearFile.as_posix()) |
1622 | | - |
1623 | | - result = module.linearized_model() |
1624 | | - (n, m, p, x0, u0, A, B, C, D, stateVars, inputVars, outputVars) = result |
1625 | | - self._linearized_inputs = inputVars |
1626 | | - self._linearized_outputs = outputVars |
1627 | | - self._linearized_states = stateVars |
1628 | | - return LinearizationResult(n, m, p, A, B, C, D, x0, u0, stateVars, |
1629 | | - inputVars, outputVars) |
1630 | | - except ModuleNotFoundError as ex: |
1631 | | - raise ModelicaSystemError("No module named 'linearized_model'") from ex |
| 1611 | + # ignore possible typing errors below (mypy) - these are caught by the try .. except .. block |
| 1612 | + linear_file_ast = ast.parse(linear_file_content) |
| 1613 | + for body_part in linear_file_ast.body[0].body: # type: ignore |
| 1614 | + if not isinstance(body_part, ast.Assign): |
| 1615 | + continue |
| 1616 | + |
| 1617 | + target = body_part.targets[0].id # type: ignore |
| 1618 | + value = ast.literal_eval(body_part.value) |
| 1619 | + |
| 1620 | + linear_data[target] = value |
| 1621 | + except (AttributeError, IndexError, ValueError, SyntaxError, TypeError) as ex: |
| 1622 | + raise ModelicaSystemError(f"Error parsing linearization file {linear_file}!") from ex |
| 1623 | + |
| 1624 | + # remove the file |
| 1625 | + linear_file.unlink() |
| 1626 | + |
| 1627 | + self._linearized_inputs = linear_data["inputVars"] |
| 1628 | + self._linearized_outputs = linear_data["outputVars"] |
| 1629 | + self._linearized_states = linear_data["stateVars"] |
| 1630 | + |
| 1631 | + return LinearizationResult( |
| 1632 | + n=linear_data["n"], |
| 1633 | + m=linear_data["m"], |
| 1634 | + p=linear_data["p"], |
| 1635 | + x0=linear_data["x0"], |
| 1636 | + u0=linear_data["u0"], |
| 1637 | + A=linear_data["A"], |
| 1638 | + B=linear_data["B"], |
| 1639 | + C=linear_data["C"], |
| 1640 | + D=linear_data["D"], |
| 1641 | + stateVars=linear_data["stateVars"], |
| 1642 | + inputVars=linear_data["inputVars"], |
| 1643 | + outputVars=linear_data["outputVars"], |
| 1644 | + ) |
1632 | 1645 |
|
1633 | 1646 | def getLinearInputs(self) -> list[str]: |
1634 | 1647 | """Get names of input variables of the linearized model.""" |
|
0 commit comments