Commit 405a5152 authored by Greenwood, Scott's avatar Greenwood, Scott
Browse files

Merge branch 'main' of code.ornl.gov:exadigit/AutoCSM into OMEdit_compliance

parents 56abefa6 00ebbdb5
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -177,6 +177,21 @@ class AutoCSM:
            print(f"Failed to create the model: {str(e)}")
            raise

    def create_setup(self, *args: tuple, **kwargs: dict) -> None:
        """
        Creates the setup file for the simulation model.

        Args:
            *args: Additional positional arguments passed to the model creation method.
            **kwargs: Additional keyword arguments passed to the model creation method.
        """        
        try:
            return self._execute_with_animation("Creating the setup file...",
                                                self._language_method.create_setup, *args, **kwargs)
        except Exception as e:
            print(f"Failed to create the setup file: {str(e)}")
            raise
            
    def create_fmu(self, *args: tuple, **kwargs: dict) -> None:
        """
        Creates the Functional Mock-up Unit (FMU).
+47 −1
Original line number Diff line number Diff line
@@ -174,3 +174,49 @@ def convert_numbers_to_bracket_form(name):
    # Use regex to find numbers surrounded by underscores and replace with "[number]."
    new_name = re.sub(r'_(\d+)_', r'[\1].', name)
    return new_name
    
#%%
def overwrite_line_with_list(input_file, output_file, keyword, replacement, replaceLine=False):
    """
    Reads a text file, searches for a keyword, and either replaces the entire line or 
    just the keyword with the provided replacement. The modified content is saved to a new file.

    Parameters:
    -----------
    input_file : str
        The path to the input text file to be read.
    output_file : str
        The path to the output text file where the modified content will be written.
    keyword : str
        The keyword to search for in the lines of the text file.
    replacement : str or list of str
        The replacement content. If replacing a line, the replacement can be a string 
        or a list of strings (which will be joined with newline characters).
        If replacing the keyword within a line, this should be a string.
    replaceLine : bool, optional
        If True, replaces the entire line containing the keyword with the replacement content. 
        If False, only the keyword within the line is replaced. Defaults to False.

    Returns:
    --------
    None
    """
    # Open the input file for reading
    with open(input_file, 'r') as file:
        lines = file.readlines()

    # Replace the line containing the keyword with the replacement_list
    with open(output_file, 'w') as file:
        for line in lines:
            if keyword in line:
                if replaceLine:
                    # Write replacement in this line
                    file.write(replacement)
                else:
                   # Replace only the keyword in the line, keeping the rest of the line intact
                   new_line = line.replace(keyword, replacement)
                   file.write(new_line)
            else:
                # Write the line as-is if keyword not found
                file.write(line)
    
 No newline at end of file
+30 −0
Original line number Diff line number Diff line
@@ -83,6 +83,33 @@ class JuliaMethods(LanguageMethod):
        except Exception as e:
            raise RuntimeError(f"Failed to create model: {str(e)}")
            
    def create_setup(self, dependencies: list = None, **kwargs):
        """
        Generates an setup file for the simulation model.
        
        Args:
            dependencies (list[str], optional): List of dependencies required for FMU generation.
            **kwargs: Additional keyword arguments for FMU creation.
        
        Raises:
            RuntimeError: If FMU creation fails.
        """       
        # Verify dependencies
        dependencies = super().create_setup(dependencies, **kwargs)
        
        # Add required project and template dependencies
        dependencies = super()._add_required_dependencies(dependencies)

        # Add simulation file
        dependencies.append(pathlib.Path(self.parent.model_path).absolute().as_posix())
        
        template_path = (self.METHOD_PATH / '../UPDATEME.mos').as_posix()
        output_path = (pathlib.Path(self.parent.model_path).parent / 'UPDATEME.mos').as_posix()
        replacement = '{' + ',\n\t\t'.join(f'"{dep}"' for dep in dependencies) + '}'
        # Modify for language:
        # 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):
        """
        Generates an FMU using the specified compiler.
@@ -99,6 +126,9 @@ class JuliaMethods(LanguageMethod):

        dependencies, experimentSettings = super().create_fmu(dependencies, fmu_compiler, experimentSettings, **kwargs)
    
        # Add required project and template dependencies
        dependencies = super()._add_required_dependencies(dependencies)
        
        # Use this to populate default dependencies
        # TODO: dependencies.append()
            
+37 −1
Original line number Diff line number Diff line
@@ -88,6 +88,18 @@ class LanguageMethod:
        
        return absolute_paths
    
    def _add_required_dependencies(self, dependencies: list = None):
        # Adds project 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)
        exadigit_package  = self.METHOD_PATH if self.METHOD_PATH.parts[-1] == 'package.mo' else self.METHOD_PATH / 'package.mo'
        if '/'.join(list(exadigit_package.parts[-2:])) not in dependencies:
            dependencies.append(exadigit_package.as_posix())
        return dependencies
    
    @abstractmethod
    def create_architecture(self, template_folder: str = None, **kwargs):
        """
@@ -134,6 +146,29 @@ class LanguageMethod:
        
        return structure_path
    
    @abstractmethod
    def create_setup(self, dependencies: list = None, **kwargs):
        """
        Generates an setup file (e.g., setup.mos) to simplify use of the generated model (e.g., Simulator.mo) in non-fmu form.
        
        Args:
            dependencies Optional[List[str]] List of dependencies required for FMU generation. If not provided, uses defaults.
            **kwargs: Additional keyword arguments passed to the method.
            
        Note:
            This method must be implemented by child classes to handle architecture
            creation specific to the language (e.g., Modelica, etc.).
        """            

        # Verify dependencies are valid
        if dependencies is not None:
            dependencies = self.validate_and_convert_paths(dependencies)
        
        # Update input parameters
        dependencies = dependencies or []
        
        return dependencies
    
    @abstractmethod
    def create_fmu(self, dependencies: list = None, fmu_compiler: str = '', experimentSettings: dict = None, **kwargs):
        """
@@ -153,6 +188,7 @@ class LanguageMethod:
        self.validate_fmu_compiler(fmu_compiler)
            
        # Verify dependencies are valid
        if dependencies is not None:
            dependencies = self.validate_and_convert_paths(dependencies)
        
        # Update input parameters
+30 −11
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ from ..language_method import LanguageMethod
from . import create_model_nested
from . import create_fmu_dymola
from . import create_architecture
from helper_functions import overwrite_line_with_list

class ModelicaMethods(LanguageMethod):
    """
@@ -95,6 +96,31 @@ class ModelicaMethods(LanguageMethod):
        except Exception as e:
            raise RuntimeError(f"Failed to create model: {str(e)}")
            
    def create_setup(self, dependencies: list = None, **kwargs):
        """
        Generates an setup file (i.e., setup.mos).
        
        Args:
            dependencies (list[str], optional): List of dependencies required for FMU generation.
            **kwargs: Additional keyword arguments for FMU creation.
        
        Raises:
            RuntimeError: If FMU creation fails.
        """      
        # Verify dependencies
        dependencies = super().create_setup(dependencies, **kwargs)
        
        # Add required project and template dependencies
        dependencies = super()._add_required_dependencies(dependencies)

        # Add simulation file
        dependencies.append(pathlib.Path(self.parent.model_path).absolute().as_posix())
        
        template_path = (self.METHOD_PATH / '../template_setup.mos').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)
        
    def create_fmu(self, dependencies: list = None, fmu_compiler: str = 'dymola', experimentSettings: dict = None, **kwargs):
        """
        Generates an FMU using the specified compiler.
@@ -108,18 +134,11 @@ class ModelicaMethods(LanguageMethod):
        Raises:
            RuntimeError: If FMU creation fails.
        """

        # Verify dependencies
        dependencies, experimentSettings = super().create_fmu(dependencies, fmu_compiler, experimentSettings, **kwargs)
    
        # Adds project 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)
        exadigit_package  = self.METHOD_PATH if self.METHOD_PATH.parts[-1] == 'package.mo' else self.METHOD_PATH / 'package.mo'
        if '/'.join(list(exadigit_package.parts[-2:])) not in dependencies:
            dependencies.append(exadigit_package.as_posix())
        # Add required project and template dependencies
        dependencies = super()._add_required_dependencies(dependencies)
            
        try:
            if fmu_compiler == 'dymola':
Loading