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