Commit 2ea65b45 authored by Greenwood, Scott's avatar Greenwood, Scott
Browse files

[refactor] add parent language method class to help accelerate development of...

[refactor] add parent language method class to help accelerate development of new language methods. add copyright
parent 96ef1d67
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 20 11:54:58 2024
@author: Scott Greenwood

@author: fig
Copyright (c) 2024 UT-Battelle
Licensed under the terms of both the MIT license and the Apache License (Version 2.0).
Users may choose either license, at their discretion.
"""

import pathlib
+5 −2
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
Created on Thu May 30 17:26:47 2024
@author: Scott Greenwood

@author: fig
Copyright (c) 2024 UT-Battelle
Licensed under the terms of both the MIT license and the Apache License (Version 2.0).
Users may choose either license, at their discretion.
"""

import json

def read_json(file_path):
+126 −18
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
Created on Wed Jun  5 13:45:13 2024
@author: Scott Greenwood

@author: fig
Copyright (c) 2024 UT-Battelle
Licensed under the terms of both the MIT license and the Apache License (Version 2.0).
Users may choose either license, at their discretion.
"""

# Import Julia-specific dependencies here
# Example: from julia_package import JuliaClass
# from .additional_julia_method import additional_method
import os
import pathlib

from ..language_method import LanguageMethod
# Import Julia-specific dependencies here, for example:
# from . import julia_create_model_nested
# from . import julia_create_fmu_fmijl
# from . import julia_create_architecture

class JuliaMethods(LanguageMethod):
    """
    Implements Julia-specific methods for creating architecture, generating models, and building FMUs.
    
    Attributes:
        METHOD_PATH (Path): The path where the Julia method to use is located.
        TEMPLATE_FOLDER (Path): Path to the folder containing the Modelica template system.
        STRUCTURE_PATH (Path): Path to the file defining the Modelica structure component.
        SUPPORTED_ARCHITECTURES (list): Architectures supported by Modelica.
        SUPPORTED_FMU_COMPILERS (list): FMU compilers supported by Modelica.
    """

    METHOD_PATH = (pathlib.Path(LanguageMethod.BASE_PATH) / '../../methods/julia/UPDATEME').resolve()
    TEMPLATE_FOLDER = (METHOD_PATH / 'Templates/UPDATEME').resolve()
    STRUCTURE_PATH = (METHOD_PATH / 'Templates/UPDATEME.mo').resolve()

    SUPPORTED_ARCHITECTURES = ['UPDATEME']
    SUPPORTED_FMU_COMPILERS = ['UPDATEME']
    
class JuliaMethods:
    def __init__(self, parent):
        self.parent = parent
        super().__init__(parent)  # Call the parent class initializer
             
    def create_architecture(self, template_folder: str = None, **kwargs):
        """
        Creates the Julia architecture by copying the template system structure.
        
        Args:
            template_folder (str, optional): Path to the template folder. If not provided, uses the default.
            **kwargs: Additional keyword arguments for creating the architecture.
        
        Raises:
            RuntimeError: If the architecture creation fails.
        """
        # Call the parent logic
        super().create_architecture(template_folder)
        
        try:
            # Insert supported methods here, for example:
            # julia_create_architecture.main(self.parent.input_specification,
            #                                   self.parent.output_path,
            #                                   template_folder,
            #                                   self.parent.architecture)
            pass
        except FileNotFoundError as e:
            raise RuntimeError(f"Template not found: {e}")
        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 specified architecture.
        
        Args:
            structure_path (str, optional): Path to the model structure file. Defaults to self.STRUCTURE_PATH.
            parent_class (str): The base example model for constructing the model.
            uniform (list, optional): A list indicating uniform settings. Defaults to [True, True].
            **kwargs: Additional keyword arguments for model creation.
        
        Raises:
            RuntimeError: If the model creation fails.
        """
        # Call the parent logic
        structure_path = super().create_model(structure_path, **kwargs)
        
        try:
            # Insert supported methods here, for example:
            # 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
                
            #     julia_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)
        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_fmu(self, dependencies: list = None, fmu_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'.
            experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict.
            **kwargs: Additional keyword arguments for FMU creation.
        
        Raises:
            RuntimeError: If FMU creation fails.
        """

        dependencies, experimentSettings = super().create_fmu(dependencies, fmu_compiler, experimentSettings, **kwargs)
    
    def read_input(self, file_path, *args, **kwargs):
        print(f"Julia: Reading input from {file_path} with architecture: {self.parent.architecture}, args: {args}, kwargs: {kwargs}")
        # Add Julia-specific read input implementation here
        # additional_method()
        # Use this to populate default dependencies
        # dependencies.append()
            
    def create_model(self, model_name, *args, **kwargs):
        print(f"Julia: Creating model {model_name} with architecture: {self.parent.architecture}, args: {args}, kwargs: {kwargs}")
        # Add Julia-specific create model implementation here
        try:
            # Insert supported methods here, for example:
            # if fmu_compiler == 'dymola':
            #     # Extract parameters from kwargs
            #     showwindow = kwargs.pop('showwindow', False)
                
    def create_fmu(self, fmu_name, *args, **kwargs):
        print(f"Julia: Creating FMU {fmu_name} with architecture: {self.parent.architecture}, args: {args}, kwargs: {kwargs}")
        # Add Julia-specific create FMU implementation here
            #     julia_create_fmu_fmijl.main(dependencies, 
            #                                     self.parent.model_path, 
            #                                     pathlib.Path(self.parent.output_path).resolve(),
            #                                     experimentSettings, 
            #                                     showwindow)
        except FileNotFoundError as e:
            raise RuntimeError(f"Dependency file not found: {e}")
        except Exception as e:
            raise RuntimeError(f"Failed to create FMU: {str(e)}")
 No newline at end of file
+162 −0
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
@author: Scott Greenwood

Copyright (c) 2024 UT-Battelle
Licensed under the terms of both the MIT license and the Apache License (Version 2.0).
Users may choose either license, at their discretion.
"""

import pathlib
from abc import ABC, abstractmethod

class LanguageMethod:
    """
    A base class for methods that extend from different language methods, such as Modelica.

    This class provides basic structure and validation for methods that are extended 
    by specific language methods (e.g., Modelica). 
    
    Child classes must implement the following abstract methods:
        - create_architecture()
        - create_model()
        - create_fmu()
    
    Attributes:
        parent (AutoCSM): The parent AutoCSM object.
        BASE_PATH (Path): Base path where python methods are located (for relative path reconstruction).
        METHOD_PATH (Path): Path to the ExaDigiT language method implmentation.
        TEMPLATE_PATH (Path): Path to folder containing the template folder structure.
        STRUCTURE_PATH (Path): Path to the file defining the structure component.
        SUPPORTED_ARCHITECTURES (list): Supported architectures for the method.
        SUPPORTED_FMU_COMPILERS (list): Supported FMU compilers for the method.
    """
    
    BASE_PATH = pathlib.Path(__file__).parent
    METHOD_PATH = None # To be overridden by child classes
    TEMPLATE_PATH = None # To be overridden by child classes
    STRUCTURE_PATH = None # To be overridden by child classes
    
    SUPPORTED_ARCHITECTURES = []  # To be overridden by child classes
    SUPPORTED_FMU_COMPILERS = []  # To be overridden by child classes
    
    def __init__(self, parent):
        self.parent = parent

    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 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 compilers are: {self.SUPPORTED_FMU_COMPILERS}")

    def validate_and_convert_paths(self, paths: list) -> list:
        """
        Validates and converts a list of file paths to absolute paths.
        
        Args:
            paths (list): List of file paths (relative or absolute).
        
        Returns:
            list: List of validated absolute paths.
        
        Raises:
            FileNotFoundError: If any file path does not exist.
        """
        absolute_paths = []
        
        for path in paths:
            abs_path = pathlib.Path(path).resolve()
            if not abs_path.exists():
                raise FileNotFoundError(f"The file '{abs_path}' does not exist.")
            absolute_paths.append(abs_path.as_posix())
        
        return absolute_paths
    
    @abstractmethod
    def create_architecture(self, template_folder: str = None, **kwargs):
        """
        Abstract method to create a language-supported architecture by copying 
        the template system structure.
        
        Args:
            template_folder (str, optional): Path to the template folder. If not 
            provided, uses the default TEMPLATE_FOLDER.
        
        Note:
            This method must be implemented by child classes to handle architecture
            creation specific to the language (e.g., Modelica, etc.).
        """

        # Validate the architecture
        self.validate_architecture()
        
        # Update input parameters
        template_folder = template_folder or self.TEMPLATE_FOLDER.as_posix()
        
        return template_folder
    
    @abstractmethod
    def create_model(self, structure_path: str = None, **kwargs):
        """
        Creates the model using the selected architecture.
        
        Args:
            structure_path (str, optional): Path to the model structure file. If not provided, uses teh default STRUCTURE_PATH.
            parent_class (str): The base example model for constructing the model.
            **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.).
        """
        
        # Validate the architecture
        self.validate_architecture()
        
        # Update input parameters
        structure_path = structure_path or self.STRUCTURE_PATH.as_posix()
        
        return structure_path
    
    @abstractmethod
    def create_fmu(self, dependencies: list = None, fmu_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.
            experimentSettings (dict, optional): FMU experiment settings. Defaults to an empty dict.
            **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.).
        """
        # Validate the fmu compiler
        self.validate_fmu_compiler(fmu_compiler)
            
        # Verify dependencies are valid
        dependencies = self.validate_and_convert_paths(dependencies)
        
        # Update input parameters
        dependencies = dependencies or []
        experimentSettings = experimentSettings or {}
                        
        return dependencies, experimentSettings
+6 −3
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
Created on Fri May 24 11:41:24 2024
@author: Scott Greenwood

@author: fig
Copyright (c) 2024 UT-Battelle
Licensed under the terms of both the MIT license and the Apache License (Version 2.0).
Users may choose either license, at their discretion.

TODO: Could add additional layer details to recreate the nested model structure of the generic approach
    - This would add to the Structure and ControlBus the appropirate information
TODO: Could autopopulate model isntance in strcuture with parallel logic (if instanceNames provided)
TODO: Could autopopulate model instance in structure with parallel logic (if instanceNames provided)
"""

import os
import shutil
import json
Loading