Loading AutoCSM/auto_csm.py +187 −75 Original line number Diff line number Diff line # -*- coding: utf-8 -*- """ Created on Wed Jun 5 13:10:34 2024 Created on Fri Sep 20 11:54:58 2024 @author: fig """ import pathlib from languages.modelica.modelica_methods import ModelicaMethods from languages.julia.julia_methods import JuliaMethods import helper_functions import pathlib from helper_functions import Loader class AutoCSM: def __init__(self, language='modelica', architecture='nested', terminal_animation=True): self.language = language self.architecture = architecture self.language_methods = { 'modelica': ModelicaMethods(self), 'julia': JuliaMethods(self) } self.terminal_animation = terminal_animation def set_language(self, language): if language in self.language_methods: self.language = language else: raise ValueError(f"Unsupported language: {language}") """ AutoCSM class to automate the creation of simulation architectures and models. Attributes: language (str): Programming language for model generation ('modelica', 'julia'). architecture (str): Architecture type for the system ('nested'). terminal_animation (bool): Whether terminal animations are shown during processing. input_specification (str): Path to the input specification file. output_path (str): Path to store output files. project_path (str): Path to the project directory. model_path (str): Path to the output directory of model created during create_model(). """ def set_architecture(self, architecture): self.architecture = architecture # Class-level constant for language methods LANGUAGE_METHODS = { 'modelica': ModelicaMethods, 'julia': JuliaMethods } def set_input_specification(self, input_specification): self.input_specification = input_specification # ARCHITECTURES = ['nested'] # at level of the language def __init__(self, language: str = 'modelica', architecture: str = 'nested', terminal_animation: bool = True): self._language = None self._architecture = None self._terminal_animation = None self._input_specification = None self._output_path = None self._project_path = None def set_output_path(self, output_path): self.output_path = output_path # Internal variables self._model_path = None def set_model_path(self, model_path): self.model_path = model_path # Setting via the property to validate. self.language = language self.architecture = architecture self.terminal_animation = terminal_animation def set_project_path(self, project_path): # Remove package.mo if contained in project_path (i.e., a common issue) temp = pathlib.Path(project_path) @property def terminal_animation(self) -> str: """Returns the terminal_animation setting.""" return self._terminal_animation @terminal_animation.setter def terminal_animation(self, value: bool) -> None: """Sets the terminal_animation.""" self._terminal_animation = value @property def language(self) -> str: """Returns the current language setting.""" return self._language @language.setter def language(self, value: str) -> None: """Sets the language for model generation, with validation.""" if value not in self.LANGUAGE_METHODS: raise ValueError(f"Unsupported language: {value}") self._language = value self._language_method = self.LANGUAGE_METHODS[value](self) @property def architecture(self) -> str: """Returns the current architecture setting.""" return self._architecture @architecture.setter def architecture(self, value: str) -> None: """Sets the architecture type.""" self._architecture = value @property def input_specification(self) -> str: """Returns the input specification path.""" return self._input_specification @input_specification.setter def input_specification(self, path: str) -> None: """Sets and validates the input specification file path.""" if not pathlib.Path(path).is_file(): raise FileNotFoundError(f"Input specification file not found: {path}") self._input_specification = path @property def output_path(self) -> str: """Returns the output directory path.""" return self._output_path @output_path.setter def output_path(self, path: str) -> None: """Sets and validates the output directory path.""" output_dir = pathlib.Path(path) if not output_dir.exists(): output_dir.mkdir(parents=True, exist_ok=True) # Create directories if they don't exist self._output_path = output_dir.as_posix() @property def model_path(self) -> str: """Returns the output directory path.""" return self._model_path @model_path.setter def model_path(self, path: str) -> None: """Sets the path to the model file.""" self._model_path = path @property def project_path(self) -> str: """Returns the project path.""" return self._project_path @project_path.setter def project_path(self, path: str) -> None: """Sets the project path, ensuring it's not 'package.mo'.""" temp = pathlib.Path(path) if temp.name == "package.mo": temp = temp.parent self.project_path = str(temp) self._project_path = temp.as_posix() ## Sub-class methods def create_architecture(self, *args, **kwargs): if self.terminal_animation: with helper_functions.Loader("Creating the architecture..."): return self.language_methods[self.language].create_architecture(*args, **kwargs) else: print("Creating the architecture...") return self.language_methods[self.language].create_architecture(*args, **kwargs) def _execute_with_animation(self, message: str, method, *args, **kwargs): """ Executes a method with an optional terminal animation. def create_model(self, *args, **kwargs): Args: message (str): The message to display during animation. method (callable): The method to be executed. """ if self.terminal_animation: with helper_functions.Loader("Creating the model..."): return self.language_methods[self.language].create_model(*args, **kwargs) with Loader(message): return method(*args, **kwargs) else: print("Creating the model...") return self.language_methods[self.language].create_model(*args, **kwargs) print(message) return method(*args, **kwargs) def create_fmu(self, *args, **kwargs): if self.terminal_animation: with helper_functions.Loader("Creating the FMU..."): return self.language_methods[self.language].create_fmu(*args, **kwargs) else: print("Creating the FMU...") return self.language_methods[self.language].create_fmu(*args, **kwargs) def create_architecture(self, *args: tuple, **kwargs: dict) -> None: """ Creates the architecture based on the selected language and architecture. if __name__ == "__main__": Args: *args: Additional positional arguments passed to the architecture creation method. **kwargs: Additional keyword arguments passed to the architecture creation method. """ try: return self._execute_with_animation("Creating the architecture...", self._language_method.create_architecture, *args, **kwargs) except Exception as e: print(f"Failed to create the architecture: {str(e)}") raise def create_model(self, model_name='Simulator.mo',*args: tuple, **kwargs: dict) -> None: """ Creates the model for simulation. Args: *args: Additional positional arguments passed to the model creation method. **kwargs: Additional keyword arguments passed to the model creation method. """ self.model_path = pathlib.Path.as_posix(pathlib.Path(self.output_path) / model_name) # Input specification JSON file path json_file_path = '../examples/data/input_specification_v0.json' try: return self._execute_with_animation("Creating the model...", self._language_method.create_model, *args, **kwargs) except Exception as e: print(f"Failed to create the model: {str(e)}") raise # Output location of generated files output_path = '../temp' def create_fmu(self, *args: tuple, **kwargs: dict) -> None: """ Creates the Functional Mock-up Unit (FMU). # Path to the project which uses an AutoCSM language method project_path = '../examples/modelica/GenericCSM' Args: *args: Additional positional arguments passed to the FMU creation method. **kwargs: Additional keyword arguments passed to the FMU creation method. """ try: return self._execute_with_animation("Creating the FMU...", self._language_method.create_fmu, *args, **kwargs) except Exception as e: print(f"Failed to create the FMU: {str(e)}") raise # Model dependecies: List of libraries needed for FMU generation dependencies = [r'../../../Modelica/modelica-buildings/Buildings/package.mo', r'../../../Modelica/TRANSFORM-Library/TRANSFORM/package.mo'] if __name__ == "__main__": # Instantiate AutoCSM csm = AutoCSM(language='modelica', architecture='nested') # Load teh specification csm.set_input_specification(json_file_path) # Load the JSON specification csm.input_specification = '../examples/data/input_specification_v0.json' # Set the path to output created files csm.set_output_path(output_path) csm.output_path = '../temp' # This line creates an project with a file structure for the specified language and architecture csm.create_architecture() # more notes # This line creates a project with a file structure for the specified language and architecture csm.create_architecture() # Useful if a project is being created from scratch # Set the path to the project of interest csm.set_project_path(project_path) # Set the path to the project which uses an AutoCSM language method csm.project_path = '../examples/modelica/GenericCSM' # Create the model for export to FMU (i.e., Simulator.mo) csm.create_model(uniform=[False, False]) # Note this does not specify a solver in the model file. That is added in create_FMU. # Model dependencies: List of libraries needed for FMU generation dependencies = [ r'../../../Modelica/TRANSFORM-Library/TRANSFORM/package.mo' ] # Generate the FMU (i.e., Simulator.fmu) csm.create_fmu(dependencies) csm.create_fmu(dependencies, experimentSettings={'solver':'Sdirk34hw','tolerance':1e-5}) No newline at end of file AutoCSM/languages/modelica/modelica_methods.py +151 −30 Original line number Diff line number Diff line Loading @@ -13,41 +13,162 @@ from . import modelica_create_fmu_dymola from . import modelica_create_architecture class ModelicaMethods: """ This class provides methods for Modelica-specific tasks such as creating architecture, generating models, and building FMUs. Attributes: parent (AutoCSM): The parent AutoCSM object that stores configuration details. base_path (str): The base path where Modelica templates are located. """ BASE_PATH = pathlib.Path(__file__).parent TEMPLATE_FOLDER = (pathlib.Path(BASE_PATH) / '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/TemplateSystem').resolve() STRUCTURE_PATH = (pathlib.Path(BASE_PATH) / '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/Structure.mo').resolve() EXADIGIT_PACKAGE_PATH = (pathlib.Path(BASE_PATH) / '../../../methods/modelica/ExaDigiT_AutoCSM/package.mo').resolve() SUPPORTED_ARCHITECTURES = ['nested'] SUPPORTED_FMU_COMPILERS = ['dymola'] def __init__(self, parent): self.parent = parent self.base_path = os.path.dirname(__file__) def create_architecture(self, template_folder=None): if template_folder is None: template_folder = os.path.abspath(os.path.join(self.base_path, '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/TemplateSystem')) def validate_architecture(self): """ Validates if the current architecture is supported. Raises: ValueError: If the architecture is not supported. """ if self.parent.architecture not in self.SUPPORTED_ARCHITECTURES: raise ValueError(f"Unsupported architecture: {self.parent.architecture}. " f"Supported architectures are: {self.SUPPORTED_ARCHITECTURES}") def validate_fmu_compiler(self, fmu_compiler): """ Validates if the current fmu compiler is supported. Raises: ValueError: If the fmu compiler is not supported. """ if fmu_compiler not in self.SUPPORTED_FMU_COMPILERS: raise ValueError(f"Unsupported fmu compiler: {fmu_compiler}. " f"Supported fmu compilers are: {self.SUPPORTED_FMU_COMPILERS}") def validate_and_convert_paths(self, paths: list) -> list: """ Validates a list of file paths, converts them to absolute paths, and checks if they exist. Args: paths (list): List of file paths (relative or absolute). Returns: list: List of dependencies as absolute paths. Raises: FileNotFoundError: If any of the file paths do not exist. """ absolute_paths = [] for path in paths: abs_path = pathlib.Path(path).resolve() # Convert to absolute path if not abs_path.exists(): # Check if the file exists raise FileNotFoundError(f"The file '{abs_path}' does not exist.") absolute_paths.append(abs_path.as_posix()) return absolute_paths def create_architecture(self, template_folder: str = None): """ Creates the Modelica architecture by copying the template system structure. Args: template_folder (str, optional): Path to the template folder. If not provided, uses the default. """ # Validate the architecture self.validate_architecture() # Update input parameters template_folder = template_folder or self.TEMPLATE_FOLDER.as_posix() try: modelica_create_architecture.main(self.parent.input_specification, self.parent.output_path, template_folder, self.parent.architecture) except Exception as e: raise RuntimeError(f"Failed to create architecture: {str(e)}") def create_model(self, structure_path: str = None, parent_class: str = 'ExaDigiT_AutoCSM.BaseClasses.Tests.PartialTest', **kwargs): """ Creates the Modelica model for the given architecture. def create_model(self, structure_path=None, parent_class='ExaDigiT_AutoCSM.BaseClasses.Tests.PartialTest', uniform=[True,True]): if structure_path is None: structure_path = os.path.abspath(os.path.join(self.base_path, '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/Structure.mo')) Args: structure_path (str, optional): Path to the model structure file. If not provided, uses default. parent_class (str): The base class used in the Modelica model. uniform (list, optional): A list indicating uniform settings. Defaults to [True, True]. """ # Validate the architecture self.validate_architecture() self.parent.set_model_path(os.path.abspath(os.path.join(self.parent.output_path,'Simulator.mo'))) # Update input parameters structure_path = structure_path or self.STRUCTURE_PATH.as_posix() try: if self.parent.architecture == 'nested': # Extract parameters from kwargs uniform = kwargs.pop('uniform', None) uniform = [True, True] if uniform is None or not uniform else uniform modelica_create_model_nested.main(self.parent.input_specification, self.parent.project_path, pathlib.Path(self.parent.project_path).name, parent_class, self.parent.model_path, structure_path, uniform=uniform) uniform) #TODO: Add new architecture methods here except Exception as e: raise RuntimeError(f"Failed to create model: {str(e)}") def create_fmu(self, dependencies=[], fmu_compiler='dymola', experimentSettings={}, **kwargs): def create_fmu(self, dependencies: list = None, fmu_compiler: str = 'dymola', experimentSettings: dict = None, **kwargs): """ Generates an FMU (Functional Mock-up Unit) using the specified compiler. Args: dependencies (list, optional): List of dependencies required for FMU generation. If not provided, uses defaults. fmu_compiler (str): Compiler to use for FMU generation. Defaults to 'dymola'. experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict. **kwargs: Additional keyword arguments passed to the FMU compiler method. """ # Validate the fmu compiler self.validate_fmu_compiler(fmu_compiler) if self.parent.project_path not in dependencies: dependencies.append(os.path.abspath(os.path.join(self.parent.project_path, 'package.mo'))) # Verify dependencies are valid dependencies = self.validate_and_convert_paths(dependencies) if 'ExaDigiT_AutoCSM/package.mo' not in dependencies: dependencies.append(os.path.abspath(os.path.join(self.base_path, '../../../methods/modelica/ExaDigiT_AutoCSM/package.mo'))) # Update input parameters dependencies = dependencies or [] experimentSettings = experimentSettings or {} try: # Adds package if not already in dependency list project_package = (pathlib.Path(self.parent.project_path) / 'package.mo').resolve() if project_package.as_posix() not in dependencies: dependencies.append(project_package.as_posix()) # This avoids redefining the ExaDigiT method template if it already was included in the dependency in a different location (e.g., a customized version) if '/'.join(list(self.EXADIGIT_PACKAGE_PATH.parts[-2])) not in dependencies: dependencies.append(self.EXADIGIT_PACKAGE_PATH.as_posix()) if fmu_compiler == 'dymola': modelica_create_fmu_dymola.main(dependencies, self.parent.model_path, pathlib.Path(self.parent.model_path).parent, experimentSettings, showwindow=False) No newline at end of file # Extract parameters from kwargs showwindow = kwargs.pop('showwindow', False) modelica_create_fmu_dymola.main(dependencies, self.parent.model_path, pathlib.Path(self.parent.output_path).resolve(), experimentSettings, showwindow) #TODO: Add new fmu compiler methods here except Exception as e: raise RuntimeError(f"Failed to create FMU: {str(e)}") No newline at end of file examples/data/input_specification_default.json 0 → 100644 +64 −0 Original line number Diff line number Diff line { "Name": "GenericCSM", "InstanceName":"simulator", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems": [ { "Name": "Datacenter", "InstanceName":"datacenter", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems":[ { "Name": "CoolingBlock", "InstanceName":"computeBlock", "Structure":{"n":10}, "ClassName":"v0", "SourceName":"NULL", "Systems":[ { "Name": "CDU", "InstanceName":"cdu", "ClassName":"v0", "Structure":{"n":1}, "SourceName":"NULL", "Systems":[{}] }, { "Name": "Cabinet", "Structure":{"n":3}, "InstanceName":"cabinet", "ClassName":"v0", "SourceName":"v0", "Systems":[{}] }] }] }, { "Name": "CentralEnergyPlant", "InstanceName":"centralEnergyPlant", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems":[ { "Name": "IntermediateLoop", "InstanceName":"intermediateLoop", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems":[{}] }, { "Name": "CoolingTowerLoop", "InstanceName":"coolingTowerLoop", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"v0", "Systems":[{}] }] }] } No newline at end of file examples/modelica/GenericCSM/Systems/Datacenter/BaseClasses/Summary.mo +4 −2 Original line number Diff line number Diff line Loading @@ -2,7 +2,9 @@ within GenericCSM.Systems.Datacenter.BaseClasses; model Summary extends ExaDigiT_AutoCSM.BaseClasses.Systems.PartialSummary; input SI.MassFlowRate m_flow_bypass = 0.0 "Bypass mass flow rate" annotation(Dialog(group="Inputs")); output Real V_flow_prim_GPM = m_flow_bypass/0.063 "Bypass volume flow rate in GPM" annotation(Dialog(group="Outputs", enable=false)); input SI.MassFlowRate m_flow_prim=0.0 "Bypass mass flow rate" annotation(Dialog(group="Inputs")); output Real V_flow_prim_GPM=m_flow_prim/0.063 "Bypass volume flow rate in GPM" annotation (Dialog(group="Outputs", enable=false)); end Summary; examples/modelica/GenericCSM/Systems/Datacenter/Models/v0.mo +2 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,8 @@ model v0 m_flow=from_gpm(1900.0)), redeclare replaceable Controls.CS_Constant controls, redeclare replaceable Data.NULL data, redeclare replaceable Sources.NULL sources); redeclare replaceable Sources.NULL sources, summary(m_flow_prim=sensor_m_flow.m_flow)); TRANSFORM.Fluid.Volumes.MixingVolume plenum_inlet( redeclare package Medium = Medium, Loading Loading
AutoCSM/auto_csm.py +187 −75 Original line number Diff line number Diff line # -*- coding: utf-8 -*- """ Created on Wed Jun 5 13:10:34 2024 Created on Fri Sep 20 11:54:58 2024 @author: fig """ import pathlib from languages.modelica.modelica_methods import ModelicaMethods from languages.julia.julia_methods import JuliaMethods import helper_functions import pathlib from helper_functions import Loader class AutoCSM: def __init__(self, language='modelica', architecture='nested', terminal_animation=True): self.language = language self.architecture = architecture self.language_methods = { 'modelica': ModelicaMethods(self), 'julia': JuliaMethods(self) } self.terminal_animation = terminal_animation def set_language(self, language): if language in self.language_methods: self.language = language else: raise ValueError(f"Unsupported language: {language}") """ AutoCSM class to automate the creation of simulation architectures and models. Attributes: language (str): Programming language for model generation ('modelica', 'julia'). architecture (str): Architecture type for the system ('nested'). terminal_animation (bool): Whether terminal animations are shown during processing. input_specification (str): Path to the input specification file. output_path (str): Path to store output files. project_path (str): Path to the project directory. model_path (str): Path to the output directory of model created during create_model(). """ def set_architecture(self, architecture): self.architecture = architecture # Class-level constant for language methods LANGUAGE_METHODS = { 'modelica': ModelicaMethods, 'julia': JuliaMethods } def set_input_specification(self, input_specification): self.input_specification = input_specification # ARCHITECTURES = ['nested'] # at level of the language def __init__(self, language: str = 'modelica', architecture: str = 'nested', terminal_animation: bool = True): self._language = None self._architecture = None self._terminal_animation = None self._input_specification = None self._output_path = None self._project_path = None def set_output_path(self, output_path): self.output_path = output_path # Internal variables self._model_path = None def set_model_path(self, model_path): self.model_path = model_path # Setting via the property to validate. self.language = language self.architecture = architecture self.terminal_animation = terminal_animation def set_project_path(self, project_path): # Remove package.mo if contained in project_path (i.e., a common issue) temp = pathlib.Path(project_path) @property def terminal_animation(self) -> str: """Returns the terminal_animation setting.""" return self._terminal_animation @terminal_animation.setter def terminal_animation(self, value: bool) -> None: """Sets the terminal_animation.""" self._terminal_animation = value @property def language(self) -> str: """Returns the current language setting.""" return self._language @language.setter def language(self, value: str) -> None: """Sets the language for model generation, with validation.""" if value not in self.LANGUAGE_METHODS: raise ValueError(f"Unsupported language: {value}") self._language = value self._language_method = self.LANGUAGE_METHODS[value](self) @property def architecture(self) -> str: """Returns the current architecture setting.""" return self._architecture @architecture.setter def architecture(self, value: str) -> None: """Sets the architecture type.""" self._architecture = value @property def input_specification(self) -> str: """Returns the input specification path.""" return self._input_specification @input_specification.setter def input_specification(self, path: str) -> None: """Sets and validates the input specification file path.""" if not pathlib.Path(path).is_file(): raise FileNotFoundError(f"Input specification file not found: {path}") self._input_specification = path @property def output_path(self) -> str: """Returns the output directory path.""" return self._output_path @output_path.setter def output_path(self, path: str) -> None: """Sets and validates the output directory path.""" output_dir = pathlib.Path(path) if not output_dir.exists(): output_dir.mkdir(parents=True, exist_ok=True) # Create directories if they don't exist self._output_path = output_dir.as_posix() @property def model_path(self) -> str: """Returns the output directory path.""" return self._model_path @model_path.setter def model_path(self, path: str) -> None: """Sets the path to the model file.""" self._model_path = path @property def project_path(self) -> str: """Returns the project path.""" return self._project_path @project_path.setter def project_path(self, path: str) -> None: """Sets the project path, ensuring it's not 'package.mo'.""" temp = pathlib.Path(path) if temp.name == "package.mo": temp = temp.parent self.project_path = str(temp) self._project_path = temp.as_posix() ## Sub-class methods def create_architecture(self, *args, **kwargs): if self.terminal_animation: with helper_functions.Loader("Creating the architecture..."): return self.language_methods[self.language].create_architecture(*args, **kwargs) else: print("Creating the architecture...") return self.language_methods[self.language].create_architecture(*args, **kwargs) def _execute_with_animation(self, message: str, method, *args, **kwargs): """ Executes a method with an optional terminal animation. def create_model(self, *args, **kwargs): Args: message (str): The message to display during animation. method (callable): The method to be executed. """ if self.terminal_animation: with helper_functions.Loader("Creating the model..."): return self.language_methods[self.language].create_model(*args, **kwargs) with Loader(message): return method(*args, **kwargs) else: print("Creating the model...") return self.language_methods[self.language].create_model(*args, **kwargs) print(message) return method(*args, **kwargs) def create_fmu(self, *args, **kwargs): if self.terminal_animation: with helper_functions.Loader("Creating the FMU..."): return self.language_methods[self.language].create_fmu(*args, **kwargs) else: print("Creating the FMU...") return self.language_methods[self.language].create_fmu(*args, **kwargs) def create_architecture(self, *args: tuple, **kwargs: dict) -> None: """ Creates the architecture based on the selected language and architecture. if __name__ == "__main__": Args: *args: Additional positional arguments passed to the architecture creation method. **kwargs: Additional keyword arguments passed to the architecture creation method. """ try: return self._execute_with_animation("Creating the architecture...", self._language_method.create_architecture, *args, **kwargs) except Exception as e: print(f"Failed to create the architecture: {str(e)}") raise def create_model(self, model_name='Simulator.mo',*args: tuple, **kwargs: dict) -> None: """ Creates the model for simulation. Args: *args: Additional positional arguments passed to the model creation method. **kwargs: Additional keyword arguments passed to the model creation method. """ self.model_path = pathlib.Path.as_posix(pathlib.Path(self.output_path) / model_name) # Input specification JSON file path json_file_path = '../examples/data/input_specification_v0.json' try: return self._execute_with_animation("Creating the model...", self._language_method.create_model, *args, **kwargs) except Exception as e: print(f"Failed to create the model: {str(e)}") raise # Output location of generated files output_path = '../temp' def create_fmu(self, *args: tuple, **kwargs: dict) -> None: """ Creates the Functional Mock-up Unit (FMU). # Path to the project which uses an AutoCSM language method project_path = '../examples/modelica/GenericCSM' Args: *args: Additional positional arguments passed to the FMU creation method. **kwargs: Additional keyword arguments passed to the FMU creation method. """ try: return self._execute_with_animation("Creating the FMU...", self._language_method.create_fmu, *args, **kwargs) except Exception as e: print(f"Failed to create the FMU: {str(e)}") raise # Model dependecies: List of libraries needed for FMU generation dependencies = [r'../../../Modelica/modelica-buildings/Buildings/package.mo', r'../../../Modelica/TRANSFORM-Library/TRANSFORM/package.mo'] if __name__ == "__main__": # Instantiate AutoCSM csm = AutoCSM(language='modelica', architecture='nested') # Load teh specification csm.set_input_specification(json_file_path) # Load the JSON specification csm.input_specification = '../examples/data/input_specification_v0.json' # Set the path to output created files csm.set_output_path(output_path) csm.output_path = '../temp' # This line creates an project with a file structure for the specified language and architecture csm.create_architecture() # more notes # This line creates a project with a file structure for the specified language and architecture csm.create_architecture() # Useful if a project is being created from scratch # Set the path to the project of interest csm.set_project_path(project_path) # Set the path to the project which uses an AutoCSM language method csm.project_path = '../examples/modelica/GenericCSM' # Create the model for export to FMU (i.e., Simulator.mo) csm.create_model(uniform=[False, False]) # Note this does not specify a solver in the model file. That is added in create_FMU. # Model dependencies: List of libraries needed for FMU generation dependencies = [ r'../../../Modelica/TRANSFORM-Library/TRANSFORM/package.mo' ] # Generate the FMU (i.e., Simulator.fmu) csm.create_fmu(dependencies) csm.create_fmu(dependencies, experimentSettings={'solver':'Sdirk34hw','tolerance':1e-5}) No newline at end of file
AutoCSM/languages/modelica/modelica_methods.py +151 −30 Original line number Diff line number Diff line Loading @@ -13,41 +13,162 @@ from . import modelica_create_fmu_dymola from . import modelica_create_architecture class ModelicaMethods: """ This class provides methods for Modelica-specific tasks such as creating architecture, generating models, and building FMUs. Attributes: parent (AutoCSM): The parent AutoCSM object that stores configuration details. base_path (str): The base path where Modelica templates are located. """ BASE_PATH = pathlib.Path(__file__).parent TEMPLATE_FOLDER = (pathlib.Path(BASE_PATH) / '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/TemplateSystem').resolve() STRUCTURE_PATH = (pathlib.Path(BASE_PATH) / '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/Structure.mo').resolve() EXADIGIT_PACKAGE_PATH = (pathlib.Path(BASE_PATH) / '../../../methods/modelica/ExaDigiT_AutoCSM/package.mo').resolve() SUPPORTED_ARCHITECTURES = ['nested'] SUPPORTED_FMU_COMPILERS = ['dymola'] def __init__(self, parent): self.parent = parent self.base_path = os.path.dirname(__file__) def create_architecture(self, template_folder=None): if template_folder is None: template_folder = os.path.abspath(os.path.join(self.base_path, '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/TemplateSystem')) def validate_architecture(self): """ Validates if the current architecture is supported. Raises: ValueError: If the architecture is not supported. """ if self.parent.architecture not in self.SUPPORTED_ARCHITECTURES: raise ValueError(f"Unsupported architecture: {self.parent.architecture}. " f"Supported architectures are: {self.SUPPORTED_ARCHITECTURES}") def validate_fmu_compiler(self, fmu_compiler): """ Validates if the current fmu compiler is supported. Raises: ValueError: If the fmu compiler is not supported. """ if fmu_compiler not in self.SUPPORTED_FMU_COMPILERS: raise ValueError(f"Unsupported fmu compiler: {fmu_compiler}. " f"Supported fmu compilers are: {self.SUPPORTED_FMU_COMPILERS}") def validate_and_convert_paths(self, paths: list) -> list: """ Validates a list of file paths, converts them to absolute paths, and checks if they exist. Args: paths (list): List of file paths (relative or absolute). Returns: list: List of dependencies as absolute paths. Raises: FileNotFoundError: If any of the file paths do not exist. """ absolute_paths = [] for path in paths: abs_path = pathlib.Path(path).resolve() # Convert to absolute path if not abs_path.exists(): # Check if the file exists raise FileNotFoundError(f"The file '{abs_path}' does not exist.") absolute_paths.append(abs_path.as_posix()) return absolute_paths def create_architecture(self, template_folder: str = None): """ Creates the Modelica architecture by copying the template system structure. Args: template_folder (str, optional): Path to the template folder. If not provided, uses the default. """ # Validate the architecture self.validate_architecture() # Update input parameters template_folder = template_folder or self.TEMPLATE_FOLDER.as_posix() try: modelica_create_architecture.main(self.parent.input_specification, self.parent.output_path, template_folder, self.parent.architecture) except Exception as e: raise RuntimeError(f"Failed to create architecture: {str(e)}") def create_model(self, structure_path: str = None, parent_class: str = 'ExaDigiT_AutoCSM.BaseClasses.Tests.PartialTest', **kwargs): """ Creates the Modelica model for the given architecture. def create_model(self, structure_path=None, parent_class='ExaDigiT_AutoCSM.BaseClasses.Tests.PartialTest', uniform=[True,True]): if structure_path is None: structure_path = os.path.abspath(os.path.join(self.base_path, '../../../methods/modelica/ExaDigiT_AutoCSM/Templates/Structure.mo')) Args: structure_path (str, optional): Path to the model structure file. If not provided, uses default. parent_class (str): The base class used in the Modelica model. uniform (list, optional): A list indicating uniform settings. Defaults to [True, True]. """ # Validate the architecture self.validate_architecture() self.parent.set_model_path(os.path.abspath(os.path.join(self.parent.output_path,'Simulator.mo'))) # Update input parameters structure_path = structure_path or self.STRUCTURE_PATH.as_posix() try: if self.parent.architecture == 'nested': # Extract parameters from kwargs uniform = kwargs.pop('uniform', None) uniform = [True, True] if uniform is None or not uniform else uniform modelica_create_model_nested.main(self.parent.input_specification, self.parent.project_path, pathlib.Path(self.parent.project_path).name, parent_class, self.parent.model_path, structure_path, uniform=uniform) uniform) #TODO: Add new architecture methods here except Exception as e: raise RuntimeError(f"Failed to create model: {str(e)}") def create_fmu(self, dependencies=[], fmu_compiler='dymola', experimentSettings={}, **kwargs): def create_fmu(self, dependencies: list = None, fmu_compiler: str = 'dymola', experimentSettings: dict = None, **kwargs): """ Generates an FMU (Functional Mock-up Unit) using the specified compiler. Args: dependencies (list, optional): List of dependencies required for FMU generation. If not provided, uses defaults. fmu_compiler (str): Compiler to use for FMU generation. Defaults to 'dymola'. experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict. **kwargs: Additional keyword arguments passed to the FMU compiler method. """ # Validate the fmu compiler self.validate_fmu_compiler(fmu_compiler) if self.parent.project_path not in dependencies: dependencies.append(os.path.abspath(os.path.join(self.parent.project_path, 'package.mo'))) # Verify dependencies are valid dependencies = self.validate_and_convert_paths(dependencies) if 'ExaDigiT_AutoCSM/package.mo' not in dependencies: dependencies.append(os.path.abspath(os.path.join(self.base_path, '../../../methods/modelica/ExaDigiT_AutoCSM/package.mo'))) # Update input parameters dependencies = dependencies or [] experimentSettings = experimentSettings or {} try: # Adds package if not already in dependency list project_package = (pathlib.Path(self.parent.project_path) / 'package.mo').resolve() if project_package.as_posix() not in dependencies: dependencies.append(project_package.as_posix()) # This avoids redefining the ExaDigiT method template if it already was included in the dependency in a different location (e.g., a customized version) if '/'.join(list(self.EXADIGIT_PACKAGE_PATH.parts[-2])) not in dependencies: dependencies.append(self.EXADIGIT_PACKAGE_PATH.as_posix()) if fmu_compiler == 'dymola': modelica_create_fmu_dymola.main(dependencies, self.parent.model_path, pathlib.Path(self.parent.model_path).parent, experimentSettings, showwindow=False) No newline at end of file # Extract parameters from kwargs showwindow = kwargs.pop('showwindow', False) modelica_create_fmu_dymola.main(dependencies, self.parent.model_path, pathlib.Path(self.parent.output_path).resolve(), experimentSettings, showwindow) #TODO: Add new fmu compiler methods here except Exception as e: raise RuntimeError(f"Failed to create FMU: {str(e)}") No newline at end of file
examples/data/input_specification_default.json 0 → 100644 +64 −0 Original line number Diff line number Diff line { "Name": "GenericCSM", "InstanceName":"simulator", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems": [ { "Name": "Datacenter", "InstanceName":"datacenter", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems":[ { "Name": "CoolingBlock", "InstanceName":"computeBlock", "Structure":{"n":10}, "ClassName":"v0", "SourceName":"NULL", "Systems":[ { "Name": "CDU", "InstanceName":"cdu", "ClassName":"v0", "Structure":{"n":1}, "SourceName":"NULL", "Systems":[{}] }, { "Name": "Cabinet", "Structure":{"n":3}, "InstanceName":"cabinet", "ClassName":"v0", "SourceName":"v0", "Systems":[{}] }] }] }, { "Name": "CentralEnergyPlant", "InstanceName":"centralEnergyPlant", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems":[ { "Name": "IntermediateLoop", "InstanceName":"intermediateLoop", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"NULL", "Systems":[{}] }, { "Name": "CoolingTowerLoop", "InstanceName":"coolingTowerLoop", "Structure":{"n":1}, "ClassName":"v0", "SourceName":"v0", "Systems":[{}] }] }] } No newline at end of file
examples/modelica/GenericCSM/Systems/Datacenter/BaseClasses/Summary.mo +4 −2 Original line number Diff line number Diff line Loading @@ -2,7 +2,9 @@ within GenericCSM.Systems.Datacenter.BaseClasses; model Summary extends ExaDigiT_AutoCSM.BaseClasses.Systems.PartialSummary; input SI.MassFlowRate m_flow_bypass = 0.0 "Bypass mass flow rate" annotation(Dialog(group="Inputs")); output Real V_flow_prim_GPM = m_flow_bypass/0.063 "Bypass volume flow rate in GPM" annotation(Dialog(group="Outputs", enable=false)); input SI.MassFlowRate m_flow_prim=0.0 "Bypass mass flow rate" annotation(Dialog(group="Inputs")); output Real V_flow_prim_GPM=m_flow_prim/0.063 "Bypass volume flow rate in GPM" annotation (Dialog(group="Outputs", enable=false)); end Summary;
examples/modelica/GenericCSM/Systems/Datacenter/Models/v0.mo +2 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,8 @@ model v0 m_flow=from_gpm(1900.0)), redeclare replaceable Controls.CS_Constant controls, redeclare replaceable Data.NULL data, redeclare replaceable Sources.NULL sources); redeclare replaceable Sources.NULL sources, summary(m_flow_prim=sensor_m_flow.m_flow)); TRANSFORM.Fluid.Volumes.MixingVolume plenum_inlet( redeclare package Medium = Medium, Loading