|
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 |
@@ -1536,14 +1535,6 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N |
1536 | 1535 | compatibility, because linearize() used to return `[A, B, C, D]`. |
1537 | 1536 | """ |
1538 | 1537 |
|
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 | | - |
1547 | 1538 | if self._xml_file is None: |
1548 | 1539 | raise ModelicaSystemError( |
1549 | 1540 | "Linearization cannot be performed as the model is not build, " |
@@ -1581,38 +1572,59 @@ def load_module_from_path(module_name, file_path): |
1581 | 1572 | if simargs: |
1582 | 1573 | om_cmd.args_set(args=simargs) |
1583 | 1574 |
|
| 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 | + |
1584 | 1580 | returncode = om_cmd.run() |
1585 | 1581 | if returncode != 0: |
1586 | 1582 | raise ModelicaSystemError(f"Linearize failed with return code: {returncode}") |
1587 | 1583 |
|
1588 | 1584 | self._simulated = True |
1589 | 1585 |
|
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!") |
1592 | 1588 |
|
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() |
1602 | 1593 | 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 | + ) |
1616 | 1628 |
|
1617 | 1629 | def getLinearInputs(self) -> list[str]: |
1618 | 1630 | """Get names of input variables of the linearized model.""" |
|
0 commit comments