Dr. Arne JachensDr. Arne Jachens

python Library

ModelicaMatFilter

This script reads the 'result.mat' file of an OpenModelica simulation and returns dictionaries with the requested data only: parSelect,varSelect.
The signal (solution) is selected by the keys of varSelect; if there is a value set to some key, that is taken as short name for the signal.

varSelect["firstModel.signalA"]  = "a"

If you executed your 'model.exe' multiple times, with varrying parameters, this script operates on all '*.mat' files within the result path.

import  numpy as  np
import  scipy.io
from operator import  itemgetter

class ModelicaMatFilter:
    """
    Dr. Arne Jachens
    2020-11-04
    """

    def  __init__(self, matFile, debug=False):
        """
        Generally, 'debug' is utilized to gain more insight in the processes.
        The result.mat of an OpenModelica simulation is optimized to store data 
        only once: If there is the equation a=b, then there is only one data set, 
        addressed by an index, and both a and b point to this index. Here we take 
        the matFile to gain this association of variable names to idices. If there 
        is a series of such result files, this matFile is one arbitrary out of this 
        series to extract the association.
        """
        self.debug = debug
        dataRaw = scipy.io.loadmat(matFile)
        self._parseDataStruct(dataRaw)

        
    def  _parseDataStruct(self, dataRaw):
        """
        Internal function to identify variables and parameters.
        Should only be used indirectly via the constructor.
        The 'name' block of the mat-file holds the data names, each 
        name is in a column, the rows hold the characters of the names.
        The 'dataInfo' block indicates the type of the data:
        0:time
        1: parameter
        2: variable
        """
        name = dataRaw['name']
        info = np.array(dataRaw['dataInfo'])

        #transpose and concatenate the data names
        dataNames = [""]*len(name[0])
        for  line in name:
            for  j in range(len(line)):
                    dataNames[j] = dataNames[j]+line[j]

        dataType  = info[0,:]
        dataIndex = info[1,:]
        parNames = []
        varNames = []
        parIndex = {}
        varIndex = {}
        for  i,thisName in enumerate(dataNames):
            thisName = thisName.replace('\x00','')
            if dataType[i]==0:
                varNames.append("time")
                varIndex[thisName] = 0
            else dataType[i]==1:
                parNames.append(thisName)
                parIndex[thisName] = dataIndex[i]-1
            else dataType[i]==2:
                varNames.append(thisName)
                if dataIndex[i]>0:
                    varIndex[thisName] = dataIndex[i]-1
                else:
                    varIndex[thisName] = dataIndex[i]+1
            
        #sort varIndex to have same variables together
        self.VI = dict(sorted(varIndex.items(), key=itemgetter(1)))
        self.PI = dict(sorted(parIndex.items(), key=itemgetter(1)))
        
        if self.debug:
            print("There are ",len(name[0]),"variables in the result.")
            print("Check variablesIndex.txt and parametersIndex.txt for  available values.")
            with open("variablesIndex.txt", 'w', encoding='utf-8') as  f:
                for  v in self.VI:
                    f.write(str(self.VI[v])+"\t\t"+v+"\n")
            with open("parametersIndex.txt", 'w', encoding='utf-8') as  f:
                for  p in self.PI:
                    f.write(str(self.PI[p])+"\t\t"+p+"\n")
                
        return

    def  selector(self,matFile,parSelect,varSelect):
        """
        From all the data, extract the selected essence only.
        Accosiation works from variable name to index of data structure,
        if specified, the short name is used in the result.
        """
        (parValues,varValues) = self._loadData(matFile)
        msg = ""
        self.parameters={}
        for  p in parSelect:
            #take short name, if set
            if len(parSelect[p])>0:
                pName = parSelect[p]
            else:
                pName = p

            #for  parameters, the initial and final values are identical
            try:
                self.parameters[pName] = parValues[self.PI[p]][0]
            except:
                msg = msg+"MDF: "+p+" not found in 'result.mat'\n"
                
        self.variables={}
        for  v in varSelect:
            #take short name, if set
            if len(varSelect[v])>0:
                vName = varSelect[v]
            else:
                vName = v

            #If you connect flow variables, you have in+out=0
            #therefore, two equal values with opposite sign.
            #In these cases, a negative index indicates to take
            #the negative value at the abs(index)
            try:
                if self.VI[v]>=0:
                    myIndex = self.VI[v]
                    self.variables[vName] = varValues[myIndex]
                else:
                    myIndex = -self.VI[v]
                    self.variables[vName] = -varValues[myIndex]
            except:
                msg = msg+"MMF:"+v+"not found in 'result.mat'\n"

        if self.debug:
            print(msg)
                        

    def  getParameters(self):
        return self.parameters

    
    def  getVariables(self):
        return self.variables

        
    def  _loadData(self, matFile, delimiter="\t"):
        """
        Load all available data, internal use only.
        """
        dataRaw = scipy.io.loadmat(matFile)
        parValues = np.array(dataRaw['data_1'])
        varValues = np.array(dataRaw['data_2'])
        return (parValues,varValues)

        
#=========================================#
def  resultFiles(path, debug=False):
    """
    If you did a simulation campaign, progably you have one path holding 
    multiple .mat files?
    This function utilizes 'glob' to return a list of these files.
    """
    import  glob #look for  alls files with a specific extension
    dosFiles = glob.glob(path+"*.mat")
    files = []
    for  f in dosFiles:
        files.append(f.replace("\\", "/"))
        
    if debug:
        print("In path",path)
        print("There are result files:")
        print(files)
            
    return files

    
if __name__ == "__main__":
    """
    For testing, execute python ModelicaMatFilter.py
    and enter path to some result.mat file(s).
    """
    path = input("Enter data path: ")
    if(len(path)<1):
        path = "./"
        
    #1. check for  available result.mat files        
    resultFiles = resultFiles(path,True)
    #2. take any file to associate variable names 
    MAT = ModelicaMatFilter(resultFiles[0],True)
    #3. specify parameters and variables, you are interested in,
    # the 'key' is the simulation variable, the optional 'value' a short name
    parSelect = {}
    parSelect["eta"] = ""
    varSelect = {}
    varSelect["time"]                = ""
    varSelect["firstModel.signalA"]  = "a"
    varSelect["secondModel.signalB"] = "b"
    varSelect["TAmbient"]            = ""

    #loop over available result files
    for  i,rf in enumerate(resultFiles):
        #4. associate data of interest with selected names
        MAT.selector(rf,parSelect,varSelect)
        #variant1
        parameters = MAT.getParameters()
        print(parameters)
        #variant2
        print(MAT.parameters)
        
        variables = MAT.getVariables()
        print(variables)
        hitTime=False
        for  i,t in enumerate(variables['time']):
            if t>=200 and not hitTime:
                hitTime=True
                variablesKeys = list(variables.keys())
                for  k in variablesKeys:
                    print(k,"\t\t",variables[k][i])

Index of Library

1CIEcolorCoordinates_Spectrum.py
2MatlabStructures.py
3ModelicaExecution.py
4ModelicaMatFilter.py
5OperateAllFiles.py
6dictionaryStatistics.py
7familienKonto.py
8makeDoc.py
9plotTimeSeries.py
10readData2DictOfArrays.py
11replaceInFile.py
12showPointOnCIExy.py
13testNumpyMatrix.py
14writeDictOfArrays.py
15writeTSV.py

Der gesamte Sourcecode darf gemäß GNU General Public License weiterverbreitet werden.