|
4 | 4 | import sys |
5 | 5 | import os |
6 | 6 | import shutil |
7 | | -import pathlib |
| 7 | +import uuid |
8 | 8 | import warnings |
9 | 9 | import atexit |
10 | 10 | import json |
@@ -49,7 +49,8 @@ class DymolaAPI(SimulationAPI): |
49 | 49 | :param str,Path working_directory: |
50 | 50 | Dirpath for the current working directory of dymola |
51 | 51 | :param str model_name: |
52 | | - Name of the model to be simulated |
| 52 | + Name of the model to be simulated. |
| 53 | + If None, it has to be provided prior to or when calling simulate(). |
53 | 54 | :param list packages: |
54 | 55 | List with path's to the packages needed to simulate the model |
55 | 56 | :keyword Boolean show_window: |
@@ -156,7 +157,7 @@ class DymolaAPI(SimulationAPI): |
156 | 157 | def __init__( |
157 | 158 | self, |
158 | 159 | working_directory: Union[Path, str], |
159 | | - model_name: str, |
| 160 | + model_name: str = None, |
160 | 161 | packages: List[Union[Path, str]] = None, |
161 | 162 | **kwargs |
162 | 163 | ): |
@@ -223,8 +224,9 @@ def __init__( |
223 | 224 | "Thus, not able to find the `dymola_exe_path` and `dymola_interface_path`. " |
224 | 225 | "Either specify both or pass an existing `dymola_path`." |
225 | 226 | ) |
| 227 | + self.dymola_path = dymola_path |
226 | 228 | if self.dymola_exe_path is None: |
227 | | - self.dymola_exe_path = self.get_dymola_path(dymola_path) |
| 229 | + self.dymola_exe_path = self.get_dymola_exe_path(dymola_path) |
228 | 230 | self.logger.info("Using dymola.exe: %s", self.dymola_exe_path) |
229 | 231 | if self.dymola_interface_path is None: |
230 | 232 | self.dymola_interface_path = self.get_dymola_interface_path(dymola_path) |
@@ -271,10 +273,14 @@ def __init__( |
271 | 273 | ) |
272 | 274 | # For translation etc. always setup a default dymola instance |
273 | 275 | self.dymola = self._setup_dymola_interface(dict(use_mp=False)) |
| 276 | + if not self.license_is_available(): |
| 277 | + warnings.warn("You have no licence to use Dymola. " |
| 278 | + "Hence you can only simulate models with 8 or less equations.") |
274 | 279 |
|
275 | 280 | self.fully_initialized = True |
276 | 281 | # Trigger on init. |
277 | | - self._update_model() |
| 282 | + if model_name is not None: |
| 283 | + self._update_model() |
278 | 284 | # Set result_names to output variables. |
279 | 285 | self.result_names = list(self.outputs.keys()) |
280 | 286 |
|
@@ -407,6 +413,13 @@ def _single_simulation(self, kwargs): |
407 | 413 | " ,".join(list(set(_res_names).difference(self.result_names))) |
408 | 414 | ) |
409 | 415 |
|
| 416 | + if self.model_name is None: |
| 417 | + raise ValueError( |
| 418 | + "You neither passed a model_name when " |
| 419 | + "starting DymolaAPI, nor when calling simulate. " |
| 420 | + "Can't simulate no model." |
| 421 | + ) |
| 422 | + |
410 | 423 | # Handle parameters: |
411 | 424 | if parameters is None: |
412 | 425 | parameters = {} |
@@ -824,14 +837,18 @@ def _setup_dymola_interface(self, kwargs: dict): |
824 | 837 | # Events can also cause errors in the shape. |
825 | 838 | dymola.experimentSetupOutput(equidistant=True, |
826 | 839 | events=False) |
827 | | - if not dymola.RequestOption("Standard"): |
828 | | - warnings.warn("You have no licence to use Dymola. " |
829 | | - "Hence you can only simulate models with 8 or less equations.") |
830 | 840 | if use_mp: |
831 | 841 | DymolaAPI.dymola = dymola |
832 | 842 | return None |
833 | 843 | return dymola |
834 | 844 |
|
| 845 | + def license_is_available(self, option: str = "Standard"): |
| 846 | + """Check if license is available""" |
| 847 | + if self.dymola is None: |
| 848 | + warnings.warn("You want to check the license before starting dymola, this is not supported.") |
| 849 | + return False |
| 850 | + return self.dymola.RequestOption(option) |
| 851 | + |
835 | 852 | def _open_dymola_interface(self, port): |
836 | 853 | """Open an instance of dymola and return the API-Object""" |
837 | 854 | if self.dymola_interface_path not in sys.path: |
@@ -968,9 +985,27 @@ def save_for_reproduction( |
968 | 985 | _total_model_name = f"Dymola/{self.model_name.replace('.', '_')}_total.mo" |
969 | 986 | _total_model = Path(self.cd).joinpath(_total_model_name) |
970 | 987 | os.makedirs(_total_model.parent, exist_ok=True) # Create to ensure model can be saved. |
| 988 | + if "(" in self.model_name: |
| 989 | + # Create temporary model: |
| 990 | + temp_model_file = Path(self.cd).joinpath(f"temp_total_model_{uuid.uuid4()}.mo") |
| 991 | + temp_mode_name = f"{self.model_name.split('(')[0].split('.')[-1]}WithModifier" |
| 992 | + with open(temp_model_file, "w") as file: |
| 993 | + file.write(f"model {temp_mode_name}\n extends {self.model_name};\nend {temp_mode_name};") |
| 994 | + res = self.dymola.openModel(str(temp_model_file), changeDirectory=False) |
| 995 | + if not res: |
| 996 | + self.logger.error( |
| 997 | + "Could not create separate model for model with modifiers: %s", |
| 998 | + self.model_name |
| 999 | + ) |
| 1000 | + model_name_to_save = self.model_name |
| 1001 | + else: |
| 1002 | + model_name_to_save = temp_mode_name |
| 1003 | + os.remove(temp_model_file) |
| 1004 | + else: |
| 1005 | + model_name_to_save = self.model_name |
971 | 1006 | res = self.dymola.saveTotalModel( |
972 | 1007 | fileName=str(_total_model), |
973 | | - modelName=self.model_name |
| 1008 | + modelName=model_name_to_save |
974 | 1009 | ) |
975 | 1010 | if res: |
976 | 1011 | files.append(ReproductionFile( |
@@ -1061,7 +1096,7 @@ def get_dymola_interface_path(dymola_install_dir): |
1061 | 1096 | return egg_file |
1062 | 1097 |
|
1063 | 1098 | @staticmethod |
1064 | | - def get_dymola_path(dymola_install_dir, dymola_name=None): |
| 1099 | + def get_dymola_exe_path(dymola_install_dir, dymola_name=None): |
1065 | 1100 | """ |
1066 | 1101 | Function to get the path of the dymola exe-file |
1067 | 1102 | on the current used machine. |
|
0 commit comments