Commit fb08014c authored by Greenwood, Scott's avatar Greenwood, Scott
Browse files

Merge branch 'OMEdit_compliance' into 'main'

OM Edit compliance and restructure

See merge request !3
parents 00ebbdb5 69778e10
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -149,6 +149,8 @@ class AutoCSM:
        """
        Creates the architecture based on the selected language and architecture.
        
        The output project architecture name is deterined from the JSON input file.

        Args:
            *args: Additional positional arguments passed to the architecture creation method.
            **kwargs: Additional keyword arguments passed to the architecture creation method.
+3 −3
Original line number Diff line number Diff line
@@ -110,13 +110,13 @@ class JuliaMethods(LanguageMethod):
        # overwrite_line_with_list(template_path, output_path, 'TODO_ADD_DEPENDENCIES_HERE', replacement)
        print('No create_setup currently implemented. Skipping create_setup.')
            
    def create_fmu(self, dependencies: list = None, fmu_compiler: str = 'dymola', experimentSettings: dict = None, **kwargs):
    def create_fmu(self, dependencies: list = None, compiler: str = 'dymola', experimentSettings: dict = None, **kwargs):
        """
        Generates an FMU using the specified compiler.
        
        Args:
            dependencies (list[str], optional): List of dependencies required for FMU generation.
            fmu_compiler (str, optional): Compiler to use for FMU generation. Defaults to 'dymola'.
            compiler (str, optional): Compiler to use for FMU generation. Defaults to 'dymola'.
            experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict.
            **kwargs: Additional keyword arguments for FMU creation.
        
@@ -124,7 +124,7 @@ class JuliaMethods(LanguageMethod):
            RuntimeError: If FMU creation fails.
        """

        dependencies, experimentSettings = super().create_fmu(dependencies, fmu_compiler, experimentSettings, **kwargs)
        dependencies, experimentSettings = super().create_fmu(dependencies, compiler, experimentSettings, **kwargs)
    
        # Add required project and template dependencies
        dependencies = super()._add_required_dependencies(dependencies)
+6 −6
Original line number Diff line number Diff line
@@ -54,15 +54,15 @@ class LanguageMethod:
            raise ValueError(f"Unsupported architecture: {self.parent.architecture}. "
                              f"Supported architectures are: {self.SUPPORTED_ARCHITECTURES}")
    
    def validate_fmu_compiler(self, fmu_compiler):
    def validate_fmu_compiler(self, compiler):
        """
        Validates if the 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}. "
        if compiler not in self.SUPPORTED_FMU_COMPILERS:
            raise ValueError(f"Unsupported FMU compiler: {compiler}. "
                             f"Supported compilers are: {self.SUPPORTED_FMU_COMPILERS}")

    def validate_and_convert_paths(self, paths: list) -> list:
@@ -170,13 +170,13 @@ class LanguageMethod:
        return dependencies
    
    @abstractmethod
    def create_fmu(self, dependencies: list = None, fmu_compiler: str = '', experimentSettings: dict = None, **kwargs):
    def create_fmu(self, dependencies: list = None, compiler: str = '', experimentSettings: dict = None, **kwargs):
        """
        Generates an FMU (Functional Mock-up Unit) using the specified compiler.
        
        Args:
            dependencies Optional[List[str]] List of dependencies required for FMU generation. If not provided, uses defaults.
            fmu_compiler (str): Compiler to use for FMU generation.
            compiler (str): Compiler to use for FMU generation.
            experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict.
            **kwargs: Additional keyword arguments passed to the method.
            
@@ -185,7 +185,7 @@ class LanguageMethod:
            creation specific to the language (e.g., Modelica, etc.).
        """
        # Validate the fmu compiler
        self.validate_fmu_compiler(fmu_compiler)
        self.validate_fmu_compiler(compiler)
            
        # Verify dependencies are valid
        if dependencies is not None:
