Commit 19cf0779 authored by Greenwood, Scott's avatar Greenwood, Scott
Browse files

remove submodules in folders. should come from cloning submoudles

parent ae431a1e
Loading
Loading
Loading
Loading

submodules/AutoCSM/.gitignore

deleted100644 → 0
+0 −5
Original line number Diff line number Diff line
*.bak-mo
**/dsfinal.txt
temp/
*.zip
*.pyc
+0 −0

Empty file deleted.

+0 −255
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 languages.modelica.methods import ModelicaMethods
from languages.julia.methods import JuliaMethods
from helper_functions import Loader

class AutoCSM:
    """
    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().
    """

    # Class-level constant for language methods
    LANGUAGE_METHODS = {
        'modelica': ModelicaMethods,
        'julia': JuliaMethods
        }
 
    # 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
        self._project_name = None
        
        # Internal variables
        self._model_path = None
        
        # Setting via the property to validate.
        self.language = language
        self.architecture = architecture
        self.terminal_animation = terminal_animation
        
    @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'. Updates project_name if name doesn't match path"""
        temp = pathlib.Path(path).resolve()
        if temp.name == "package.mo":
            temp = temp.parent
        self._project_path = temp.as_posix()
        if self._project_name != temp.stem:
            print(f'Updating project name to match project path. Changed {self._project_name} to {temp.stem}')
            self._project_name = temp.stem
             
    @property
    def project_name(self) -> str:
        """Returns the project name."""
        return self._project_name

    @project_name.setter
    def project_name(self, name: str) -> None:
        """Sets the project name."""
        self._project_name = name
                    
    def _execute_with_animation(self, message: str, method, *args, **kwargs):
        """
        Executes a method with an optional terminal animation.
        
        Args:
            message (str): The message to display during animation.
            method (callable): The method to be executed.
        """
        if self.terminal_animation:
            with Loader(message):
                return method(*args, **kwargs)
        else:
            print(message)
            return method(*args, **kwargs)

    def create_architecture(self, *args: tuple, **kwargs: dict) -> None:
        """
        Creates the architecture based on the selected language and architecture.
        
        The output project architecture name is deterined from the project name if provided else the JSON file.

        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)
        
        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

    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).

        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

if __name__ == "__main__":

    # Instantiate AutoCSM
    csm = AutoCSM(language='modelica', architecture='nested')

    # Load the JSON specification
    csm.input_specification = '../examples/json/input_specification_v0.json'

    # Set the path to output created files
    csm.output_path = '../temp'

    csm.project_name = 'GenericDatacenter'
    
    # 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 which uses an AutoCSM language method 
    csm.project_path = '../examples/modelica/GenericDatacenter' + csm.project_name

    # 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, experimentSettings={'solver':'Sdirk34hw','tolerance':1e-5})
 No newline at end of file
+0 −230
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 json

def read_json(file_path):
    """
    Reads a JSON file from the specified file path and returns its contents as a dictionary.

    Parameters:
    file_path (str): The path to the JSON file.

    Returns:
    dict: The contents of the JSON file.
    """
    try:
        with open(file_path, 'r') as file:
            data = json.load(file)
        return data
    except FileNotFoundError:
        print(f"Error: The file {file_path} was not found.")
    except json.JSONDecodeError:
        print(f"Error: The file {file_path} is not a valid JSON file.")
    except Exception as e:
        print(f"An error occurred: {e}")

def export_dict_to_json(data, file_path):
    import json
     # Exporting to a JSON string
    json_string = json.dumps(data, indent=4)

    # Exporting to a JSON file
    with open(file_path, 'w') as f:
        json.dump(data, f, indent=4)
        
#%%
from itertools import cycle
from shutil import get_terminal_size
from threading import Thread
from time import sleep
class Loader:
    def __init__(self, desc="Loading...", end="Done!", timeout=0.1, steps='cycle'):
        """
        Source: https://stackoverflow.com/questions/22029562/python-how-to-make-simple-animated-loading-while-process-is-running
        A loader-like context manager

        Args:
            desc (str, optional): The loader's description. Defaults to "Loading...".
            end (str, optional): Final print. Defaults to "Done!".
            timeout (float, optional): Sleep time between prints. Defaults to 0.1.
        
        Example usage:
        with Loader("Loading with context manager..."):
            for i in range(10):
                sleep(0.25)

        loader = Loader("Loading with object...", "That was fast!", 0.05).start()
        for i in range(10):
            sleep(0.25)
        loader.stop()
        """
        self.desc = desc
        self.end = end
        self.timeout = timeout

        self._thread = Thread(target=self._animate, daemon=True)
        
        if steps == 'dots':
            self.steps = ["", "", "", "", "", "", "", ""]
        elif steps == 'cycle':
            self.steps = ['-', '/', '|', '\\']
        elif type(steps) == list:
            self.steps = steps
            
        self.done = False

    def start(self):
        self._thread.start()
        return self

    def _animate(self):
        for c in cycle(self.steps):
            if self.done:
                break
            print(f"\r{self.desc} {c}", flush=True, end="")
            sleep(self.timeout)

    def __enter__(self):
        self.start()

    def stop(self):
        self.done = True
        cols = get_terminal_size((80, 20)).columns
        print("\r" + " " * cols, end="", flush=True)
        print(f"\r{self.desc} {self.end}", flush=True)

    def __exit__(self, exc_type, exc_value, tb):
        # handle exceptions with those variables ^
        self.stop()
       
#%%
import time

class TicToc:
    '''
    # Example usage:
    tt = TicToc()
    tt.tic()
    time.sleep(2)  # Simulate a delay
    tt.toc()
    '''
    def __init__(self):
        self.start_time = None

    def tic(self):
        self.start_time = time.time()
        print("Tic...")

    def toc(self):
        if self.start_time is None:
            print("Toc: You need to call tic() first!")
        else:
            elapsed_time = time.time() - self.start_time
            print(f"Toc: {elapsed_time:.6f} seconds")
            self.start_time = None  # Reset start_time if you want to measure multiple intervals

if __name__ == "__main__":
    with Loader("Loading with context manager..."):
        for i in range(10):
            sleep(0.25)

    loader = Loader("Loading with object...", "That was fast!", 0.1, steps='cycle').start()
    for i in range(10):
        sleep(0.25)
    loader.stop()
    
#%%
import numpy as np

def create_new_array_with_renamed_fields(original_array, field_mapping, keep_original=None):
    # Extract the original dtype names and formats
    original_dtype = original_array.dtype
    original_fields = original_dtype.names
    
    # If keep_original is None, keep all original fields
    if keep_original is None:
        keep_original = original_fields
    
    # Create a new dtype list with the new field names
    new_dtype = []
    for field in original_fields:
        if field in keep_original:
            new_dtype.append((field, original_dtype[field]))
    
    # Add the new fields to the dtype list
    for old_field, new_field in field_mapping:
        new_dtype.append((new_field, original_dtype[old_field]))
    
    # Create the new array with the new dtype
    new_array = np.empty(original_array.shape, dtype=new_dtype)
    
    # Copy the data from the original fields to the new fields
    for field in original_fields:
        if field in keep_original:
            new_array[field] = original_array[field]
    
    for old_field, new_field in field_mapping:
        new_array[new_field] = original_array[old_field]
    
    return new_array

#%%
import re

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
+0 −0

Empty file deleted.

Loading