Skip to content

Handle process externally in ModelicaSystem.simulate and ModelicaSystem.linearized #230

Open
@kiroorooki

Description

@kiroorooki

Hello!
I've noticed that we could run simulations in parallel if we handled the process outside the simulate() method.

It is useful to me because I need to run a log of simulations, and running them in parallel saves me a lot of time.
In order to keep the same functionality, I added a boolean handleProcessOutside, and if set to true, I don't wait() and terminate() the process, I simply return it, so the user can handle the process themselves. That way people can use the library the same way, or get extended functionality if needed with just a boolean.

In my case, I run all my simulations, and I make sure all of them are done later when I save my results (opening .mat file, converting things to csv...).

I just realized the library changed quite a bit, but I believe the same functionality can still be implemented in _run_cmd and propagated to the methods using it (simulate and linearized).

Thanks for your hard work, I hope my suggestion makes sense!

This is how I currently implemented it in my version for reference (quite outdated):
`

def simulate(self, resultfile=None, simflags=None, verbose=True, handleProcessOutside=False):  # 11
    """
    This method simulates model according to the simulation options.
    usage
    >>> simulate()
    >>> simulate(resultfile="a.mat")
    >>> simulate(simflags="-noEventEmit -noRestart -override=e=0.3,g=10) set runtime simulation flags
    """
    if(resultfile is None):
        r=""
        self.resultfile = os.path.join(self.tempdir, self.modelName + "_res.mat").replace("\\", "/")
    else:
        r=" -r=" + resultfile
        self.resultfile = resultfile

    # allow runtime simulation flags from user input
    if(simflags is None):
        simflags=""
    else:
        simflags=" " + simflags

    overrideFile = os.path.join(self.tempdir, '{}.{}'.format(self.modelName + "_override", "txt")).replace("\\", "/")
    if (self.overridevariables or self.simoptionsoverride):
        tmpdict=self.overridevariables.copy()
        tmpdict.update(self.simoptionsoverride)
        # write to override file
        file = open(overrideFile, "w")
        for (key, value) in tmpdict.items():
            name = key + "=" + value + "\n"
            file.write(name)
        file.close()
        override =" -overrideFile=" + overrideFile
    else:
        override =""

    if (self.inputFlag):  # if model has input quantities
        for i in self.inputlist:
            val=self.inputlist[i]
            if(val==None):
                val=[(float(self.simulateOptions["startTime"]), 0.0), (float(self.simulateOptions["stopTime"]), 0.0)]
                self.inputlist[i]=[(float(self.simulateOptions["startTime"]), 0.0), (float(self.simulateOptions["stopTime"]), 0.0)]
            if float(self.simulateOptions["startTime"]) != val[0][0]:
                print("!!! startTime not matched for Input ",i)
                return
            if float(self.simulateOptions["stopTime"]) != val[-1][0]:
                print("!!! stopTime not matched for Input ",i)
                return
            if val[0][0] < float(self.simulateOptions["startTime"]):
                print('Input time value is less than simulation startTime for inputs', i)
                return
        self.createCSVData()  # create csv file
        csvinput=" -csvInput=" + self.csvFile
    else:
        csvinput=""

    if (platform.system() == "Windows"):
        getExeFile = os.path.join(self.tempdir, '{}.{}'.format(self.modelName, "exe")).replace("\\", "/")
    else:
        getExeFile = os.path.join(self.tempdir, self.modelName).replace("\\", "/")
    currentDir = os.getcwd()
    if (os.path.exists(getExeFile)):
        cmd = getExeFile + override + csvinput + r + simflags
        #print(cmd)
        os.chdir(self.tempdir)
        if (platform.system() == "Windows"):
            omhome = os.path.join(os.environ.get("OPENMODELICAHOME"))
            dllPath = os.path.join(omhome, "bin").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc").replace("\\", "/") + os.pathsep + os.path.join(omhome, "lib/omc/cpp").replace("\\", "/") +  os.pathsep + os.path.join(omhome, "lib/omc/omsicpp").replace("\\", "/")
            my_env = os.environ.copy()
            my_env["PATH"] = dllPath + os.pathsep + my_env["PATH"]
            if not verbose:
                p = subprocess.Popen(cmd, env=my_env, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
            else:
                p = subprocess.Popen(cmd, env=my_env)
            if not handleProcessOutside:
                p.wait()
                p.terminate()
        else:
            if not verbose:
                p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
            else:
                p = subprocess.Popen(cmd)
        os.chdir(currentDir)
        self.simulationFlag = True
        if handleProcessOutside:
            return p
    else:
        raise Exception("Error: Application file path not found: " +  getExeFile)

`

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions