diff --git a/Py4Mod/py4mod/DymInterface.py b/Py4Mod/py4mod/DymInterface.py new file mode 100644 index 0000000000000000000000000000000000000000..3dd365bf07d324f8001b197236a40db7ce02a45c --- /dev/null +++ b/Py4Mod/py4mod/DymInterface.py @@ -0,0 +1,192 @@ +import os +import re +from dymola.dymola_interface import DymolaInterface +from dymola.dymola_exception import DymolaException +from modelicares import SimRes +import numpy as np + +class DymInterface(object): + def __init__(self, dymola_path = "", use64bit = True, port = -1, showwindow = False, debug = False, allowremote = False, nolibraryscripts = False): + """ + Creates a wrapper for Dymola and starts Dymola. + + @param str dymolapath: The path to ``Dymola.exe``. Default is an empty string, which means that the path should be auto-detected. + @param bool use64bit: If ``True``, the 64-bit Dymola is used. If ``False``, the 32-bit Dymola is used. Default is ``True``. + @param int port: The port number to use for connecting to Dymola. Default is ``-1``, which means that a port should be auto-assigned. + @param bool showwindow: This flag controls whether the Dymola window will be visible or not when calling functions in the interface. The default value is ``False``, which means that Dymola will be hidden. + @param bool debug: For debugging the Dymola Python interface. Note that it is intended for debugging the interface, not Modelica models. Setting this variable to ``True`` outputs debug messages in the console. For advanced users only. + @param bool allowremote: By default, Dymola only allows local connections. The default value is ``False``. Set this flag to ``True`` to allow access from other machines. + @param bool nolibraryscripts: Corresponds to the command-line argument with the same name. The default value is ``False``. + @raises: :class:DymolaException <dymola.dymola_exception.DymolaException> + """ + #dymola parameters + self.__dymola = None #dymola wrapper object + self.__dymola_path = dymola_path + self.__use64bit = use64bit + self.__port = port + self.__showwindow = showwindow + self.__debug = debug + self.__allowremote = allowremote + self.__nolibraryscripts = nolibraryscripts + + #model variables + self.modelName="" + self.resultFileName="" + + try: + #Instantiate the Dymola interface and starts Dymola + self.__dymola = DymolaInterface(self.__dymola_path, self.__use64bit, self.__port, self.__showwindow, self.__debug, self.__allowremote, self.__nolibraryscripts) + + # Set Dymola options + self.__dymola.ExecuteCommand("experimentSetupOutput(events=false);") + self.__dymola.ExecuteCommand("Advanced.OutputModelicaCode:=true;") + self.__dymola.ExecuteCommand("Advanced.OutputModelicaCodeWithAliasVariables = true;") + #dymola.ExecuteCommand("Advanced.StoreProtectedVariables:=true;") + + except DymolaException as ex: + raise Exception(ex) + + + def changeWorkingDirectory(self, dir=""): + """ + Change directory to the given path + If the given path is the empty string, the function simply returns the current working directory + + @param str Dir: Directory to change to. If directory="" this function return the current directory + @raises: exception + """ + if dir=="": + test = self.__dymola.cd() + cwd=self.__dymola.getLastError().replace(' ',"").replace('=true', "").replace('\n',"") + return cwd + + if not os.path.exists(dir): + raise Except("File Error: " + os.path.abspath(fileName) + " does not exists!!!") + + self.__dymola.cd(dir) + + + def loadFile(self, fName, mustRead=True, changeDirectory=False): + """ + Reads the file specified by fName. + This corresponds to File->Open in the menus. + + @param str fName: File-path to open. + @param bool mustRead=False means that if the file already is opened/read, it is not opened/read again. + If mustRead=true in such a case the user is promted for removing the present one and open it again. + The value false can be useful in scriping, when only wanting to assure that a certain file has been read. + @param bool changeDirectory: If true we automatically change to the directory of the file. + @returns: true if successful + @raises: :class:DymolaException <dymola.dymola_exception.DymolaException> + """ + if not os.path.isfile(fName): + msg = "File Error: " + os.path.abspath(fileName) + " does not exists!!!" + raise Exception(msg) + if not self.__dymola.openModel(fName, mustRead, changeDirectory): + return False + return True + + + def dymApplyModifiers(self, modifierList): + """ Applies modifiers in modifierList, which is of format + modifierList=[['submodule1','param1','value1'],['submodule2','param2','value2'],...] + """ + #function not tested + cmdString="" + for i in range(len(modifierList)): + cmdString+= modifierList[i][0] + "." + modifierList[i][1] + "=" + str(modifierList[i][2]) + ";" + self.__dymola.ExecuteCommand(cmdString) + + + def buildModel(self, model): + """ + Compile the model (with current settings). + This corresponds to Translate in the menus + + @param str problem: Name of model, e.g. Modelica.Mechanics.Rotational.Components.Clutch. + @raises: Exception + + """ + self.modelName=model + try: + if not self.__dymola.translateModel(model): + msg = 'Translation failed. Below is the Translation log: \n' + self.__dymola.getLastError() + raise Exception(msg) + except DymolaException as err: + msg = 'Error in dymTranslate: {}'.format(err) + raise Exception(msg) + + + def simulateModel(self, model, startTime=0, stopTime=10, + numberOfIntervals=0, outputInterval=1, method='Dassl', tolerance=0.0001, + fixedstepsize=0.0, resultFile="dsres", initialNames=[], initialValues=[], + finalNames=[], autoLoad=False): + """Simulates the model in the mode specified in simMode. The results are stored in + files with the name resultName. It returns a list of the Variable Names andanother + list with the values they had at the start of the simulation. + + @param str model: Name of model, e.g. Modelica.Mechanics.Rotational.Components.Clutch. + @param float startTime: Start of simulation. + @param float stopTime: End of simulation. + @param int numberOfIntervals: Number of output points. + @param float outputInterval: Distance between output points. + @param str method: Integration method. + @param float tolerance: Tolerance of integration. + @param float fixedstepsize: Fixed step size for Euler. + @param str resultFile: Where to store result. + @param str[] initialNames: Parameters and start-values to set. Dimension ``[:] + """ + try: + # starts simulation + result = self.__dymola.simulateExtendedModel(model, startTime, stopTime, + numberOfIntervals, outputInterval, method, tolerance, + fixedstepsize, resultFile, initialNames, initialValues, + finalNames, autoLoad) + self.resultFileName=resultFile + + if not result: + err_msg = "Simulation failed. Below is the simulation log:\n" + self.__dymola.getLastError() + raise Exception(err_msg) + + except DymolaException as err: + err_msg = str("Dymola failed. Below is the log:\n" + str(err)) + raise Exception(err_msg) + + + def getResultsFast(self, outputVarList=[]): + """ Returns the trajectories for the variables in outputVarList in + the format of numpy.array by using ModelicaRes + + This Method can work faster than getSolutions() if the number of solutions is big + """ + file = os.path.join(self.changeWorkingDirectory(""), self.resultFileName + ".mat") + sim=SimRes(file) + arrTraj=[] + for item in outputVarList: + if np.array_equal(sim('Time').samples.values,sim(item).samples.times): + arrTraj.append(sim(item).samples.values) + else: + arrTraj.append(np.array(np.interp(sim('Time').samples.values,sim(item).samples.times,sim(item).samples.values),'float32')) + return np.array(arrTraj) + + + def getResults(self, outputVarList = []): + """ Returns the trajectories for the variables in outputVarList in + the format of numpy.array by using DymolaInterface in the same order as outputVarList + """ + sizeTraj = self.__dymola.readTrajectorySize(self.resultFileName + ".mat") + arrTraj = np.array(self.__dymola.readTrajectory(self.resultFileName + ".mat", outputVarList, sizeTraj)) + return arrTraj + + + def getResultVarNames(self): + """ Returns the variablen names from the simulation result.""" + varNames = self.__dymola.readTrajectoryNames(self.resultFileName + ".mat") + varNames.sort() + return varNames + + + def close(self): + if self.__dymola is not None: + self.__dymola.close() + self.__dymola = None \ No newline at end of file diff --git a/Py4Mod/py4mod/ModelicaModel.py b/Py4Mod/py4mod/ModelicaModel.py new file mode 100644 index 0000000000000000000000000000000000000000..7fb8d803debe4b6c9ff7fe34a88396fa310c153c --- /dev/null +++ b/Py4Mod/py4mod/ModelicaModel.py @@ -0,0 +1,693 @@ +from shutil import copyfile +from shutil import copy +from bisect import bisect +import numpy as np +import re +import ast +# moved interface imports for Dymola and OM to create Interface to allow for OMInterface use with Python 3.x + +class ModelicaModel(object): + def __init__(self, model, mainPath): + """Constructor of the class ModelicaModel, reads and format a model that + is given by the path of the project and the Name it has in this Project + """ + self._model = model # model identifier in package hierarchy [e.g. packageName.subPackageName.modelName] + self._mainPath = mainPath # path where either the model file itself or the corresponding package.mo can be found + if not self._mainPath[-1] == "\\": + self._mainPath = self._mainPath + "\\" + self._modelName = self.getModelName() # actual name of model extracted from self._model + self._modelPath = self.__getModelPath() # complete path of the model file deduced from self._mainPath and self._model + self._modelLines = self.__readModel() + self._formatModel() + self._values = {} + self.interface = None #interface object + self.interaceName = "" #"DYMOLA" or "OPENMODELICA" + + + #-------------------------- FUNCTIONS TO USE DYMOLA OR OPENMODELICA -------------------------# + def createInterface(self, interface, **kwargs): + if interface=='DYMOLA': + from DymolaFunctions import DymInterface + + dymola_path = "" + use64bit = True + port = -1 + showwindow = False + debug = False + allowremote = False + nolibraryscripts = False + + for key, value in kwargs.items(): + if key=='dymola_path': + dymola_path=value + elif key=='use64bit': + use64bit=value + elif key=='port': + port=value + elif key=='showwindow': + showwindow=value + elif key=='debug': + debug=value + elif key=='allowremote': + allowremote=value + elif key=='nolibraryscripts': + nolibraryscripts=value + + self.interface = DymInterface(dymola_path, use64bit, port, showwindow, debug, allowremote, nolibraryscripts) + self.interfaceName=interface + + elif interface=='OPENMODELICA': + from OMInterface import OMInterface + self.interface = OMInterface() + for key,value in kwargs.items(): + if key=='om_path': + self.interface.setModelicaPath(value) + self.interfaceName=interface + + else: + raise Exception('The possible Interfaces are "{}" and "{}"'.format('DYMOLA', 'OPENMODELICA')) + + def changeWorkingDirectory(self, dir=""): + """ + Change directory to the given path + If the given path is the empty string, the function simply returns the current working directory + + @param str Dir: Directory to change to. If directory="" this function return the current directory + @raises: exception + """ + if self.interface is None: + raise Exception('The interface has not yet been initialized !!!') + + if dir=="": + return self.interface.changeWorkingDirectory(dir) + + self.interface.changeWorkingDirectory(dir) + + def loadFile(self, fName): + """ + Reads the file specified by fName. + This corresponds to File->Open in the menus. + + @param str fName: File-path to open. + @raises: Exception() + """ + if self.interface is None: + raise Exception('The interface has not yet been initialized !!!') + + self.interface.loadFile(fName) + + def buildModel(self, **kwargs): + """ + builds self._model by generating c code and build it. + + other arguments for modelica interface: + @input float startTime: the start time of the simulation. <default> = 0.0 + @input float stopTime: the stop time of the simulation. <default> = 1.0 + @input float numberOfIntervals: number of intervals in the result file. <default> = 500 + @input float tolerance: tolerance used by the integration method. <default> = 1e-6 + @input str method: integration method used for simulation. <default> = "dassl" + @input str fileNamePrefix: fileNamePrefix. <default> = "" + @input str options: options. <default> = "" + @input str outputFormat: Format for the result file. <default> = "mat" + @input str variableFilter: Filter for variables that should store in result file. <default> = .* + @input str cflags: compiler flags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/omchelptext.html) + @input str simflags: simflags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/simulationflags.html) + """ + if self.interfaceName=='OPENMODELICA': + self.interface.buildModel(self._model, **kwargs) + + elif self.interfaceName=='DYMOLA': + self.interface.buildModel(self._model) + + elif self.interface is None: + raise Exception('The interface has not yet been initialized !!!') + + def simulate(self, **kwargs): + """ + DYMOLA: Simulates self._model + optional arguments for the dymola interface: + @param float startTime: Start of simulation. + @param float stopTime: End of simulation. + @param int numberOfIntervals: Number of output points. + @param float outputInterval: Distance between output points. + @param str method: Integration method. + @param float tolerance: Tolerance of integration. + @param float fixedstepsize: Fixed step size for Euler. + @param str resultFile: Where to store result. + @param str[] initialNames: Parameters and start-values to set. Dimension ``[:] + + MODELICA: simulates or re-simulate the model that was compiled with ModelicaModel.buildModel() + - optional arguments for the modelica interface + @input str simFlags: simflags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/simulationflags.html) + Note: Model must be built before this function can be called! + """ + + if self.interfaceName=='DYMOLA': + startTime=0 + stopTime=10 + numberOfIntervals=0 + outputInterval=1 + method='Dassl' + tolerance=0.0001 + fixedstepsize=0.0 + resultFile="dsres" + initialNames=[] + initialValues=[] + finalNames=[] + autoLoad=False + + for key, value in kwargs.items(): + if key=='startTime': + startTime=value + elif key=='stopTime': + stopTime=value + elif key=='numberOfIntervals': + numberOfIntervals=value + elif key=='outputInterval': + outputInterval=value + elif key=='method': + method=value + elif key=='tolerance': + tolerance=value + elif key=='fixedstepsize': + fixedstepsize=value + elif key=='resultFile': + resultFile=value + elif key=='initialNames': + initialNames=value + elif key=='initialValues': + initialValues=value + elif key=='finalNames': + finalNames=value + elif key=='autoLoad': + autoLoad=value + + self.interface.simulateModel(self._model, startTime, stopTime, numberOfIntervals, + outputInterval, method, tolerance, fixedstepsize, resultFile, + initialNames, initialValues, finalNames, autoLoad) + + elif self.interfaceName=='OPENMODELICA': + simflags=None + for key, value in kwargs.items(): + if key=='simflags': + simflags=value + + self.interface.simulate(simflags) + + elif self.interface is None: + raise Exception('The interface has not yet been initialized !!!') + + def getResults(self, outputVarList = []): + if self.interface is None: + raise Exception('The interface has not yet been initialized !!!') + + return self.interface.getResultsFast(outputVarList) + + def getResultVarNames(self): + if self.interface is None: + raise Exception('The interface has not yet been initialized !!!') + + return self.interface.getResultVarNames() + + def close(self): + """ + Terminate execution of the interface + """ + if self.interfaceName=='OPENMODELICA': + self.interface.buildModel(self._model, **kwargs) + + self.interface.close() + self.interface=None + + + #-------------------------- FUNCTIONS TO WORK ON MODELICA CODE ------------------------------# + def getSubmodelNames(self, subModelType): + """Searches every instance of the type subModelType and returns a list with + the names they have in the model. + """ + subModelNames = [] + for i in range(self.__var_start, self.__var_end +1): + mo = re.search(subModelType + r" +(\w+)[ (]?", self._modelLines[i]) + if mo: + name = mo.group(1) + subModelNames.append(name) + return subModelNames + + def getSubmodelModifierValue(self,listSubmodelNames,modifierName): + modValue = [] + positions=[] + for submodelName in listSubmodelNames: + positions.append(self.__getLinePositionName(submodelName)) + for pos in positions: + mo = re.search(modifierName + r"=(\d+)", self._modelLines[pos]) + if mo: + val = mo.group(1) + modValue.append(val) + return modValue + + def getSubModelTypesAll(self): + """ Returns a list of all submodeltypes that the model contains + """ + submodeltypes= [] + for i in range(self.__var_start,self.__var_end): + mo = re.search(r" *(?:inner)? ([\w\.]+) +(?:\w+)[ (]?", self._modelLines[i]) + if mo: + submodeltype = mo.group(1) + if not (submodeltype in submodeltypes): + submodeltypes.append(submodeltype) + return submodeltypes + + + def changeSubmodelTypeAll(self, oldType, newType, positions = []): + """Changes the type of all submodels with the type oldType to the type newType. + ATTENTION: this can lead to Problems if the equations (e.g. connections) don't + match with the new type. + """ + if positions == []: + positions = self.__getLinePositionSubmodel(oldType) + for i in positions: + mo = re.search(oldType + r" ", self._modelLines[i]) + start = mo.start() + end = mo.end() - 1 + self._modelLines[i] = self._modelLines[i][:start] + newType + self._modelLines[i][end:] + self.__writeModel() + + def changeSubmodelType(self, listSubmodelNames, oldType, newType): + """Changes the type of all submodels in listSubmodelNames from oldType to type newType. + ATTENTION: this can lead to Problems if the equations (e.g. connections) don't + match with the new type. + """ + positions=[] + for submodelName in listSubmodelNames: + positions.append(self.__getLinePositionName(submodelName)) + for pos in positions: + mo = re.search(oldType + r" ", self._modelLines[pos]) + start = mo.start() + end = mo.end() - 1 + self._modelLines[pos] = self._modelLines[pos][:start] + newType + self._modelLines[pos][end:] + self.__writeModel() + + def changeSubmodelName(self, dictSubmodelNames): + """Changes the names of the submodels in the declaration according to dictSubmodelNames, which includes the + renaming entries in the manner {"oldSubmodelName":"newSubmodelName"} + ATTENTION: corresponding connections, which include the old submodel name, must be changed seperatedly by using changeConnection + or changeConnectionPin + """ + for submodelNameOld,submodelNameNew in dictSubmodelNames.iteritems(): + pos = self.__getLinePositionName(submodelNameOld) + self._modelLines[pos] = re.sub(submodelNameOld,submodelNameNew,self._modelLines[pos]) + self.__writeModel() + + def changeVars(self, varList, overwrite = True): + """Changes or adds the initial values of variables/parameters that are specified + in the variable-section of the model. + The list varList contains the variables and the values that should be set. + The items stored in varList have the form of ["Submodel.Subsubmodel", value] + if you set the value of a differential variable the variable name has to end + with ".start". + E.g. if you want to change the startvalue of zLoad.v.re to 4, varList + has to contain the item ["zLoad.v.re.start", 4]. + If the overwrite flag is set to False, existing Values won't be changed. + """ + for item in varList: + name = item[0] + mo = re.search(r"^(\w+)\.", name) + mainName = mo.group(1) + pos = self.__getLinePositionName(mainName) + line = self._modelLines[pos] + self.__addLinetoValues(mainName) + self.__addtoValues(item) + #mo = re.search(r"^(\s+\w* *\w[\w\.]+\w) ", line) + mo = re.search(r"^(\s*\w* *\w[\w\.]+\w) ", line) + prefix = mo.group(1) + line = prefix + " " + self.__writeVariableString(self._values[mainName], mainName) + "\n" + self._modelLines[pos] = line + + self.__writeModel() + + def getValues(self, varList): + """Returns a list of the current initial values of the variables and parameters given in varList.""" + values = [] + for item in varList: + name = item + mo = re.search(r"^(\w+)\.", name) + mainName = mo.group(1) + self.__addLinetoValues(mainName) + value = self.__searchinValues(item) + values.append(value) + return values + + def deleteValues(self, varList): + """Deletes the initial values for variables or parameters given in varList. + You can delete single values or a whole group of values. + E.g if you want to delete the value for zLoad.v.re and zLoad.v.im + var list can be ["zLoad.v.re", "zLoad.v.im"] or ["zLoad.v"]. + """ + for item in varList: + name = item + mo = re.search(r"^(\w+)\.", name) + mainName = mo.group(1) + pos = self.__getLinePositionName(mainName) + line = self._modelLines[pos] + self.__addLinetoValues(mainName) + self.__deletefromValues(self._values, item.split(".")) + #mo = re.search(r"^(\s+\w* *\w[\w\.]+\w) ", line) + mo = re.search(r"^(\s*\w* *\w[\w\.]+\w) ", line) + prefix = mo.group(1) + + if mainName in self._values.keys(): + line = prefix + " " + self.__writeVariableString(self._values[mainName], mainName) + "\n" + else: + line = prefix + " " + mainName + "\n" + self._modelLines[pos] = line + self.__writeModel() + + def addConnection(self, pin1, pin2): + """Creates a new connection between pin1 and pin2 in the equation-section.""" + line = " connect(" + pin1 + "," + pin2 + ");\n" + self._modelLines.insert(self.__eq_end, line) + self.__eq_end = self.__eq_end + 1 + self.__writeModel() + + def getConnection(self, name): + """Returns the pins of connections which contain name. + Name can be either just a Submodel so that this function returns all connection that + exist for this Submodel specific connector. + The function returns two lists, the first list contains the pins that conatins name and the + second one contains the pin that are part of the connections. + """ + pin1 = [] + pin2 = [] + for i in range(self.__eq_start, self.__eq_end): + line = self._modelLines[i] + if "connect" in line and name in line: + mo = re.search(r"connect\( ?([\w\.\[\]]+) ?, ?([\w\.\[\]]+) ?\)", line) + if name in mo.group(1): + pin1.append(mo.group(1)) + pin2.append(mo.group(2)) + else: + pin1.append(mo.group(2)) + pin2.append(mo.group(1)) + return [pin1, pin2] + + def changeConnection(self, listConnectionPins): + """ + Changes the definition of a specific connection allowing to redefine both pins + The items stored in listConnectionPins must be in the form of ["oldConnectionPin1","oldConnectionPin2","newConnectionPin1","newConnectionPin2"] + """ + for i in range(self.__eq_start, self.__eq_end): + line = self._modelLines[i] + if "connect" in line and listConnectionPins[0] in line and listConnectionPins[1] in line: + line=re.sub(listConnectionPins[0],listConnectionPins[2],line) + self._modelLines[i]=re.sub(listConnectionPins[1],listConnectionPins[3],line) + self.__writeModel() + + def changeConnectionPin(self, listConnectionPin): + """ + Substitutes all the appearances of a specific connection pin by a new one + listConnectionPin should be in the form of ["oldConnectionPin","newConnectionPin"] + """ + for i in range(self.__eq_start, self.__eq_end): + line = self._modelLines[i] + if "connect" in line and listConnectionPin[0] in line: + self._modelLines[i]=re.sub(listConnectionPin[0],listConnectionPin[1],line) + self.__writeModel() + + def copyModelToDir(self, destDir="", postfix=""): + """Copys the model to the given directory and with the given postfix. At the destination directory no package integration is done. + The newly created model and its references are now stored in this class. + """ + + oldModelPath = self._modelPath + + self._mainPath=destDir + self._model=self._modelName + postfix # incorporates that the model is extracted from any package + self._modelPath = self.__getModelPath() + self._modelName = self.getModelName() + + copyfile(oldModelPath, self._modelPath) + + if self._modelLines[0][0:6] == "within": # remove package integration + self._modelLines[0]="within ;\n" + + self._modelLines[1] = "model " + self._modelName + "\n" + self._modelLines[-1] = "end " + self._modelName + ";" + + self.__writeModel() + + + def copyModelIntoPackage(self, newModelName): + """Copys the model and gives it a new name. + The newly created model is integrated in the original package and is now the one that is stored in this class. + """ + # Sets a new model name and creates the corresponding newly named file + mo = re.search(r"([\w\.]+\.)\w+$", self._model) + if not mo==None: + self._model = mo.group(1) + newModelName + else: + self._model = newModelName + self._modelName = self.getModelName() + + oldModelPath = self._modelPath + self._modelPath = self.__getModelPath() + copyfile(oldModelPath, self._modelPath) + + self._modelLines[1] = "model " + self._modelName + "\n" + self._modelLines[-1] = "end " + self._modelName + ";" + self.__writeModel() + + # Integrate newly create model in the package + packagePath = self._modelPath[:-(len(self._modelName + ".mo"))] + "package.order" + package = open(packagePath, "r") + packageData = [line for line in package] + package.close() + + if not(self._modelName + "\n" in packageData): + i = bisect(packageData, self._modelName + "\n") + packageData.insert(i, self._modelName + "\n") + + package = open(packagePath, "w") + package.writelines(packageData) + package.close() + + + + #--------------------------HELPER-FUNCTIONS-------------------------------# + + def __getModelPath(self): + """Creates the modelpath out of the projectpath and the model.""" + modelpath = re.sub(r"\.", "\\\\", self._model) + modelpath = self._mainPath + modelpath + ".mo" + return modelpath + + def getModelName(self): + """Extracts the modelname out of the name that the model has in + the project. + e.g.: + When the model is "ACS_PowerSystems.SinglePhase.Examples.StatPh_Examples.Line_PQLoad" + it will return "Line_PQLoad" + """ + mo = re.search(r"\.(\w+)$", self._model) + if not mo==None: + modelName = mo.group(1) + else: + modelName = self._model + return modelName + + def __readModel(self): + """Reads the lines in the modelfile into a list""" + modelFile = open(self.__getModelPath(), "r") + modelLines = [line for line in modelFile] + modelFile.close() + return modelLines + + def __writeModel(self): + """Writes the lines stored in the private list modelLines back into the file""" + modelFile = open(self._modelPath, "w") + modelFile.writelines(self._modelLines) + modelFile.close() + + def _formatModel(self): + """Reformats the sourcecode of the model in a way so that it is easier for the script to work on it.""" + annotation = False + annotation_next = False + mergelines = False + + model_end = len(self._modelLines)-1 + self.__var_start = 0 + self.__var_end = 0 + self.__eq_start = 0 + self.__eq_end = 0 + + i = 0 + while i <= model_end : + if self._modelLines[i][0:5] == "model": + self.__var_start = i+1 + i = i + 1 + continue + elif self._modelLines[i][0:8] == "equation": + self.__var_end = i + self.__eq_start = i + 1 + i = i + 1 + continue + elif self._modelLines[i][0:3] == "end": + self.__eq_end = i + i = i + 1 + continue + + if "annotation" in self._modelLines[i]: + ann_start = self._modelLines[i].find("annotation") + if not self._modelLines[i][:ann_start].lstrip() == "": + self._modelLines.insert(i+1, " " + self._modelLines[i][ann_start:]) + self._modelLines[i] = self._modelLines[i][:ann_start] + "\n" + model_end = model_end + 1 + annotation_next = True + else: + annotation = True + mergelines = False + + if not annotation and not self._modelLines[i] == "\n": + if self._modelLines[i+1].lstrip()[0:10] == "annotation": + annotation_next = True + if mergelines == True: + self._modelLines[i-1] = self._modelLines[i-1][:-1] + self._modelLines[i].lstrip() + del self._modelLines[i] + i = i - 1 + model_end = model_end - 1 + mergelines = False + if not (self._modelLines[i][-2:] == ";\n") and not(annotation_next): + mergelines = True + elif annotation: + if self._modelLines[i][-2:] == ";\n": + annotation = False + i = i + 1 + if annotation_next == True: + annotation_next = False + annotation = True + mergelines = False + self.__writeModel() + + def __readVariableString(self, varStr): + """Transforms an initialization string into a dictionary. + E.g. (Vnom(displayUnit="kV")=20e3,v(re(start=1),im(start=2)), i(re(start=3), im(start=4))) + becomes {"Vnom":{"displayUnit":"kV","value":20e3},"v":{"re":{"start":1},"im":{"start":2}},"i":{"re":{"start":3},"im":{"start":4}}}. + """ + varStr = re.sub(r"\s", "", varStr) + varStr = re.sub(r"\)=(\d*(\.\d+)?(e\d+)?)",r",value=\1)",varStr) + varStr = re.sub(r"=", "\":", varStr) + varStr = re.sub(r"\(", "\":{\"", varStr) + varStr = re.sub(r"\)", "}", varStr) + varStr = re.sub(r",", ",\"", varStr) + + #marks the string values that aren't enclosed by qutoes in modelicacode e.g. "smoothnessSetting":Modelica.Blocks.Types.Smoothness.ConstantSegments + blocklist = re.findall(r":([\w]+\.[\w\.]+)}",varStr) + for item in blocklist: + tmpStr = item + varStr = re.sub(re.escape(tmpStr), "\"!" + tmpStr + "!\"", varStr) + + varStr = varStr[2:] + + dic = ast.literal_eval(varStr) + + return dic + + def __writeVariableString(self, dic, name): + """Recursive function that turns a dictionary into a string that can be used + to initialize a variable. + """ + varStr = name + "(" + for key in dic.keys(): + if type(dic[key]) == dict: + tmpStr = self.__writeVariableString(dic[key], key) + elif type(dic[key]) == str: + #checks if the string should have quotes or not + mo = re.search(r"!(.+)!", dic[key]) + if mo is None: + tmpStr = key + " = \""+ str(dic[key]) + "\"" + else: + tmpStr = key + " = " + str(mo.group(1)) + else: + tmpStr = key + " = " + str(dic[key]) + varStr = varStr + " " + tmpStr + "," + varStr = varStr[:-1] + ")" + return varStr + + def __getLinePositionName(self, name): + """Returns the linenumber of the line in which the variable with the name + "name" are declared. + """ + for i in range(self.__var_start,self.__var_end): + #mo = re.search( r" +(\w+) *\(", self._modelLines[i]) + #mo = re.search( r" +(\w+) *?(\([\w,\(\)=\"\. ]*\))?\s+$", self._modelLines[i]) + #mo = re.search( r" +(\w+) *?(\([\w,\(\)=\"\. -]*\))?\s+$", self._modelLines[i]) + mo = re.search(r" *([\w\.]*) " + name + r" *?(\([\w,\(\)=\"\. -_]*\))?\s+", self._modelLines[i]) + if mo: + break + return i + + def __getLinePositionSubmodel(self, subModelType): + """Returns a list which contains the linenumbers of lines in which + variables with the type subModelType are declared. + """ + lines = [] + for i in range(self.__var_start,self.__var_end): + mo = re.search(subModelType + r" +(\w+)[ (]?", self._modelLines[i]) + if mo: + lines.append(i) + return lines + + def __addtoValues(self, item): + """Adds a item with the form ["Submodel.Subsubmodel", value]. To the + dictionary self._values in which all initial values are stored. + """ + name = item[0] + value = item[1] + l = name.split(".") + dic = self._values[l[0]] + for i in range(1, len(l)-1): + if not dic.has_key(l[i]): + dic[l[i]] = {} + dic = dic[l[i]] + dic[l[-1]] = value + + def __deletefromValues(self, dic, item): + """Deletes an entry from a nested dictionary.""" + key = item[0] + if not len(item) == 1: + self.__deletefromValues(dic[key], item[1:]) + if dic[key] == {}: + del dic[key] + else: + del dic[key] + + def __searchinValues(self, name): + """Searches a Variable in the dictionary self._values and returns the initial value of this variable.""" + l = name.split(".") + dic = self._values[l[0]] + for i in range(1, len(l)-1): + if not dic.has_key(l[i]): + dic[l[i]] = {} + dic = dic[l[i]] + value = dic[l[-1]] + return value + + def __addLinetoValues(self, name): + """Adds the initial values of the variable withe the name "name" given + in the modelfile to self._modelLines. + """ + pos = self.__getLinePositionName(name) + line = self._modelLines[pos] + if not name in self._values: + #mo = re.search(r" +(\w+(\([\w,\(\)=\"\. ]*\))?)\s+$", line) + #mo = re.search(r" *([\w\.]*) " + name + r" *?(\([\w,\(\)=\"\. -_]*\))?\s+", line) + #mo = re.search(r" +(\w+(\([\w,\(\)=\"\. _]*\))?)(\s+\"\w+\")?\s+$", line) + mo = re.search(r" +(\w+(\([\w,\(\)=\"\.\: _\\/-]*\))?)(\s+\"\w+\")?\s+$", line) + varstr = mo.group(1)[len(name):] + + if not varstr == "": + tmpDic = self.__readVariableString(varstr) + else: + tmpDic = {} + self._values[name] = tmpDic + + diff --git a/Py4Mod/py4mod/OMInterface.py b/Py4Mod/py4mod/OMInterface.py new file mode 100644 index 0000000000000000000000000000000000000000..166294a00c194d564e9fba9ff6f5a6acb8951496 --- /dev/null +++ b/Py4Mod/py4mod/OMInterface.py @@ -0,0 +1,473 @@ +import os +import sys +import platform +import subprocess +import numpy as np +import xml.etree.ElementTree as ET + +from modelicares import SimRes +from OMPython import OMCSessionZMQ + +class OMInterface(object): + def __init__(self): + """ + create an OMCSessionZMQ object + """ + #create an OMCSessionZMQ object + self.__omc = OMCSessionZMQ() + + #model variables + self.modelName="" + self.resultFileName="" + self.exeFile="" + self.__xmlInitFile="" + + #xml init file variables + self.__xmlFile=None + self.__xmlTree=None + self.__xmlRoot=None + self.__modelVariables=[] + + #class flags + self.__buildFlag=False + self.__simulationFlag=False + self.__readXMLinitFileFlag=False + + + # request to OMC + def __requestApi(self, apiName, entity=None, properties=None): + if (entity is not None and properties is not None): + exp = '{}({}, {})'.format(apiName, entity, properties) + elif entity is not None and properties is None: + if (apiName == "loadFile" or apiName == "importFMU"): + exp = '{}("{}")'.format(apiName, entity) + else: + exp = '{}({})'.format(apiName, entity) + else: + exp = '{}()'.format(apiName) + try: + res = self.__omc.sendExpression(exp) + except Exception as e: + print(e) + res = None + return res + + + def close(self): + """ + Terminate execution of Modelica environment + """ + self.__requestApi("exit", 0) + + + def changeWorkingDirectory(self, dir=""): + """ + Change directory to the given path (which may be either relative or absolute) + If the given path is the empty string, the function simply returns the current working directory + + @param str Dir: Directory to change to. If directory="" this function return the current working directory + @raises: exception + """ + if dir=="": + exp='cd("")' + return self.__omc.sendExpression(exp) + + if not os.path.exists(dir): + raise Exception('File Error: {} does not exists!!!'.format(os.path.abspath(dir))) + + exp = 'cd("{}")'.format(str(dir)) + res=self.__omc.sendExpression(exp) + + if not (res==dir): + raise Exception(res) + + + def loadLibrary(self, lName): + """ + Loads a Modelica library from the path indicated by the + environment variable OPENMODELICALIBRARY (e.g. in C:\OpenModelica1.12.0-64bit\lib\omlibrary) + You can use the method OMInterface.getAvailableLibraries to see all available libraries in OPENMODELICALIBRARY + + @param str lName: the model name to load + @raises: Exception() + """ + if lName not in (self.getAvailableLibraries()): + raise Exception('The library "{}" was not found in the environment variable OPENMODELICALIBRARY'.format(lName)) + + loadmodelError = '' + loadModelResult = self.__requestApi("loadModel", lName) + loadmodelError = self.__requestApi('getErrorString') + if loadmodelError: + raise Exception(loadmodelError) + + + def getAvailableLibraries(self): + """ + Looks for all libraries that are visible from the getModelicaPath(). + @return a list with the abailable libraries + """ + return self.__requestApi("getAvailableLibraries") + + + def loadFile(self, fName): + """ + Reads the file specified by fName. + This corresponds to File->Open in the menus. + + @param str fName: File-path to open. + @raises: Exception() + """ + # if file does not exists + if not os.path.exists(fName): + raise Exception("File Error: " + os.path.abspath(fileName) + " does not exists!!!") + + loadfileResult = self.__requestApi("loadFile", fName) + + if not loadfileResult: + """ + if loadfileError: + specError = 'Parser error: Unexpected token near: optimization (IDENT)' + if specError in loadfileError: + self.__requestApi("setCommandLineOptions", '"+g=Optimica"') + self.__requestApi("loadFile", fName) + """ + loadfileError = self.__requestApi("getErrorString") + raise Exception('loadFile Error: ' + loadfileError) + + + def getModelicaPath(self): + """ + get the environment variable OPENMODELICALIBRARY + """ + return self.__requestApi("getModelicaPath") + + + def setModelicaPath(self, path): + """ + set the environment variable OPENMODELICALIBRARY + @return true if successful + **not tested + """ + exp = 'setModelicaPath("{}")'.format(path) + return self.__omc.sendExpression(exp) + + + def getComponents(self, cName): + """ + Returns a list of component declarations within class cName: "{{Atype, varidA, + "commentA"}, {Btype, varidB, "commentB"}, {...}}" and so on. + """ + return self.__requestApi("getComponents", cName) + #return self.__omc.getComponents(cName) + + + def isModel(self, model): + """ + Returns True if the given class has restriction model. + """ + return self.__requestApi("isModel", str(model)) + + + def getPackages(self): + """ + Returns a list of names of libraries and their path on the system, for example: + (("Modelica","/usr/lib/omlibrary/Modelica 3.2.1"),("ModelicaServices","/usr/lib/omlibrary/ModelicaServices 3.2.1")) + """ + return self.__requestApi("getLoadedLibraries") + + + def buildModel(self, mName, **kwargs): + """ + builds a modelica model by generating c code and build it. + The only required argument is the className, while all others have some default values. + + @input str mName: Name of model, e.g. Modelica.Mechanics.Rotational.Components.Clutch. + @input str model: the model that should simulated + @input float startTime: the start time of the simulation. <default> = 0.0 + @input float stopTime: the stop time of the simulation. <default> = 1.0 + @input float numberOfIntervals: number of intervals in the result file. <default> = 500 + @input float tolerance: tolerance used by the integration method. <default> = 1e-6 + @input str method: integration method used for simulation. <default> = "dassl" + @input str fileNamePrefix: fileNamePrefix. <default> = "" + @input str options: options. <default> = "" + @input str outputFormat: Format for the result file. <default> = "mat" + @input str variableFilter: Filter for variables that should store in result file. <default> = .* + + @input str cflags: compiler flags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/omchelptext.html) + @input str simflags: simflags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/simulationflags.html) + + @raises: Exception() + """ + self.modelName=mName + + #check if the model exists + if not self.isModel(mName): + raise Exception('The model "{}" does not exists'.format(mName)) + + command = "buildModel(" + mName + for k,v in kwargs.items(): + if isinstance(v, str): + command += ', {}="{}"'.format(k, v) + else: + #command += ", " + k + "=" + str(v) + command += ', {}={}'.format(k, v) + command+= ")" + + #d: Shows additional information from the initialization process. + self.__omc.sendExpression("setCommandLineOptions(\"+d=initialization\")") + + buildModelResult = self.__omc.sendExpression(command) + + #print warnings + if self.__requestApi("countMessages")[2]: + print('WARNINGS:') + self.__requestApi("getErrorString") + + if ('' in buildModelResult): + raise Exception(buildModelError) + + self.exeFile=str(buildModelResult[0]) + self.__xmlInitFile=str(buildModelResult[1]) + self.__buildFlag=True + + + def simulate(self, simflags=None): + """ + to simulate or re-simulate model by calling the c programm according to the simulation options. + It can be called: + •only without any arguments: simulate the model + + Note: Model must be built before this function can be called! + """ + + if (platform.system() == "Windows"): + exeFile = self.exeFile + ".exe" + else: + exeFile = self.exeFile + + if simflags is not None: + if not isinstance(simflags, str): + raise Exception('simflags must be a string') + cmd = exeFile + ' {}'.format(simflags) + else: + cmd = exeFile + + #exeFile = os.path.join(self.changeWorkingDirectory(), exeFile).replace("\\", "/") + #check_exeFile_ = os.path.exists(exeFile) + + if (os.path.exists(exeFile)): + omhome = os.path.join(os.environ.get("OPENMODELICAHOME"), 'bin').replace("\\", "/") + my_env = os.environ.copy() + my_env["PATH"] = omhome + os.pathsep + my_env["PATH"] + #process = subprocess.Popen(cmd, cwd=self.changeWorkingDirectory(), env=my_env) + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.changeWorkingDirectory(), env=my_env) + + while True: + output = process.stdout.readline() + output = output.decode('utf8') + + if output == '' and process.poll() is not None: + break + if output: + if 'stdout | info | ' in output: + sys.stdout.write('INFO: {}'.format(output.replace('stdout | info | ',""))) + #print('INFO: {}'.format(output.replace('stdout | info | ',""))) + elif 'stdout | warning |' in output: + sys.stdout.write('WARNING: {}'.format(output.replace('stdout | warning | ',""))) + #print('WARNING: {}'.format(output.replace('stdout | warning | ',""))) + else: + sys.stdout.write(output) + #print(output) + + rc = process.poll() + process.terminate() + + if (rc is not 0): + raise Exception(str(stderr)) #not tested + + self.__simulationFlag = True + self.resultFileName = os.path.join(self.changeWorkingDirectory(), self.modelName + '_res.mat') + + else: + print("Error: application file not generated yet") + + def simulateModel(self, model, **kwargs): + """ + Simulates a modelica model by generating c code, build it and run the simulation executable. + The only required argument is the className, while all others have some default values. + input str model: the model that should simulated + input float startTime: the start time of the simulation. <default> = 0.0 + input float stopTime: the stop time of the simulation. <default> = 1.0 + input float numberOfIntervals: number of intervals in the result file. <default> = 500 + input float tolerance: tolerance used by the integration method. <default> = 1e-6 + input str method:integration method used for simulation. <default> = "dassl" + input str fileNamePrefix: fileNamePrefix. <default> = "" + input str options: options. <default> = "" + input str outputFormat: Format for the result file. <default> = "mat" + input str variableFilter: Filter for variables that should store in result file. <default> = .* + + input str cflags: compiler flags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/omchelptext.html) + input str simflags: simflags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/simulationflags.html) + output dict results={String resultFile, String simulationOptions, String messages, Real timeFrontend, Real timeBackend, + Real timeSimCode, Real timeTemplates, Real timeCompile, Real timeSimulation, Real timeTotal} + + Note: this function can not be used with the functions: simulate(), setValue(), getSimulationValues(), setSimulationOptions(), buildModel() + """ + + command = "simulate(" + model + for k,v in kwargs.items(): + if isinstance(v, str): + command += ', {}="{}"'.format(k, v) + else: + #command += ", " + k + "=" + str(v) + command += ', {}={}'.format(k, v) + command+= ")" + + results = self.__omc.sendExpression(command) + if not results: + err_msg = self.__omc.sendExpression("getErrorString()") + raise Exception(err_msg) + + self.resultFileName = os.path.join(self.changeWorkingDirectory(), results['resultFile']) + self.__simulationFlag = True + + return results + + def getResultVarNames(self): + """ Returns the variablen names from the simulation result.""" + # check for result file exits + if not os.path.exists(self.resultFileName): + raise Exception("Error: Result file does not exist.") + + validSolution = self.__omc.sendExpression("readSimulationResultVars(\"" + self.resultFileName + "\")") + return validSolution + + + # to extract simulation results + def getResults(self, outputVarList = []): + """ + Returns the solutions for the variables in outputVarList in + the format of numpy.array by using OMPython in the same order as outputVarList + """ + + # check for result file exits + if not os.path.exists(self.resultFileName): + raise Exception("Error: Result file does not exist.") + + if all(isinstance(a, str) for a in outputVarList): + outputList = self.getResultVarNames() + for v in outputVarList: + if v == 'time': + continue + if v not in outputList: + raise Exception('!!! {} does not exist\n'.format(v)) + + variables = ",".join(outputVarList) + exp = "readSimulationResult(\"" + self.resultFileName + '", {' + variables + '})' + + res = self.__omc.sendExpression(exp) + exp2 = "closeSimulationResultFile()" + self.__omc.sendExpression(exp2) + + return np.array(res) + + + def getResultsFast(self, outputVarList=[]): + """ + Returns the trajectories for the variables in outputVarList in + the format of numpy.array by using ModelicaRes + + This Method can work faster than getSolutions() if the number of solutions is big + """ + + # check for result file exits + if not os.path.exists(self.resultFileName): + raise Exception("Error: Result file does not exist.") + + if all(isinstance(a, str) for a in outputVarList): + outputList = self.getResultVarNames() + for v in outputVarList: + if v == 'time': + continue + if v not in outputList: + raise Exception('!!! {} does not exist\n'.format(v)) + + sim=SimRes(self.resultFileName) + arrTraj=[] + for item in outputVarList: + if np.array_equal(sim('Time').samples.values,sim(item).samples.times): + arrTraj.append(sim(item).samples.values) + else: + arrTraj.append(np.array(np.interp(sim('Time').samples.values,sim(item).samples.times,sim(item).samples.values),'float32')) + return np.array(arrTraj) + + + #========== functions that reads/edits file ModelName_init.xml file ================== + def __readXMLinitFile(self): + self.__xmlFile = os.path.join(self.changeWorkingDirectory(), self.__xmlInitFile) + self.__xmlTree = ET.parse(self.__xmlFile) + self.__xmlRoot = self.__xmlTree.getroot() + + #get model parameters and store it in the list self.__modelParameters + for scalarVariable in self.__xmlRoot.iter('ScalarVariable'): + name = scalarVariable.get("name") + changeable = scalarVariable.get("isValueChangeable") + scalarVariable_children = scalarVariable.getchildren() + start = None + for attr in scalarVariable_children: + start = attr.get('start') + self.__modelVariables.append(ModelicaVariable(name, changeable, start)) + + + def setValue(self, values): + """ + change simulation variables in the xml model description file + @values is a dict + + Note: Model must be built before this function can be called! + """ + if not self.__buildFlag: + raise Exception("Model must be built before OMInterface.setValue() can be called!") + + if not self.__readXMLinitFileFlag: + self.__readXMLinitFile() + + for n in values: + index = self.__first(n) + if index==-1: + raise Exception('Error: {} is not a variable'.format(n)) + else: + if self.__modelVariables[index].changeable == 'false': + raise Exception('!!! value cannot be set for {}'.format(n)) + + else: + self.__modelVariables[index].start = float(values.get(n)) + for scalarVariable in self.__xmlRoot.iter('ScalarVariable'): + if scalarVariable.get('name') == str(n): + scalarVariable_children = scalarVariable.getchildren() + for attr in scalarVariable_children: + val = float(values.get(n)) + attr.set('start', str(val)) + self.__xmlTree.write(self.__xmlFile, encoding='UTF-8', xml_declaration=True) + + def __first(self, name): + """ + to search variable in self.__modelVariables + """ + index=0 + for item in self.__modelVariables: + if (item.name == name): + return index + index+=1 + return -1 + +class ModelicaVariable(object): + """ + To represent modelica ModelVariables + This class is used to store the values reads from the xlm init file + """ + def __init__(self, name, changeable, start): + self.name = name #name of the variable + self.changeable = changeable #is a parameter or a + self.start = start #initial condition for state variables + \ No newline at end of file diff --git a/Py4Mod/py4mod/README.md b/Py4Mod/py4mod/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a5186757765148205903bde67d0a2bae78ec8d05 --- /dev/null +++ b/Py4Mod/py4mod/README.md @@ -0,0 +1,32 @@ +# Funktionennamen + +The following table gives a comparison between the names of the functions of the interfaces and a small description of their function. It is important to keep in mind that some functions receive different arguments depending on the chosen interface. + + +| DymolaInterface | OpenModelica Interface | ModelicaModel | | +| ------------------------ | ------------------------ | ------------- | ------------------------------------------------------------------------------------------------------ | +| __init__ | __init__ | createInterface | In OM creates an OMCSessionZMQ object, in DymInterface starts Dymola | +| changeWorkingDirectory | changeWorkingDirectory | changeWorkingDirectory | Change directory to the given path | +| ** | loadLibrary | --- | Loads a Modelica library from the path indicated by the environment variable OPENMODELICALIBRARY | +| --- | getAvailableLibraries | --- | Looks for all libraries that are visible from the getModelicaPath() | +| loadFile | loadFile | loadFile | Reads the file specified by fName. This corresponds to File->Open in the menus. | +| --- | getModelicaPath | --- | Returns the environment variable OPENMODELICALIBRARY | +| --- | setModelicaPath | --- | To set the environment variable OPENMODELICALIBRARY | +| --- | getComponents | --- | To get list of all ModelicaVariable names | +| --- | isModel | --- | Returns True if the given class has restriction model. | +| --- | getPackages | --- | Returns a list of names of loaded libraries and their path on the system | +| dymApplyModifiers | setValue | --- | In Dymola applies modifiers in modifierList. In OM chage the starts values in the XML init file | +| buildModel | buildModel | buildModel | Builds a modelica model by generating c code and build it | +| simulateModel | simulate | simulate | In OM simulate or re-simulate model by calling the .exe file (generated with buildModel()). In Dymola simulates the model and compiles it if it has not been compiled before | +| --- | simulateModel | --- | In OM simulates a modelica model by generating c code, build it and run the simulation executable. | +| getResultVarNames | getResultVarNames | getResultVarNames | Returns the variablen names from the simulation result | +| getResultsFast | getResultsFast | getResults | Returns the trajectories for the variables in outputVarList by using ModelicaRes | +| getResults | getResults | --- | To extract simulation results by using OMPython/DymolaInterface | +| close | close | close | To close dymola or OM | + +<br /> +** This function is not necesary in Dymola <br /> +--- This function is not implemented in the Interface + +<br /> +The functions that have not been implemented in ModelicaModel but in any of the interfaces can be easily called with the class variable self.interface. diff --git a/Py4Mod/py4mod/archive/OMFunctions_old.py b/Py4Mod/py4mod/archive/OMFunctions_old.py new file mode 100644 index 0000000000000000000000000000000000000000..de47a0d886b4d9f60d36d42548c6aef9b343e480 --- /dev/null +++ b/Py4Mod/py4mod/archive/OMFunctions_old.py @@ -0,0 +1,572 @@ +import os +import sys +import platform +import subprocess +import numpy as np +import xml.etree.ElementTree as ET +from modelicares import SimRes +from OMPython import OMCSessionZMQ + +class ModelicaVariable(object): + """ + To represent modelica ModelVariables + """ + def __init__(self, name, start, changable, variability, description, causality, alias, aliasvariable): + self.name = name + self.start = start + self.changable = changable + self.description = description + self.variability = variability + self.causality = causality + self.alias = alias + self.aliasvariable = aliasvariable + + +class OMInterface(object): + def __init__(self, modelica_path=None): + """ + create an OMCSessionZMQ object + + @param str modelica_path: The path to the directory "openmodelica_install_directory\lib\omlibrary". + Default is used the environment variable OPENMODELICALIBRARY + """ + + #create an OMCSessionZMQ object + self.__omc = OMCSessionZMQ() + + #set dymola path + if modelica_path is not None: + self.setModelicaPath(modelica_path) + + #model variables + self.modelName="" + self.resultFileName="" + + #variables to describe the modelName_init.xml file. + #This is the file that description the building model + self.__xmlFile=None #Modelica xml Representation + self.__xmlTree=None #xml File tree structure + self.__xmlRoot=None #xml File root element + + #list of variables with detailes such as name, value, changeable, and description, + #where changeable means if value for corresponding quantity name is changeable or not. + #To describe each variable, we use the class ModelicaVariable() + self.__variablesList=[] + + #class flags + self.__buildFlag=False + self.__simulationFlag=False + + + def changeWorkingDirectory(self, dir=""): + """ + Change directory to the given path (which may be either relative or absolute) + If the given path is the empty string, the function simply returns the current working directory + + @param str Dir: Directory to change to. If directory="" this function return the current working directory + @raises: exception + """ + if dir=="": + exp='cd("")' + return self.__omc.sendExpression(exp) + + if not os.path.exists(dir): + raise Exception('File Error: {} does not exists!!!'.format(os.path.abspath(dir))) + + exp = 'cd("{}")'.format(str(dir)) + res=self.__omc.sendExpression(exp) + + if not (res==dir): + raise Exception(res) + + + def loadLibrary(self, lName): + """ + Loads a Modelica library from the path indicated by the + environment variable OPENMODELICALIBRARY (e.g. in C:\OpenModelica1.12.0-64bit\lib\omlibrary) + You can use the method OMInterface.getAvailableLibraries to see all available libraries in OPENMODELICALIBRARY + + @param str lName: the model name to load + @raises: Exception() + """ + if lName not in (self.getAvailableLibraries()): + raise Exception('The library "{}" was not found in the environment variable OPENMODELICALIBRARY'.format(lName)) + + loadmodelError = '' + loadModelResult = self.__requestApi("loadModel", lName) + loadmodelError = self.__requestApi('getErrorString') + if loadmodelError: + raise Exception(loadmodelError) + + + def getAvailableLibraries(self): + """ + Looks for all libraries that are visible from the getModelicaPath(). + @return a list with the abailable libraries + """ + return self.__requestApi("getAvailableLibraries") + + + def loadFile(self, fName): + """ + Reads the file specified by fName. + This corresponds to File->Open in the menus. + + @param str fName: File-path to open. + @raises: Exception() + """ + # if file does not exists + if not os.path.exists(fName): + raise Exception("File Error: " + os.path.abspath(fileName) + " does not exists!!!") + + loadfileResult = self.__requestApi("loadFile", fName) + loadfileError = self.__requestApi("getErrorString") + + if loadfileError: + specError = 'Parser error: Unexpected token near: optimization (IDENT)' + if specError in loadfileError: + self.__requestApi("setCommandLineOptions", '"+g=Optimica"') + self.__requestApi("loadFile", fName) + else: + raise Exception('loadFile Error: ' + loadfileError) + + + def getModelicaPath(self): + """ + get the environment variable OPENMODELICALIBRARY + """ + return self.__requestApi("getModelicaPath") + + + def setModelicaPath(self, path): + """ + set the environment variable OPENMODELICALIBRARY + @return true if successful + **not tested + """ + exp = 'setModelicaPath("{}")'.format(path) + return self.__omc.sendExpression(exp) + + + def getComponents2(self): + """ + to get list of all ModelicaVariable names + """ + return self.__requestApi("listVariables") + #return self.__omc.getComponents(cName) + + + def getComponents(self): + """ + to get list of all ModelicaVariable names + Note: Model must be built before this function can be called! + """ + if not self.__buildFlag: + raise Exception("Model must be built before getComponents() can be called!") + + components = [] + for k in self.__variablesList: + components.append(k.name) + + return components + + + def isModel(self, model): + """ + Returns True if the given class has restriction model. + """ + return self.__requestApi("isModel", str(model)) + + + def getPackages(self): + """ + Returns a list of names of libraries and their path on the system, for example: + (("Modelica","/usr/lib/omlibrary/Modelica 3.2.1"),("ModelicaServices","/usr/lib/omlibrary/ModelicaServices 3.2.1")) + """ + return self.__requestApi("getLoadedLibraries") + + + def buildModel(self, mName): + """ + builds a modelica model by generating c code and build it. + The only required argument is the className, while all others have some default values. + + @param str mName: Name of model, e.g. Modelica.Mechanics.Rotational.Components.Clutch. + + @input str model: the model that should simulated + @input float startTime: the start time of the simulation. <default> = 0.0 + @input float stopTime: the stop time of the simulation. <default> = 1.0 + @input float numberOfIntervals: number of intervals in the result file. <default> = 500 + @input float tolerance: tolerance used by the integration method. <default> = 1e-6 + @input str method: integration method used for simulation. <default> = "dassl" + @input str fileNamePrefix: fileNamePrefix. <default> = "" + @input str options: options. <default> = "" + @input str outputFormat: Format for the result file. <default> = "mat" + @input str variableFilter: Filter for variables that should store in result file. <default> = .* + + @input str cflags: compiler flags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/omchelptext.html) + @input str simflags: simflags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/simulationflags.html) + + @output String[2] buildModelResults; + + @raises: Exception() + """ + self.modelName=mName + + #check if the model exists + if not self.isModel(mName): + raise Exception('The model "{}" does not exists'.format(mName)) + + #d: Shows additional information from the initialization process. + self.__omc.sendExpression("setCommandLineOptions(\"+d=initialization\")") + + buildModelResult = self.__requestApi("buildModel", mName) + + #print warnings + if self.__requestApi("countMessages")[2]: + print("building warnings:") + buildModelError = self.__requestApi("getErrorString") + + if ('' in buildModelResult): + raise Exception(buildModelError) + + #create list self.__variablesList. It displays details of ModelicaVariables such as name, value, + #changeable, and description, where changeable means if value for corresponding quantity name + #is changeable or not. + #Parse XML file: + self.__xmlFile = os.path.join(self.changeWorkingDirectory(), buildModelResult[1]) + self.__xmlTree = ET.parse(self.__xmlFile) + self.__xmlRoot = self.__xmlTree.getroot() + + for sv in self.__xmlRoot.iter('ScalarVariable'): + name = sv.get('name') + changable = sv.get('isValueChangeable') + description = sv.get('description') + variability = sv.get('variability') + causality = sv.get('causality') + alias = sv.get('alias') + aliasvariable = sv.get('aliasVariable') + ch = sv.getchildren() + start = None + for att in ch: + start = att.get('start') + self.__variablesList.append(ModelicaVariable(name, start, changable, variability, description, causality, alias, aliasvariable)) + + self.__buildFlag=True + + + def checkAvailability(self, names, chkList): + """ + check if names exist in a list + """ + try: + if isinstance(names, list): + nonExistingList = [] + for n in names: + if n not in chkList: + nonExistingList.append(n) + if nonExistingList: + print('Error!!! ' + str(nonExistingList) + ' does not exist.') + return False + elif isinstance(names, str): + if names not in chkList: + print('Error!!! ' + names + ' does not exist.') + return False + else: + print('Error!!! Incorrect format') + return False + return True + + except Exception as e: + print(e) + + + def setSimulationOptions(self, **simOptions): + """ + change the simulation values 'startTime', 'stopTime', 'stepSize', 'tolerance', 'solver' in the xml model description file + + @input float startTime: the start time of the simulation. + @input float stopTime: the stop time of the simulation. + @input float stepSize: the step time of the simulation. + @input float tolerance: tolerance used by the integration method. + @input float solver: integration method + + Note: Model must be built before this function can be called! + """ + if not self.__buildFlag: + raise Exception("Model must be built before setSimulationOptions() can be called!") + + valuesList = self.getSimulationValues() + simNamesList = ['startTime', 'stopTime', 'stepSize', 'tolerance', 'solver'] + try: + for key in simOptions: + if key in simNamesList: + if key == 'stopTime': + if float(simOptions[key]) <= float(valuesList[0]): + print('!!! stoptTime should be greater than startTime') + return + if key == 'startTime': + if float(simOptions[key]) >= float(valuesList[1]): + print('!!! startTime should be less than stopTime') + return + + for sim in self.__xmlRoot.iter('DefaultExperiment'): + sim.set(key, str(simOptions.get(key))) + self.__xmlTree.write(self.__xmlFile, encoding='UTF-8', xml_declaration=True) + + else: + print('!!!' + key + ' is not an option') + continue + + except Exception as e: + print(e) + + + def getSimulationValues(self): + """ + Read the simulation values 'startTime', 'stopTime', 'stepSize', 'tolerance', 'solver' from the xml model description file + + Note: Model must be built before this function can be called! + """ + if self.__xmlFile is None: + raise Exception("Model must be built before setSimulationOptions() can be called!") + + simValuesList = [] + + for attr in self.__xmlRoot.iter('DefaultExperiment'): + startTime = attr.get('startTime') + simValuesList.append(float(startTime)) + stopTime = attr.get('stopTime') + simValuesList.append(float(stopTime)) + stepSize = attr.get('stepSize') + simValuesList.append(float(stepSize)) + tolerance = attr.get('tolerance') + simValuesList.append(float(tolerance)) + solver = attr.get('solver') + simValuesList.append(solver) + + return simValuesList + + + def setValue(self, values): + """ + change simulation variables in the xml model description file + + Note: Model must be built before this function can be called! + """ + if not self.__buildFlag: + raise Exception("Model must be built before OMFunctions.setValue() can be called!") + + namesList=self.getComponents() + for n in values: + if n in namesList: + for l in self.__variablesList: + if (l.name == n): + if l.changable == 'false': + raise Exception('!!! value cannot be set for {}'.format(n)) + + else: + l.start = float(values.get(n)) + for paramVar in self.__xmlRoot.iter('ScalarVariable'): + if paramVar.get('name') == str(n): + c = paramVar.getchildren() + for attr in c: + val = float(values.get(n)) + attr.set('start', str(val)) + self.__xmlTree.write(self.__xmlFile, encoding='UTF-8', xml_declaration=True) + else: + raise Exception('Error: {} is not a variable'.format(n)) + + + def simulate(self, simFlags=None): + """ + to simulate or re-simulate model by calling the c programm according to the simulation options. + It can be called: + •only without any arguments: simulate the model + + Note: Model must be built before this function can be called! + """ + + if (platform.system() == "Windows"): + exeFile = self.modelName + ".exe" + else: + exeFile = self.modelName + + exeFile = os.path.join(self.changeWorkingDirectory(), exeFile).replace("\\", "/") + check_exeFile_ = os.path.exists(exeFile) + + if simFlags is not None: + if not isinstance(simFlags, str): + raise Exception('SimFlags must be a string') + cmd = exeFile + ' {}'.format(simFlags) + else: + cmd = exeFile + + + if (check_exeFile_): + omhome = os.path.join(os.environ.get("OPENMODELICAHOME"), 'bin').replace("\\", "/") + my_env = os.environ.copy() + my_env["PATH"] = omhome + os.pathsep + my_env["PATH"] + #process = subprocess.Popen(cmd, cwd=self.changeWorkingDirectory(), env=my_env) + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.changeWorkingDirectory(), env=my_env) + + while True: + output = process.stdout.readline() + output = output.decode('utf8') + + if output == '' and process.poll() is not None: + break + if output: + if 'stdout | info | ' in output: + sys.stdout.write('INFO: {}'.format(output.replace('stdout | info | ',""))) + #print('INFO: {}'.format(output.replace('stdout | info | ',""))) + elif 'stdout | warning |' in output: + sys.stdout.write('WARNING: {}'.format(output.replace('stdout | warning | ',""))) + #print('WARNING: {}'.format(output.replace('stdout | warning | ',""))) + else: + sys.stdout.write(output) + #print(output) + + rc = process.poll() + process.terminate() + + if (rc is not 0): + raise Exception(str(stderr)) #not tested + + self.__simulationFlag = True + self.resultFileName = os.path.join(self.changeWorkingDirectory(), self.modelName + '_res.mat') + + else: + print("Error: application file not generated yet") + + + def simulateModel(self, model, **kwargs): + """ + Simulates a modelica model by generating c code, build it and run the simulation executable. + The only required argument is the className, while all others have some default values. + input str model: the model that should simulated + input float startTime: the start time of the simulation. <default> = 0.0 + input float stopTime: the stop time of the simulation. <default> = 1.0 + input float numberOfIntervals: number of intervals in the result file. <default> = 500 + input float tolerance: tolerance used by the integration method. <default> = 1e-6 + input str method:integration method used for simulation. <default> = "dassl" + input str fileNamePrefix: fileNamePrefix. <default> = "" + input str options: options. <default> = "" + input str outputFormat: Format for the result file. <default> = "mat" + input str variableFilter: Filter for variables that should store in result file. <default> = .* + + input str cflags: compiler flags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/omchelptext.html) + input str simflags: simflags. <default> = "" (more info: https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/simulationflags.html) + output dict results={String resultFile, String simulationOptions, String messages, Real timeFrontend, Real timeBackend, + Real timeSimCode, Real timeTemplates, Real timeCompile, Real timeSimulation, Real timeTotal} + + Note: this function can not be used with the functions: simulate(), setValue(), getSimulationValues(), setSimulationOptions(), buildModel() + """ + + command = "simulate(" + model + for k,v in kwargs.items(): + if isinstance(v, str): + command += ', {}="{}"'.format(k, v) + else: + #command += ", " + k + "=" + str(v) + command += ', {}={}'.format(k, v) + command+= ")" + + results = self.__omc.sendExpression(command) + if not results: + err_msg = self.__omc.sendExpression("getErrorString()") + raise Exception(err_msg) + + self.resultFileName = os.path.join(self.changeWorkingDirectory(), results['resultFile']) + self.__simulationFlag = True + + return results + + + def getResultVarNames(self): + """ Returns the variablen names from the simulation result.""" + # check for result file exits + if not os.path.exists(self.resultFileName): + raise Exception("Error: Result file does not exist.") + + validSolution = self.__omc.sendExpression("readSimulationResultVars(\"" + self.resultFileName + "\")") + return validSolution + + + # to extract simulation results + def getResults(self, outputVarList = []): + """ + Returns the solutions for the variables in outputVarList in + the format of numpy.array by using OMPython in the same order as outputVarList + """ + + # check for result file exits + if not os.path.exists(self.resultFileName): + raise Exception("Error: Result file does not exist.") + + if all(isinstance(a, str) for a in outputVarList): + outputList = self.getResultVarNames() + for v in outputVarList: + if v == 'time': + continue + if v not in outputList: + raise Exception('!!! {} does not exist\n'.format(v)) + + variables = ",".join(outputVarList) + exp = "readSimulationResult(\"" + self.resultFileName + '", {' + variables + '})' + + res = self.__omc.sendExpression(exp) + exp2 = "closeSimulationResultFile()" + self.__omc.sendExpression(exp2) + + return np.array(res) + + + def getResultsFast(self, outputVarList=[]): + """ + Returns the trajectories for the variables in outputVarList in + the format of numpy.array by using ModelicaRes + + This Method can work faster than getSolutions() if the number of solutions is big + """ + + # check for result file exits + if not os.path.exists(self.resultFileName): + raise Exception("Error: Result file does not exist.") + + if all(isinstance(a, str) for a in outputVarList): + outputList = self.getResultVarNames() + for v in outputVarList: + if v == 'time': + continue + if v not in outputList: + raise Exception('!!! {} does not exist\n'.format(v)) + + sim=SimRes(self.resultFileName) + arrTraj=[] + for item in outputVarList: + if np.array_equal(sim('Time').samples.values,sim(item).samples.times): + arrTraj.append(sim(item).samples.values) + else: + arrTraj.append(np.array(np.interp(sim('Time').samples.values,sim(item).samples.times,sim(item).samples.values),'float32')) + return np.array(arrTraj) + + + # request to OMC + def __requestApi(self, apiName, entity=None, properties=None): + if (entity is not None and properties is not None): + exp = '{}({}, {})'.format(apiName, entity, properties) + elif entity is not None and properties is None: + if (apiName == "loadFile" or apiName == "importFMU"): + exp = '{}("{}")'.format(apiName, entity) + else: + exp = '{}({})'.format(apiName, entity) + else: + exp = '{}()'.format(apiName) + try: + res = self.__omc.sendExpression(exp) + except Exception as e: + print(e) + res = None + return res + \ No newline at end of file diff --git a/Py4Mod/py4mod/modelicafunctions.py b/Py4Mod/py4mod/modelicafunctions.py deleted file mode 100644 index 7c77cb22b38c3d170a2e2a39febb440a5c956e79..0000000000000000000000000000000000000000 --- a/Py4Mod/py4mod/modelicafunctions.py +++ /dev/null @@ -1,605 +0,0 @@ -import dymola.dymola_enums -from dymola.dymola_interface import DymolaInterface -from dymola.dymola_exception import DymolaException - -from shutil import copyfile -from bisect import bisect -import numpy as np -import re -import ast -from modelicares import SimRes - -class ModelicaModel(object): - def __init__(self, model, mainPath): - """Constructor of the class ModelicaModel, reads and format a model that - is given by the path of the project and the Name it has in this Project - """ - self._model = model # model identifier in package hierarchy [e.g. packageName.subPackageName.modelName] - self._mainPath = mainPath # path where either the model file itself or the corresponding package.mo can be found - if not self._mainPath[-1] == "\\": - self._mainPath = self._mainPath + "\\" - self._modelName = self.getModelName() # actual name of model extracted from self._model - self._modelPath = self.__getModelPath() # complete path of the model file deduced from self._mainPath and self._model - self._modelLines = self.__readModel() - self._formatModel() - self._values = {} - - #-------------------------- FUNCTIONS TO USE DYMOLA API ------------------------------# - - def dymOpen(self): - self.__dymObj = None - try: - # Instantiate the Dymola interface and start Dymola - self.__dymObj = DymolaInterface() - self.__dymObj.openModel(self._modelPath) - - # Set Dymola options - self.__dymObj.ExecuteCommand("experimentSetupOutput(events=false);") - self.__dymObj.ExecuteCommand("Advanced.OutputModelicaCode:=true;") - self.__dymObj.ExecuteCommand("Advanced.OutputModelicaCodeWithAliasVariables = true;") - #dymola.ExecuteCommand("Advanced.StoreProtectedVariables:=true;") - except DymolaException as ex: - print("Error in dymOpen: " + str(ex)) - - def dymTranslate(self): - try: - result = self.__dymObj.ExecuteCommand("translateModel(\"" + self._model + "\")") - if not result: - print("Translation failed. Below is the translation log.") - log = self.__dymObj.getLastError() - print(log) - exit(1) - except DymolaException as ex: - print("Error in dymTranslate: " + str(ex)) - - - def dymApplyModifiers(self, modifierList): - """ Applies modifiers in modifierList, which is of format - modifierList=[['submodule1','param1','value1'],['submodule2','param2','value2'],...] - """ - cmdString="" - for i in range(len(modifierList)): - cmdString+= modifierList[i][0] + "." + modifierList[i][1] + "=" + str(modifierList[i][2]) + ";" - self.__dymObj.ExecuteCommand(cmdString) - - - def dymSimulate(self, simOptions = "", resultName = "simResult"): - """Simulates the model in the mode specified in simMode. The results are stored in - files with the name resultName. It returns a list of the Variable Names andanother - list with the values they had at the start of the simulation. - """ - try: - if simOptions != "": - simOptions = simOptions + "," - - result = self.__dymObj.ExecuteCommand("simulateModel(\"" + self._model + "\" , " + simOptions + "resultFile = \"" + resultName + "\")") - - if not result: - print("Simulation failed. Below is the translation log.") - log = self.__dymObj.getLastError() - print(log) - exit(1) - except DymolaException as ex: - print("Error in dymSimulate: " + str(ex)) - - - def dymSimulateMultiResults(self, resultName = "simResult", simOptions = "", initialNames="", initialValues="", resultNames=""): - try: - # Simulate Model - if simOptions != "": - simOptions = "," + simOptions + "," - result = self.__dymObj.ExecuteCommand("simulateMultiResultsModel(\"" + self._model + "\"" + simOptions + "initialNames={" + initialNames + "}, initialValues=" + initialValues + ", resultFile = \"" + resultName + "\"," + "resultNames={\"" + resultNames + "\"})") - if not result: - print("Simulation failed. Below is the translation log.") - log = self.__dymObj.getLastError() - print(log) - exit(1) - except DymolaException as ex: - print("Error in dymSimulateMultiResults: " + str(ex)) - return result - - def dymGetResultsFast(self, resultName = "simResult", outputVarList = []): - """ Returns the trajectories for the variables in outputVarList in - the format of numpy.array by using ModelicaRes - """ - sim=SimRes(re.sub(self._modelName+".mo",resultName + ".mat",self._modelPath)) - arrTraj=[] - for item in outputVarList: - if np.array_equal(sim('Time').samples.values,sim(item).samples.times): - arrTraj.append(sim(item).samples.values) - else: - arrTraj.append(np.array(np.interp(sim('Time').samples.values,sim(item).samples.times,sim(item).samples.values),'float32')) - return np.array(arrTraj) - - def dymGetResults(self, resultName = "simResult", outputVarList = []): - """ Returns the trajectories for the variables in outputVarList in - the format of numpy.array by using DymolaInterface - """ - sizeTraj = self.__dymObj.readTrajectorySize(resultName + ".mat") - arrTraj = np.array(self.__dymObj.readTrajectory(resultName + ".mat", outputVarList, sizeTraj)) - return arrTraj - - def dymGetResultVarNames(self, resultName = "simResult"): - """ Returns the variablen names from the simulation result.""" - varNames = self.__dymObj.readTrajectoryNames(resultName + ".mat") - varNames.sort() - return varNames - - - def dymClose(self): - if self.__dymObj is not None: - self.__dymObj.close() - self.__dymObj = None - - #-------------------------- FUNCTIONS TO WORK ON MODELICA CODE ------------------------------# - - def getSubmodelNames(self, subModelType): - """Searches every instance of the type subModelType and returns a list with - the names they have in the model. - """ - subModelNames = [] - for i in range(self.__var_start, self.__var_end +1): - mo = re.search(subModelType + r" +(\w+)[ (]?", self._modelLines[i]) - if mo: - name = mo.group(1) - subModelNames.append(name) - return subModelNames - - def getSubmodelModifierValue(self,listSubmodelNames,modifierName): - modValue = [] - positions=[] - for submodelName in listSubmodelNames: - positions.append(self.__getLinePositionName(submodelName)) - for pos in positions: - mo = re.search(modifierName + r"=(\d+)", self._modelLines[pos]) - if mo: - val = mo.group(1) - modValue.append(val) - return modValue - - def getSubModelTypesAll(self): - """ Returns a list of all submodeltypes that the model contains - """ - submodeltypes= [] - for i in range(self.__var_start,self.__var_end): - mo = re.search(r" *(?:inner)? ([\w\.]+) +(?:\w+)[ (]?", self._modelLines[i]) - if mo: - submodeltype = mo.group(1) - if not (submodeltype in submodeltypes): - submodeltypes.append(submodeltype) - return submodeltypes - - - def changeSubmodelTypeAll(self, oldType, newType, positions = []): - """Changes the type of all submodels with the type oldType to the type newType. - ATTENTION: this can lead to Problems if the equations (e.g. connections) don't - match with the new type. - """ - if positions == []: - positions = self.__getLinePositionSubmodel(oldType) - for i in positions: - mo = re.search(oldType + r" ", self._modelLines[i]) - start = mo.start() - end = mo.end() - 1 - self._modelLines[i] = self._modelLines[i][:start] + newType + self._modelLines[i][end:] - self.__writeModel() - - def changeSubmodelType(self, listSubmodelNames, oldType, newType): - """Changes the type of all submodels in listSubmodelNames from oldType to type newType. - ATTENTION: this can lead to Problems if the equations (e.g. connections) don't - match with the new type. - """ - positions=[] - for submodelName in listSubmodelNames: - positions.append(self.__getLinePositionName(submodelName)) - for pos in positions: - mo = re.search(oldType + r" ", self._modelLines[pos]) - start = mo.start() - end = mo.end() - 1 - self._modelLines[pos] = self._modelLines[pos][:start] + newType + self._modelLines[pos][end:] - self.__writeModel() - - def changeSubmodelName(self, dictSubmodelNames): - """Changes the names of the submodels in the declaration according to dictSubmodelNames, which includes the - renaming entries in the manner {"oldSubmodelName":"newSubmodelName"} - ATTENTION: corresponding connections, which include the old submodel name, must be changed seperatedly by using changeConnection - or changeConnectionPin - """ - for submodelNameOld,submodelNameNew in dictSubmodelNames.iteritems(): - pos = self.__getLinePositionName(submodelNameOld) - self._modelLines[pos] = re.sub(submodelNameOld,submodelNameNew,self._modelLines[pos]) - self.__writeModel() - - def changeVars(self, varList, overwrite = True): - """Changes or adds the initial values of variables/parameters that are specified - in the variable-section of the model. - The list varList contains the variables and the values that should be set. - The items stored in varList have the form of ["Submodel.Subsubmodel", value] - if you set the value of a differential variable the variable name has to end - with ".start". - E.g. if you want to change the startvalue of zLoad.v.re to 4, varList - has to contain the item ["zLoad.v.re.start", 4]. - If the overwrite flag is set to False, existing Values won't be changed. - """ - for item in varList: - name = item[0] - mo = re.search(r"^(\w+)\.", name) - mainName = mo.group(1) - pos = self.__getLinePositionName(mainName) - line = self._modelLines[pos] - self.__addLinetoValues(mainName) - self.__addtoValues(item) - #mo = re.search(r"^(\s+\w* *\w[\w\.]+\w) ", line) - mo = re.search(r"^(\s*\w* *\w[\w\.]+\w) ", line) - prefix = mo.group(1) - line = prefix + " " + self.__writeVariableString(self._values[mainName], mainName) + "\n" - self._modelLines[pos] = line - - self.__writeModel() - - def getValues(self, varList): - """Returns a list of the current initial values of the variables and parameters given in varList.""" - values = [] - for item in varList: - name = item - mo = re.search(r"^(\w+)\.", name) - mainName = mo.group(1) - self.__addLinetoValues(mainName) - value = self.__searchinValues(item) - values.append(value) - return values - - def deleteValues(self, varList): - """Deletes the initial values for variables or parameters given in varList. - You can delete single values or a whole group of values. - E.g if you want to delete the value for zLoad.v.re and zLoad.v.im - var list can be ["zLoad.v.re", "zLoad.v.im"] or ["zLoad.v"]. - """ - for item in varList: - name = item - mo = re.search(r"^(\w+)\.", name) - mainName = mo.group(1) - pos = self.__getLinePositionName(mainName) - line = self._modelLines[pos] - self.__addLinetoValues(mainName) - self.__deletefromValues(self._values, item.split(".")) - #mo = re.search(r"^(\s+\w* *\w[\w\.]+\w) ", line) - mo = re.search(r"^(\s*\w* *\w[\w\.]+\w) ", line) - prefix = mo.group(1) - - if mainName in self._values.keys(): - line = prefix + " " + self.__writeVariableString(self._values[mainName], mainName) + "\n" - else: - line = prefix + " " + mainName + "\n" - self._modelLines[pos] = line - self.__writeModel() - - def addConnection(self, pin1, pin2): - """Creates a new connection between pin1 and pin2 in the equation-section.""" - line = " connect(" + pin1 + "," + pin2 + ");\n" - self._modelLines.insert(self.__eq_end, line) - self.__eq_end = self.__eq_end + 1 - self.__writeModel() - - def getConnection(self, name): - """Returns the pins of connections which contain name. - Name can be either just a Submodel so that this function returns all connection that - exist for this Submodel specific connector. - The function returns two lists, the first list contains the pins that conatins name and the - second one contains the pin that are part of the connections. - """ - pin1 = [] - pin2 = [] - for i in range(self.__eq_start, self.__eq_end): - line = self._modelLines[i] - if "connect" in line and name in line: - mo = re.search(r"connect\( ?([\w\.\[\]]+) ?, ?([\w\.\[\]]+) ?\)", line) - if name in mo.group(1): - pin1.append(mo.group(1)) - pin2.append(mo.group(2)) - else: - pin1.append(mo.group(2)) - pin2.append(mo.group(1)) - return [pin1, pin2] - - def changeConnection(self, listConnectionPins): - """ - Changes the definition of a specific connection allowing to redefine both pins - The items stored in listConnectionPins must be in the form of ["oldConnectionPin1","oldConnectionPin2","newConnectionPin1","newConnectionPin2"] - """ - for i in range(self.__eq_start, self.__eq_end): - line = self._modelLines[i] - if "connect" in line and listConnectionPins[0] in line and listConnectionPins[1] in line: - line=re.sub(listConnectionPins[0],listConnectionPins[2],line) - self._modelLines[i]=re.sub(listConnectionPins[1],listConnectionPins[3],line) - self.__writeModel() - - def changeConnectionPin(self, listConnectionPin): - """ - Substitutes all the appearances of a specific connection pin by a new one - listConnectionPin should be in the form of ["oldConnectionPin","newConnectionPin"] - """ - for i in range(self.__eq_start, self.__eq_end): - line = self._modelLines[i] - if "connect" in line and listConnectionPin[0] in line: - self._modelLines[i]=re.sub(listConnectionPin[0],listConnectionPin[1],line) - self.__writeModel() - - def copyModelToDir(self, destDir="", postfix=""): - """Copys the model to the given directory and with the given postfix. At the destination directory no package integration is done. - The newly created model and its references are now stored in this class. - """ - - oldModelPath = self._modelPath - - self._mainPath=destDir - self._model=self._modelName + postfix # incorporates that the model is extracted from any package - self._modelPath = self.__getModelPath() - self._modelName = self.getModelName() - - copyfile(oldModelPath, self._modelPath) - - if self._modelLines[0][0:6] == "within": # remove package integration - self._modelLines[0]="within ;\n" - - self._modelLines[1] = "model " + self._modelName + "\n" - self._modelLines[-1] = "end " + self._modelName + ";" - - self.__writeModel() - - - def copyModelIntoPackage(self, newModelName): - """Copys the model and gives it a new name. - The newly created model is integrated in the original package and is now the one that is stored in this class. - """ - # Sets a new model name and creates the corresponding newly named file - mo = re.search(r"([\w\.]+\.)\w+$", self._model) - if not mo==None: - self._model = mo.group(1) + newModelName - else: - self._model = newModelName - self._modelName = self.getModelName() - - oldModelPath = self._modelPath - self._modelPath = self.__getModelPath() - copyfile(oldModelPath, self._modelPath) - - self._modelLines[1] = "model " + self._modelName + "\n" - self._modelLines[-1] = "end " + self._modelName + ";" - self.__writeModel() - - # Integrate newly create model in the package - packagePath = self._modelPath[:-(len(self._modelName + ".mo"))] + "package.order" - package = open(packagePath, "r") - packageData = [line for line in package] - package.close() - - if not(self._modelName + "\n" in packageData): - i = bisect(packageData, self._modelName + "\n") - packageData.insert(i, self._modelName + "\n") - - package = open(packagePath, "w") - package.writelines(packageData) - package.close() - - - #--------------------------HELPER-FUNCTIONS-------------------------------# - - def __getModelPath(self): - """Creates the modelpath out of the projectpath and the model.""" - modelpath = re.sub(r"\.", "\\\\", self._model) - modelpath = self._mainPath + modelpath + ".mo" - print(modelpath) - return modelpath - - def getModelName(self): - """Extracts the modelname out of the name that the model has in - the project. - e.g.: - When the model is "ACS_PowerSystems.SinglePhase.Examples.StatPh_Examples.Line_PQLoad" - it will return "Line_PQLoad" - """ - mo = re.search(r"\.(\w+)$", self._model) - if not mo==None: - modelName = mo.group(1) - else: - modelName = self._model - return modelName - - def __readModel(self): - """Reads the lines in the modelfile into a list""" - modelFile = open(self.__getModelPath(), "r") - modelLines = [line for line in modelFile] - modelFile.close() - return modelLines - - def __writeModel(self): - """Writes the lines stored in the private list modelLines back into the file""" - modelFile = open(self._modelPath, "w") - modelFile.writelines(self._modelLines) - modelFile.close() - - def _formatModel(self): - """Reformats the sourcecode of the model in a way so that it is easier for the script to work on it.""" - annotation = False - annotation_next = False - mergelines = False - - model_end = len(self._modelLines)-1 - self.__var_start = 0 - self.__var_end = 0 - self.__eq_start = 0 - self.__eq_end = 0 - - i = 0 - while i <= model_end : - if self._modelLines[i][0:5] == "model": - self.__var_start = i+1 - i = i + 1 - continue - elif self._modelLines[i][0:8] == "equation": - self.__var_end = i - self.__eq_start = i + 1 - i = i + 1 - continue - elif self._modelLines[i][0:3] == "end": - self.__eq_end = i - i = i + 1 - continue - - if "annotation" in self._modelLines[i]: - ann_start = self._modelLines[i].find("annotation") - if not self._modelLines[i][:ann_start].lstrip() == "": - self._modelLines.insert(i+1, " " + self._modelLines[i][ann_start:]) - self._modelLines[i] = self._modelLines[i][:ann_start] + "\n" - model_end = model_end + 1 - annotation_next = True - else: - annotation = True - mergelines = False - - if not annotation and not self._modelLines[i] == "\n": - if self._modelLines[i+1].lstrip()[0:10] == "annotation": - annotation_next = True - if mergelines == True: - self._modelLines[i-1] = self._modelLines[i-1][:-1] + self._modelLines[i].lstrip() - del self._modelLines[i] - i = i - 1 - model_end = model_end - 1 - mergelines = False - if not (self._modelLines[i][-2:] == ";\n") and not(annotation_next): - mergelines = True - elif annotation: - if self._modelLines[i][-2:] == ";\n": - annotation = False - i = i + 1 - if annotation_next == True: - annotation_next = False - annotation = True - mergelines = False - self.__writeModel() - - def __readVariableString(self, varStr): - """Transforms an initialization string into a dictionary. - E.g. (Vnom(displayUnit="kV")=20e3,v(re(start=1),im(start=2)), i(re(start=3), im(start=4))) - becomes {"Vnom":{"displayUnit":"kV","value":20e3},"v":{"re":{"start":1},"im":{"start":2}},"i":{"re":{"start":3},"im":{"start":4}}}. - """ - varStr = re.sub(r"\s", "", varStr) - varStr = re.sub(r"\)=(\d*(\.\d+)?(e\d+)?)",r",value=\1)",varStr) - varStr = re.sub(r"=", "\":", varStr) - varStr = re.sub(r"\(", "\":{\"", varStr) - varStr = re.sub(r"\)", "}", varStr) - varStr = re.sub(r",", ",\"", varStr) - - #marks the string values that aren't enclosed by qutoes in modelicacode e.g. "smoothnessSetting":Modelica.Blocks.Types.Smoothness.ConstantSegments - blocklist = re.findall(r":([\w]+\.[\w\.]+)}",varStr) - for item in blocklist: - tmpStr = item - varStr = re.sub(re.escape(tmpStr), "\"!" + tmpStr + "!\"", varStr) - - varStr = varStr[2:] - - dic = ast.literal_eval(varStr) - - return dic - - def __writeVariableString(self, dic, name): - """Recursive function that turns a dictionary into a string that can be used - to initialize a variable. - """ - varStr = name + "(" - for key in dic.keys(): - if type(dic[key]) == dict: - tmpStr = self.__writeVariableString(dic[key], key) - elif type(dic[key]) == str: - #checks if the string should have quotes or not - mo = re.search(r"!(.+)!", dic[key]) - if mo is None: - tmpStr = key + " = \""+ str(dic[key]) + "\"" - else: - tmpStr = key + " = " + str(mo.group(1)) - else: - tmpStr = key + " = " + str(dic[key]) - varStr = varStr + " " + tmpStr + "," - varStr = varStr[:-1] + ")" - return varStr - - def __getLinePositionName(self, name): - """Returns the linenumber of the line in which the variable with the name - "name" are declared. - """ - for i in range(self.__var_start,self.__var_end): - #mo = re.search( r" +(\w+) *\(", self._modelLines[i]) - #mo = re.search( r" +(\w+) *?(\([\w,\(\)=\"\. ]*\))?\s+$", self._modelLines[i]) - #mo = re.search( r" +(\w+) *?(\([\w,\(\)=\"\. -]*\))?\s+$", self._modelLines[i]) - mo = re.search(r" *([\w\.]*) " + name + r" *?(\([\w,\(\)=\"\. -_]*\))?\s+", self._modelLines[i]) - if mo: - break - return i - - def __getLinePositionSubmodel(self, subModelType): - """Returns a list which contains the linenumbers of lines in which - variables with the type subModelType are declared. - """ - lines = [] - for i in range(self.__var_start,self.__var_end): - mo = re.search(subModelType + r" +(\w+)[ (]?", self._modelLines[i]) - if mo: - lines.append(i) - return lines - - def __addtoValues(self, item): - """Adds a item with the form ["Submodel.Subsubmodel", value]. To the - dictionary self._values in which all initial values are stored. - """ - name = item[0] - value = item[1] - l = name.split(".") - dic = self._values[l[0]] - for i in range(1, len(l)-1): - if not dic.has_key(l[i]): - dic[l[i]] = {} - dic = dic[l[i]] - dic[l[-1]] = value - - def __deletefromValues(self, dic, item): - """Deletes an entry from a nested dictionary.""" - key = item[0] - if not len(item) == 1: - self.__deletefromValues(dic[key], item[1:]) - if dic[key] == {}: - del dic[key] - else: - del dic[key] - - def __searchinValues(self, name): - """Searches a Variable in the dictionary self._values and returns the initial value of this variable.""" - l = name.split(".") - dic = self._values[l[0]] - for i in range(1, len(l)-1): - if not dic.has_key(l[i]): - dic[l[i]] = {} - dic = dic[l[i]] - value = dic[l[-1]] - return value - - def __addLinetoValues(self, name): - """Adds the initial values of the variable withe the name "name" given - in the modelfile to self._modelLines. - """ - pos = self.__getLinePositionName(name) - line = self._modelLines[pos] - if not name in self._values: - #mo = re.search(r" +(\w+(\([\w,\(\)=\"\. ]*\))?)\s+$", line) - #mo = re.search(r" *([\w\.]*) " + name + r" *?(\([\w,\(\)=\"\. -_]*\))?\s+", line) - #mo = re.search(r" +(\w+(\([\w,\(\)=\"\. _]*\))?)(\s+\"\w+\")?\s+$", line) - mo = re.search(r" +(\w+(\([\w,\(\)=\"\.\: _\\/-]*\))?)(\s+\"\w+\")?\s+$", line) - varstr = mo.group(1)[len(name):] - - if not varstr == "": - tmpDic = self.__readVariableString(varstr) - else: - tmpDic = {} - self._values[name] = tmpDic - - diff --git a/Scripts/InterfaceExamples/.gitkeep b/Scripts/InterfaceExamples/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Scripts/InterfaceExamples/ExampleDymolaInterface/.gitkeep b/Scripts/InterfaceExamples/ExampleDymolaInterface/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Scripts/InterfaceExamples/ExampleDymolaInterface/DymolaExample.py b/Scripts/InterfaceExamples/ExampleDymolaInterface/DymolaExample.py new file mode 100644 index 0000000000000000000000000000000000000000..a4cc9b7a2c44bdc4c5789742a673f094da39ea15 --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleDymolaInterface/DymolaExample.py @@ -0,0 +1,34 @@ +""" +example of how to use the DymolaInterface +""" + +from py4mod.DymInterface import DymInterface + +#Instantiate the Dymola interface and start Dymola +dymola = DymInterface(showwindow=True) +print("") + +try: + # Call a function in Dymola and check its return value + result = dymola.simulateModel("Modelica.Mechanics.Rotational.Examples.CoupledClutches") + + print('cwd:{}\n'.format(dymola.changeWorkingDirectory())) + print("VarNames:") + print(dymola.getResultVarNames()) + print("\n\n\n") + + print("J1.J") + print(dymola.getResults(["J1.J", "fixed.flange.phi"])) + print("\n\n\n") + + print("J1.J") + print(dymola.getResultsFast(["J1.J", "fixed.flange.phi"])) + print("\n\n\n") + +except Exception as err: + print("Error: " + str(err)) + +finally: + if dymola is not None: + dymola.close() + dymola = None diff --git a/Scripts/InterfaceExamples/ExampleModelicaModel/test_ModelicaModel.py b/Scripts/InterfaceExamples/ExampleModelicaModel/test_ModelicaModel.py new file mode 100644 index 0000000000000000000000000000000000000000..cb47c6e6a0a9cfca56720560a440e274ee1cef65 --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleModelicaModel/test_ModelicaModel.py @@ -0,0 +1,80 @@ +#test of ModelicaFunctions.py +import os +import sys +sys.path.append(r"C:\\Users\\Martin\\Desktop\\hiwi\\git\\python-for-modelica\\Py4Mod\\py4mod") + +from ModelicaModel import ModelicaModel + +try: + #Instantiate ModelicaModel + interface = ModelicaModel("ModPowerSystems.DynPhasorThreePhase.Examples.BasicCircuits.VoltageSource_RL", r"C:\\Users\\Martin\\Desktop\\hiwi\\git\\modpowersystems\\") + + #initialize the OM interface + interface.createInterface("OPENMODELICA") + + #load ModPowerSystems package + interface.loadFile(r"C:/Users/Martin/Desktop/hiwi/git/modpowersystems/ModPowerSystems/package.mo") + + #print loaded packages + print("\nLoaded Packages:") + print(*interface.interface.getPackages(), sep='\n') + print("") + + #change working directory + cwd = os.getcwd() + wd=os.path.join(cwd, 'test_modelicaFunctions') + if not os.path.exists(wd): + os.makedirs(wd) + interface.changeWorkingDirectory(wd.replace("\\","/")) + + #create a copy of the model + interface.copyModelIntoPackage("VoltageSource_RL_copy") + + #get all submodeltypes that the model contains + print("submodels:") + print(interface.getSubModelTypesAll()) + + #get sub models type ModPowerSystems.DynPhasorThreePhase.Basics.Resistor + print(interface.getSubmodelNames("Resistor")) + + #get value of resistors + #print('start value resistor 1: {}'.format(interface.getValues(["ModPowerSystems.DynPhasorThreePhase.Basics.Resistor.R[1]"]))) + #print('start value resistor 2:{}'.format(interface.getValues(["resistor.R[2]"]))) + #print('start value resistor 3:{}'.format(interface.getValues(["resistor.R[3]"]))) + + #build model + print("\nBuild model ModPowerSystems.DynPhasorThreePhase.Examples.BasicCircuits.VoltageSource_RL") + interface.buildModel(startTime=5, stopTime=10, simflags="-override=resistor.R[2]=10") + + + #simulate model + + #create the file with the initial values: + initFile=os.path.join(wd, 'init_values.csv') + f = open(initFile,'w') + file='startTime=0 \n stopTime=30 \n resistor.R[1]=10 \n resistor.R[2]=20 \n resistor.R[3]=30 \n'.replace(" ", "") + f.write(file) + f.close() + + #it is important that the parameter file concludes with a blank line and that no + #spaces can be inserted before or after the equal sign, otherwise the + #OpenModelica compiler does not inherit the parameters in the model. + + sim_flags='-overrideFile {}'.format(initFile).replace("\\","/") + print("\nSimulate Model:") + interface.simulate(simflags=sim_flags) + + #check start value of the resistors + test = interface.getResults(["resistor.R[1]", "resistor.R[2]", "resistor.R[3]", "time"]) + print('\nresistor.R[1]: {}'.format(test[0][0])) + print('resistor.R[2]: {}'.format(test[1][0])) + print('resistor.R[3]: {}'.format(test[2][0])) + print('time: {}'.format(test[3])) + print("\n") + + print(test) + + +except Exception as err: + print(err) + exit(1) \ No newline at end of file diff --git a/Scripts/InterfaceExamples/ExampleOMInterface/.gitkeep b/Scripts/InterfaceExamples/ExampleOMInterface/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Scripts/InterfaceExamples/ExampleOMInterface/test_OMFunctions_old.py b/Scripts/InterfaceExamples/ExampleOMInterface/test_OMFunctions_old.py new file mode 100644 index 0000000000000000000000000000000000000000..24c29acea259b59766591ab8243ae8112ab54af5 --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleOMInterface/test_OMFunctions_old.py @@ -0,0 +1,77 @@ +#test of OMFunctions.py +import os +import sys +sys.path.append(r"C:\\Users\\Martin\\Desktop\\hiwi\\git\\python-for-modelica\\Py4Mod\\py4mod") + +from OMFunctions_old import OMInterface + +#Instantiate OMInterface +print("") +modelica = OMInterface() +print("") + +#load Modelica-package +try: + modelica.loadLibrary("Modelica") +except Exception as err: + print(err) + +#print loaded packages +print("Loaded Packages:") +print(*modelica.getPackages(), sep='\n') +print("") + +#change working directory +cwd = os.getcwd() +wd=os.path.join(cwd, 'test') +if not os.path.exists(wd): + os.makedirs(wd) +modelica.changeWorkingDirectory(wd.replace("\\","/")) + +#check the current working directory +print('current working directory: "{}"\n'.format(modelica.changeWorkingDirectory(""))) + +#build model +model = "Modelica.Blocks.Examples.PID_Controller" +try: + modelica.buildModel(model) +except Exception as err: + print(err) + exit() + +print("\nget component names") +print(modelica.getComponents()) +#print(*modelica.getComponents(), sep='\n') + +#change sim option values +modelica.setSimulationOptions(stopTime=10, stepSize=20) + +#get simulation values +simNamesList = ['startTime', 'stopTime', 'stepSize', 'tolerance', 'solver'] +simValues = modelica.getSimulationValues() +print("\nsimulation values:") +for index, value in enumerate(simValues): + print('{}={}'.format(simNamesList[index], value)) +print("") + +#set spring.w_rel to 10 deg: +modelica.setValue({"spring.w_rel":10}) + +#simulate model +print("Simulate model:") +modelica.simulate() + +#get output variables +print("\noutput variables:") +print(*modelica.getResultVarNames(), sep='\n') +print("\n") + +#get solutions +print("get simulation results:") +test = modelica.getResults(["time", "PI.I.y","inertia1.phi"]) +print('time: ') +print(test[0]) +print("\n") +print('PI.I.y: ') +print(test[1]) +print("") diff --git a/Scripts/InterfaceExamples/ExampleOMInterface/test_OMInterface.py b/Scripts/InterfaceExamples/ExampleOMInterface/test_OMInterface.py new file mode 100644 index 0000000000000000000000000000000000000000..e9742844d9afec608fd7e6bdd4ae615a05f5cae5 --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleOMInterface/test_OMInterface.py @@ -0,0 +1,74 @@ +#test of OMInterface.py +import os +import sys +import time +import numpy as np + +sys.path.append(r"C:\\Users\\Martin\\Desktop\\hiwi\\git\\python-for-modelica\\Py4Mod\\py4mod") +from OMInterface import OMInterface + +#Instantiate OMInterface +print("") +modelica = OMInterface() +print("") + +#load Modelica-package +try: + modelica.loadLibrary("Modelica") +except Exception as err: + print(err) + +#print loaded packages +print("Loaded Packages:") +print(*modelica.getPackages(), sep='\n') +print("") + +#change working directory +cwd = os.getcwd() +wd=os.path.join(cwd, 'test_OMInterface') +if not os.path.exists(wd): + os.makedirs(wd) +modelica.changeWorkingDirectory(wd.replace("\\","/")) + +#check the current working directory +print('current working directory: "{}"'.format(modelica.changeWorkingDirectory(""))) + +#build model +model = "Modelica.Blocks.Examples.PID_Controller" +print("Build Model:") +try: + modelica.buildModel(model, startTime=5, stopTime=10) +except Exception as err: + print(err) + exit() + +#simulate model +print("\nSimulate model:") +modelica.simulate('-override spring.w_rel=12') + +#get output variables +print("\noutput variables:") +print(*modelica.getResultVarNames(), sep='\n') + +#get solutions +start = time.time() +print("\nget simulation results:") +test = modelica.getResults(["time", "PI.I.y","spring.w_rel"]) +print(test) +end = time.time() +print('time elapsed: {}'.format(end-start)) + +#get solutions +start = time.time() +print("\nget simulation results fast:") +test = modelica.getResultsFast(["time", "PI.I.y","spring.w_rel"]) +print(test) +end = time.time() +print('time elapsed: {}'.format(end-start)) + +#test setValue(): +values={"spring.w_rel":30} +modelica.setValue(values) +modelica.simulate() +test = modelica.getResults(["spring.w_rel"]) +print('spring.w_rel[0]={}'.format(test[0][0])) diff --git a/Scripts/InterfaceExamples/ExampleOMInterface/test_performance.py b/Scripts/InterfaceExamples/ExampleOMInterface/test_performance.py new file mode 100644 index 0000000000000000000000000000000000000000..c0ba583476b0805c16a6e52e086930cb30bca2ae --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleOMInterface/test_performance.py @@ -0,0 +1,83 @@ +""" +comparison between the time used by the function simulate() and simulateModel() to simulate 5 times 25s the model "Modelica.Blocks.Examples.PID_Controller" +""" + +import time +import os +import sys +sys.path.append("C:/Users/Martin/Desktop/hiwi/git/python-for-modelica/Py4Mod/py4mod") + +from OMInterface import OMInterface + +#Instantiate OMInterface +print("") +modelica = OMInterface() +print("") + +#load Modelica-package +try: + modelica.loadLibrary("Modelica") + + #change working directory + cwd = os.getcwd() + wd=os.path.join(cwd, 'test_performance') + if not os.path.exists(wd): + os.makedirs(wd) + modelica.changeWorkingDirectory(wd.replace("\\","/")) + + print("using the funcion simulateModel()...") + start0 = time.time() + for x in range(0, 5): + start = time.time() + print('starts {}.simulation'.format(x+1)) + + #simulate model + modelica.simulateModel("Modelica.Blocks.Examples.PID_Controller", startTime=0.0, stopTime=25.0, + simflags="-override=spring.w_rel=10,PI.Dzero.k=5.0") + + end = time.time() + print('time elapsed {}.simulation: {}seg'.format(x+1, end - start)) + + end = time.time() + print('total time elapsed: {}'.format(end - start0)) + + + print("\n======================\n") + + print("using the function simulate()...") + start0 = time.time() + + for x in range(0, 5): + start = time.time() + print('starts {}.simulation'.format(x+1)) + if x==0: + #build model + modelica.buildModel("Modelica.Blocks.Examples.PID_Controller") + + #create the file with the initial values: + initFile=os.path.join(wd, 'init_values.csv') + f = open(initFile,'w') + file='startTime=0 \n stopTime=25 \n spring.w_rel=10.0 \n PI.Dzero.k=5.0 \n'.replace(" ", "") + f.write(file) + f.close() + + sim_flags='-overrideFile {}'.format(initFile).replace("\\","/") + modelica.simulate(simflags=sim_flags) + + end = time.time() + print('time elapsed {}.simulation: {}seg'.format(x+1, end - start)) + + print('total time elapsed: {}'.format(end - start0)) + print("\n======================") + + #check start value of spring.w_rel and PI.Dzero.k and time + test = modelica.getResults(["spring.w_rel", "PI.Dzero.k", "time"]) + print('\nspring.w_rel[0]: {}'.format(test[0][0])) + print('PI.Dzero.k[0]: {}'.format(test[1][0])) + print('time: {}'.format(test[2])) + print("\n") + + +except Exception as err: + print(err) + exit() \ No newline at end of file diff --git a/Scripts/InterfaceExamples/ExampleOMInterface/test_simflag_override.py b/Scripts/InterfaceExamples/ExampleOMInterface/test_simflag_override.py new file mode 100644 index 0000000000000000000000000000000000000000..9792134d544ec62c0036fec17d79eaf505e4df49 --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleOMInterface/test_simflag_override.py @@ -0,0 +1,52 @@ +""" +example of how to use the function simulateModel and the sim flag -override +""" + +import os +import sys +sys.path.append(r"C:\\Users\\Martin\\Desktop\\hiwi\\git\\python-for-modelica\\Py4Mod\\py4mod") + +from OMInterface import OMInterface + +#Instantiate OMInterface +print("") +modelica = OMInterface() +print("") + +#load Modelica-package +try: + modelica.loadLibrary("Modelica") +except Exception as err: + print(err) + + +#example how to set init values with the sim flag -override +try: + #change working directory + cwd = os.getcwd() + wd=os.path.join(cwd, 'test_simflag_override') + if not os.path.exists(wd): + os.makedirs(wd) + modelica.changeWorkingDirectory(wd.replace("\\","/")) + + #build model + print("Build model Modelica.Blocks.Examples.PID_Controller") + modelica.buildModel("Modelica.Blocks.Examples.PID_Controller", startTime=5, stopTime=10) + + + #simulate model + #you can edit the initial values with the sim flag -override + modelica.simulate(simflags="-override=spring.w_rel=20,PI.Dzero.k=5.0,stopTime=50") + + + #check start value of spring.w_rel and PI.Dzero.k + test = modelica.getResults(["spring.w_rel", "PI.Dzero.k", "time"]) + print('\nspring.w_rel[0]: {}'.format(test[0][0])) + print('PI.Dzero.k[0]: {}'.format(test[1][0])) + print('time: {}'.format(test[2])) + print("\n") + +except Exception as err: + print(err) + + diff --git a/Scripts/InterfaceExamples/ExampleOMInterface/test_simflag_overrideFile.py b/Scripts/InterfaceExamples/ExampleOMInterface/test_simflag_overrideFile.py new file mode 100644 index 0000000000000000000000000000000000000000..2575df30925ae80be0c9d5b5d9ae6046ff355a7b --- /dev/null +++ b/Scripts/InterfaceExamples/ExampleOMInterface/test_simflag_overrideFile.py @@ -0,0 +1,61 @@ +""" +example of how to use the function simulateModel and set initial values from a file +""" + +import os +import sys +sys.path.append("C:/Users/Martin/Desktop/hiwi/git/python-for-modelica/Py4Mod/py4mod") + +from OMInterface import OMInterface + +#Instantiate OMInterface +print("") +modelica = OMInterface() +print("") + +#load Modelica-package +try: + modelica.loadLibrary("Modelica") +except Exception as err: + print(err) + +#example how to set initial values from a file +try: + #change working directory + cwd = os.getcwd() + wd=os.path.join(cwd, 'test_simflag_overrideFile') + if not os.path.exists(wd): + os.makedirs(wd) + modelica.changeWorkingDirectory(wd.replace("\\","/")) + + #build model + print("Build model Modelica.Blocks.Examples.PID_Controller") + modelica.buildModel("Modelica.Blocks.Examples.PID_Controller") + + #simulate model + #you can use one file to set initial values + #Note that: -overrideFile CANNOT be used with -override. Use when variables for -override are too many. + + #create the file with the initial values: + #it is important that the parameter file concludes with a blank line and that no + #spaces can be inserted before or after the equal sign, otherwise the + #OpenModelica compiler does not inherit the parameters in the model. + initFile=os.path.join(wd, 'init_values.csv') + f = open(initFile,'w') + file='startTime=0 \n stopTime=30 \n spring.w_rel=20.0 \n PI.Dzero.k=10.0 \n'.replace(" ", "") + f.write(file) + f.close() + + sim_flags='-overrideFile {}'.format(initFile).replace("\\","/") + print("Simulate Model:") + modelica.simulate(simflags=sim_flags) + + #check start value of spring.w_rel and PI.Dzero.k and time + test = modelica.getResults(["spring.w_rel", "PI.Dzero.k", "time"]) + print('\nspring.w_rel[0]: {}'.format(test[0][0])) + print('PI.Dzero.k[0]: {}'.format(test[1][0])) + print('time: {}'.format(test[2])) + print("\n") + +except Exception as err: + print(err)