+32 −9
Original line number Diff line number Diff line
@@ -51,10 +51,11 @@ def sources_line(data, project_path, base_path, model_class_path, key_path, unif
    temp_path = pathlib.Path(project_path).parts[:-1]
    
    model_path_list = list(temp_path) + model_class_path.split('.')[0:-2] + ['Models'] + [data['ModelClass'] + '.mo']
    model_path = os.path.join(*model_path_list)
    model_path = pathlib.Path(*model_path_list)
    
    # Find the default by searching for "redeclare replaceable Sources.* sources"
    default_source_class = parse_files.extract_default_class_from_model(model_path, 'Sources', 'source')[0]  
    default_source_class = parse_files.extract_default_class_from_model(model_path.as_posix(), 'Sources', 'sources')
    default_source_class = default_source_class if default_source_class == [] else default_source_class[0]
    
    # If 'SourceClass' is not in in the input file and is not the default, redeclare
    if 'SourceClass' in data.keys() and default_source_class != data['SourceClass']:
@@ -106,7 +107,7 @@ def sources_line(data, project_path, base_path, model_class_path, key_path, unif
    line += f'sources({temp if temp else ""})\n'
    return line

def create_nested_lines(data, project_path, base_path, uniform=[True,True], lines=None, is_top_system=True, key_path=None, level=0):
def create_nested_lines(data, project_path, base_path, uniform=[True,True], lines=None, is_top_system=True, key_path=None, level=0, last_modelClass=None, force_redeclare=True):

    """
    Creates a list of lines representing the structure of the Modelica model.
@@ -114,10 +115,14 @@ def create_nested_lines(data, project_path, base_path, uniform=[True,True], line
    Args:
        data (dict): The input data dictionary.
        project_path (str): Path to the Modelica project folder.
        unifrom (list[2] bool): controls if structure [0] and sources [1] should be assumed uniform or individually
        lines (list, optional): An existing list of lines to append to. Defaults to None.
        is_top_system (bool, optional): Whether the current system is the top-level system. Defaults to True.
        structure_path (str, optional): The path to the structure template file.
        key_path (str) is the dotted path to the current point in hierarchy
        key_path (str): the dotted path to the current point in hierarchy
        level (int): the level into the hierarchy
        last_modelClass (string): the parent model class
        force_redeclare (bool): adds redeclare statement to file even when child class remains unchanged from the default value
    Returns:
        list: A list of lines representing the structure of the Modelica model.
    """
@@ -133,9 +138,25 @@ def create_nested_lines(data, project_path, base_path, uniform=[True,True], line
    else:
        base_path = '.'.join([base_path, 'Systems', data['Name']])

    model_class_path = '.'.join([base_path, 'Models', data['ModelClass']])
    model_instance_name = data['InstanceName']
    if is_top_system or'ModelClass' in data.keys():
        model_class_path = '.'.join([base_path, 'Models', data['ModelClass']])
        lines.append(f'redeclare {model_class_path} {model_instance_name}(')
    else:
        # If not specified in JSON, find default class from parent model (doesn't require redeclare b)
        temp_path = pathlib.Path(project_path).parts[:-1]
        model_path_list = list(temp_path) + base_path.split('.')[0:-2] + ['Models'] + [last_modelClass + '.mo']
        model_path = pathlib.Path(*model_path_list)
        default_class = parse_files.extract_default_class_from_model(model_path.as_posix(), 'Models', model_instance_name)
        data['ModelClass'] = default_class if default_class == [] else default_class[0]
        model_class_path = '.'.join([base_path, 'Models', data['ModelClass']])
        
        if force_redeclare:
            lines.append(f'redeclare {model_class_path} {model_instance_name}(')
        else:
            lines.append(f'{model_instance_name}(')


    
    # Current system changes
    ## Structure
@@ -147,7 +168,7 @@ def create_nested_lines(data, project_path, base_path, uniform=[True,True], line
    # Next system changes
    for sub_dict in data['Systems']:
        if sub_dict:  # Terminate if dictionary is empty
            lines = create_nested_lines(sub_dict, project_path, base_path, uniform, lines, False, key_path, level+1)
            lines = create_nested_lines(sub_dict, project_path, base_path, uniform, lines, False, key_path, level+1, data['ModelClass'], force_redeclare)
    lines.append(')')

    return lines
@@ -178,7 +199,7 @@ def create_model_file(content, parent_class, output_path):
            mo_file.write(f'{var}\n')
        mo_file.write(f'end {temp_path.stem};')
        
def main(json_file_path, project_path, base_path, parent_class, output_path, structure_path, uniform=[False, False]):
def main(json_file_path, project_path, base_path, parent_class, output_path, structure_path, uniform=[False, False], force_redeclare=True):
    """
    Main function to create a Modelica model file based on the given input parameters.

@@ -188,6 +209,8 @@ def main(json_file_path, project_path, base_path, parent_class, output_path, str
        parent_class (str): The parent class to extend.
        output_path (str or pathlib.Path): The path to the output Modelica file.
        structure_path (str, optional): The path to the structure template file (contains the common structural parameters).
        unifrom (list[2] bool): controls if structure [0] and sources [1] should be assumed uniform or individually
        force_redeclare (bool): adds redeclare statement to file even when child class remains unchanged from the default value
    """
    data_orig = helper_functions.read_json(json_file_path)
    
@@ -198,7 +221,7 @@ def main(json_file_path, project_path, base_path, parent_class, output_path, str
    nested_structure_utils.expand_data(data, {item[2]: item[3] for item in default_structure_parameters})

    
    lines = create_nested_lines(data, project_path, base_path, uniform)
    lines = create_nested_lines(data, project_path, base_path, uniform, force_redeclare=force_redeclare)
    content = ','.join(lines).replace(',)', ')').replace('(,', '(')
    create_model_file(content, parent_class, output_path)
    return data
+20 −13
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ class ModelicaMethods(LanguageMethod):
        SUPPORTED_FMU_COMPILERS (list): FMU compilers supported by Modelica.
    """

    METHOD_PATH = (pathlib.Path(LanguageMethod.BASE_PATH) / '../../methods/modelica/ExaDigiT_AutoCSM').resolve()
    METHOD_PATH = (pathlib.Path(LanguageMethod.BASE_PATH) / '../../methods/modelica/TemplatesCSM').resolve()
    TEMPLATE_FOLDER = (METHOD_PATH / 'Templates/TemplateSystem').resolve()
    STRUCTURE_PATH = (METHOD_PATH / 'Templates/Structure.mo').resolve()

@@ -61,7 +61,7 @@ class ModelicaMethods(LanguageMethod):
        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):
    def create_model(self, structure_path: str = None, parent_class: str = 'TemplatesCSM.BaseClasses.Tests.PartialTest', **kwargs):
        """
        Creates the Modelica model for the specified architecture.
        
@@ -83,20 +83,23 @@ class ModelicaMethods(LanguageMethod):
                uniform = kwargs.pop('uniform', None)
                uniform = [True, True] if uniform is None or not uniform else uniform
                
                force_redeclare = kwargs.pop('force_redeclare', True)
                
                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,
                                                  force_redeclare)
            #TODO: Add new architecture methods here
        except FileNotFoundError as e:
            raise RuntimeError(f"File not found during model creation: {e}")
        except Exception as e:
            raise RuntimeError(f"Failed to create model: {str(e)}")
            
    def create_setup(self, dependencies: list = None, **kwargs):
    def create_setup(self, dependencies: list = None, compiler: str = 'dymola', **kwargs):
        """
        Generates an setup file (i.e., setup.mos).
        
@@ -116,18 +119,22 @@ class ModelicaMethods(LanguageMethod):
        # Add simulation file
        dependencies.append(pathlib.Path(self.parent.model_path).absolute().as_posix())
        
        template_path = (self.METHOD_PATH / '../template_setup.mos').as_posix()
        if compiler == 'dymola' or compiler == 'omedit':
            temp = '../template_setup_dymola.mos' if compiler=='dymola' else '../template_setup_omedit.mos'
            template_path = (self.METHOD_PATH / temp).as_posix()
            output_path = (pathlib.Path(self.parent.model_path).parent / 'setup.mos').as_posix()
            replacement = '{' + ',\n\t\t'.join(f'"{dep}"' for dep in dependencies) + '}'
            overwrite_line_with_list(template_path, output_path, 'TODO_ADD_DEPENDENCIES_HERE', replacement)
        else:
            raise ValueError('Unsupported compiler for setup')
            
    def create_fmu(self, dependencies: list = None, fmu_compiler: str = 'dymola', experimentSettings: dict = None, **kwargs):
    def create_fmu(self, dependencies: list = None, compiler: str = 'dymola', experimentSettings: dict = None, **kwargs):
        """
        Generates an FMU using the specified compiler.
        
        Args:
            dependencies (list[str], optional): List of dependencies required for FMU generation.
            fmu_compiler (str, optional): Compiler to use for FMU generation. Defaults to 'dymola'.
            compiler (str, optional): Compiler to use for FMU generation. Defaults to 'dymola'.
            experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict.
            **kwargs: Additional keyword arguments for FMU creation.
        
@@ -135,13 +142,13 @@ class ModelicaMethods(LanguageMethod):
            RuntimeError: If FMU creation fails.
        """
        # Verify dependencies
        dependencies, experimentSettings = super().create_fmu(dependencies, fmu_compiler, experimentSettings, **kwargs)
        dependencies, experimentSettings = super().create_fmu(dependencies, compiler, experimentSettings, **kwargs)
    
        # Add required project and template dependencies
        dependencies = super()._add_required_dependencies(dependencies)
            
        try:
            if fmu_compiler == 'dymola':
            if compiler == 'dymola':
                # Extract parameters from kwargs
                showwindow = kwargs.pop('showwindow', False)
                
Loading