|
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 |
@@ -1451,14 +1451,6 @@ def linearize(self, lintime: Optional[float] = None, simflags: Optional[str] = N |
1451 | 1451 | compatibility, because linearize() used to return `[A, B, C, D]`. |
1452 | 1452 | """ |
1453 | 1453 |
|
1454 | | - # replacement for depreciated importlib.load_module() |
1455 | | - def load_module_from_path(module_name, file_path): |
1456 | | - spec = importlib.util.spec_from_file_location(module_name, file_path) |
1457 | | - module_def = importlib.util.module_from_spec(spec) |
1458 | | - spec.loader.exec_module(module_def) |
1459 | | - |
1460 | | - return module_def |
1461 | | - |
1462 | 1454 | if self._xml_file is None: |
1463 | 1455 | raise ModelicaSystemError( |
1464 | 1456 | "Linearization cannot be performed as the model is not build, " |
@@ -1501,38 +1493,61 @@ def load_module_from_path(module_name, file_path): |
1501 | 1493 | if simargs: |
1502 | 1494 | om_cmd.args_set(args=simargs) |
1503 | 1495 |
|
| 1496 | + # the file create by the model executable which contains the matrix and linear inputs, outputs and states |
| 1497 | + linear_file = self.getWorkDirectory() / "linearized_model.py" |
| 1498 | + |
| 1499 | + linear_file.unlink(missing_ok=True) |
| 1500 | + |
1504 | 1501 | returncode = om_cmd.run() |
1505 | 1502 | if returncode != 0: |
1506 | 1503 | raise ModelicaSystemError(f"Linearize failed with return code: {returncode}") |
1507 | 1504 |
|
1508 | 1505 | self._simulated = True |
1509 | 1506 |
|
1510 | 1507 | # code to get the matrix and linear inputs, outputs and states |
1511 | | - linearFile = self.getWorkDirectory() / "linearized_model.py" |
1512 | | - |
1513 | | - # support older openmodelica versions before OpenModelica v1.16.2 where linearize() generates "linear_model_name.mo" file |
1514 | | - if not linearFile.exists(): |
1515 | | - linearFile = pathlib.Path(f'linear_{self._model_name}.py') |
1516 | | - |
1517 | | - if not linearFile.exists(): |
1518 | | - raise ModelicaSystemError(f"Linearization failed: {linearFile} not found!") |
1519 | | - |
1520 | | - # this function is called from the generated python code linearized_model.py at runtime, |
1521 | | - # 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 |
| 1508 | + linear_file = self.getWorkDirectory() / "linearized_model.py" |
| 1509 | + if not linear_file.exists(): |
| 1510 | + raise ModelicaSystemError(f"Linearization failed: {linear_file} not found!") |
| 1511 | + |
| 1512 | + # extract data from the python file with the linearized model using the ast module - this allows to get the |
| 1513 | + # needed information without executing the created code |
| 1514 | + linear_data = {} |
| 1515 | + linear_file_content = linear_file.read_text() |
1522 | 1516 | try: |
1523 | | - # do not add the linearfile directory to path, as multiple execution of linearization will always use the first added path, instead execute the file |
1524 | | - # https://github.com/OpenModelica/OMPython/issues/196 |
1525 | | - module = load_module_from_path(module_name="linearized_model", file_path=linearFile.as_posix()) |
1526 | | - |
1527 | | - result = module.linearized_model() |
1528 | | - (n, m, p, x0, u0, A, B, C, D, stateVars, inputVars, outputVars) = result |
1529 | | - self._linearized_inputs = inputVars |
1530 | | - self._linearized_outputs = outputVars |
1531 | | - self._linearized_states = stateVars |
1532 | | - return LinearizationResult(n, m, p, A, B, C, D, x0, u0, stateVars, |
1533 | | - inputVars, outputVars) |
1534 | | - except ModuleNotFoundError as ex: |
1535 | | - raise ModelicaSystemError("No module named 'linearized_model'") from ex |
| 1517 | + # ignore possible typing errors below (mypy) - these are caught by the try .. except .. block |
| 1518 | + linear_file_ast = ast.parse(linear_file_content) |
| 1519 | + for body_part in linear_file_ast.body[0].body: # type: ignore |
| 1520 | + if not isinstance(body_part, ast.Assign): |
| 1521 | + continue |
| 1522 | + |
| 1523 | + target = body_part.targets[0].id # type: ignore |
| 1524 | + value = ast.literal_eval(body_part.value) |
| 1525 | + |
| 1526 | + linear_data[target] = value |
| 1527 | + except (AttributeError, IndexError, ValueError, SyntaxError, TypeError) as ex: |
| 1528 | + raise ModelicaSystemError(f"Error parsing linearization file {linear_file}!") from ex |
| 1529 | + |
| 1530 | + # remove the file |
| 1531 | + linear_file.unlink() |
| 1532 | + |
| 1533 | + self._linearized_inputs = linear_data["inputVars"] |
| 1534 | + self._linearized_outputs = linear_data["outputVars"] |
| 1535 | + self._linearized_states = linear_data["stateVars"] |
| 1536 | + |
| 1537 | + return LinearizationResult( |
| 1538 | + n=linear_data["n"], |
| 1539 | + m=linear_data["m"], |
| 1540 | + p=linear_data["p"], |
| 1541 | + x0=linear_data["x0"], |
| 1542 | + u0=linear_data["u0"], |
| 1543 | + A=linear_data["A"], |
| 1544 | + B=linear_data["B"], |
| 1545 | + C=linear_data["C"], |
| 1546 | + D=linear_data["D"], |
| 1547 | + stateVars=linear_data["stateVars"], |
| 1548 | + inputVars=linear_data["inputVars"], |
| 1549 | + outputVars=linear_data["outputVars"], |
| 1550 | + ) |
1536 | 1551 |
|
1537 | 1552 | def getLinearInputs(self) -> list[str]: |
1538 | 1553 | """Get names of input variables of the linearized model.""" |
|
0 commit comments