|
32 | 32 | CONDITIONS OF OSMC-PL. |
33 | 33 | """ |
34 | 34 |
|
| 35 | +import ast |
35 | 36 | import csv |
36 | 37 | from dataclasses import dataclass |
37 | | -import importlib |
38 | 38 | import logging |
39 | 39 | import numbers |
40 | 40 | import numpy as np |
@@ -1438,14 +1438,6 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N |
1438 | 1438 | compatibility, because linearize() used to return `[A, B, C, D]`. |
1439 | 1439 | """ |
1440 | 1440 |
|
1441 | | - # replacement for depreciated importlib.load_module() |
1442 | | - def load_module_from_path(module_name, file_path): |
1443 | | - spec = importlib.util.spec_from_file_location(module_name, file_path) |
1444 | | - module_def = importlib.util.module_from_spec(spec) |
1445 | | - spec.loader.exec_module(module_def) |
1446 | | - |
1447 | | - return module_def |
1448 | | - |
1449 | 1441 | if self._xml_file is None: |
1450 | 1442 | raise ModelicaSystemError( |
1451 | 1443 | "Linearization cannot be performed as the model is not build, " |
@@ -1484,38 +1476,62 @@ def load_module_from_path(module_name, file_path): |
1484 | 1476 | if simargs: |
1485 | 1477 | om_cmd.args_set(args=simargs) |
1486 | 1478 |
|
| 1479 | + # the file create by the model executable which contains the matrix and linear inputs, outputs and states |
| 1480 | + linear_file = self._tempdir / "linearized_model.py" |
| 1481 | + |
| 1482 | + linear_file.unlink(missing_ok=True) |
| 1483 | + |
1487 | 1484 | returncode = om_cmd.run() |
1488 | 1485 | if returncode != 0: |
1489 | 1486 | raise ModelicaSystemError(f"Linearize failed with return code: {returncode}") |
1490 | 1487 |
|
1491 | 1488 | self._simulated = True |
1492 | 1489 |
|
1493 | | - # code to get the matrix and linear inputs, outputs and states |
1494 | | - linearFile = self._tempdir / "linearized_model.py" |
| 1490 | + if not linear_file.exists(): |
| 1491 | + raise ModelicaSystemError(f"Linearization failed: {linear_file} not found!") |
1495 | 1492 |
|
1496 | 1493 | # support older openmodelica versions before OpenModelica v1.16.2 where linearize() generates "linear_model_name.mo" file |
1497 | | - if not linearFile.exists(): |
1498 | | - linearFile = pathlib.Path(f'linear_{self._model_name}.py') |
1499 | | - |
1500 | | - if not linearFile.exists(): |
1501 | | - raise ModelicaSystemError(f"Linearization failed: {linearFile} not found!") |
| 1494 | + if not linear_file.exists(): |
| 1495 | + linear_file = pathlib.Path(f'linear_{self._model_name}.py') |
1502 | 1496 |
|
1503 | | - # this function is called from the generated python code linearized_model.py at runtime, |
1504 | | - # 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 |
| 1497 | + # extract data from the python file with the linearized model using the ast module - this allows to get the |
| 1498 | + # needed information without executing the created code |
| 1499 | + linear_data = {} |
| 1500 | + linear_file_content = linear_file.read_text() |
1505 | 1501 | try: |
1506 | | - # do not add the linearfile directory to path, as multiple execution of linearization will always use the first added path, instead execute the file |
1507 | | - # https://github.com/OpenModelica/OMPython/issues/196 |
1508 | | - module = load_module_from_path(module_name="linearized_model", file_path=linearFile.as_posix()) |
1509 | | - |
1510 | | - result = module.linearized_model() |
1511 | | - (n, m, p, x0, u0, A, B, C, D, stateVars, inputVars, outputVars) = result |
1512 | | - self._linearized_inputs = inputVars |
1513 | | - self._linearized_outputs = outputVars |
1514 | | - self._linearized_states = stateVars |
1515 | | - return LinearizationResult(n, m, p, A, B, C, D, x0, u0, stateVars, |
1516 | | - inputVars, outputVars) |
1517 | | - except ModuleNotFoundError as ex: |
1518 | | - raise ModelicaSystemError("No module named 'linearized_model'") from ex |
| 1502 | + linear_file_ast = ast.parse(linear_file_content) |
| 1503 | + for body_part in linear_file_ast.body[0].body: |
| 1504 | + if not isinstance(body_part, ast.Assign): |
| 1505 | + continue |
| 1506 | + |
| 1507 | + target = body_part.targets[0].id |
| 1508 | + value = ast.literal_eval(body_part.value) |
| 1509 | + |
| 1510 | + linear_data[target] = value |
| 1511 | + except (AttributeError, IndexError, ValueError, SyntaxError, TypeError) as ex: |
| 1512 | + raise ModelicaSystemError(f"Error parsing linearization file {linear_file}!") from ex |
| 1513 | + |
| 1514 | + # remove the file |
| 1515 | + linear_file.unlink() |
| 1516 | + |
| 1517 | + self._linearized_inputs = linear_data["inputVars"] |
| 1518 | + self._linearized_outputs = linear_data["outputVars"] |
| 1519 | + self._linearized_states = linear_data["stateVars"] |
| 1520 | + |
| 1521 | + return LinearizationResult( |
| 1522 | + n=linear_data["n"], |
| 1523 | + m=linear_data["m"], |
| 1524 | + p=linear_data["p"], |
| 1525 | + x0=linear_data["x0"], |
| 1526 | + u0=linear_data["u0"], |
| 1527 | + A=linear_data["A"], |
| 1528 | + B=linear_data["B"], |
| 1529 | + C=linear_data["C"], |
| 1530 | + D=linear_data["D"], |
| 1531 | + stateVars=linear_data["stateVars"], |
| 1532 | + inputVars=linear_data["inputVars"], |
| 1533 | + outputVars=linear_data["outputVars"], |
| 1534 | + ) |
1519 | 1535 |
|
1520 | 1536 | def getLinearInputs(self) -> list[str]: |
1521 | 1537 | """Get names of input variables of the linearized model.""" |
|
0 commit comments