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