Commit 81cf84ad authored by ext-bershanska_a's avatar ext-bershanska_a
Browse files

runOPAL.py and simulation.py changed in order to work with OPTIMIZE (Issue #8)...

runOPAL.py and simulation.py changed in order to work with OPTIMIZE (Issue #8) and new features, sich as --filename and shortcuts, have been added
parent ef2f0f1b
...@@ -14,53 +14,62 @@ import subprocess ...@@ -14,53 +14,62 @@ import subprocess
from simulation import Simulation from simulation import Simulation
from opaldict import OpalDict from opaldict import OpalDict
def getBaseName():
path ='.'
ext1 ='*.tmpl'
ext2 ='*.data'
templates = glob.glob(os.path.join(path,ext1))
datafiles = glob.glob(os.path.join(path,ext2))
if templates:
str1 = templates[0]
else:
print('No template file (.tmpl) found')
sys.exit()
if datafiles:
str2 = datafiles[0]
else:
print('No data file (.data) found')
sys.exit()
str1spl = str1.split('.') def getPaths(path, pattern, name):
str2spl = str2.split('.') result = glob.glob(os.path.join(path,pattern))
if str1spl[1] == str2spl[1]: if not result:
name = str2spl[1] print('No '+name+' file ('+pattern+') found')
name = name.split('/')[1]
else:
print('Template and data filename do not match', str1spl, str2spl)
sys.exit() sys.exit()
return name return result
def printUsage(): def getBaseName(inputfilePath): # EDIT: more flexible (can have several *.data files)
print("./runOPAL.py [--help] [--quiet] [--info=num] [--test] [--keep] [--queue=qname] [--hypert=num] [--nobatch] [ATTR=SCANVALUE] {[ATTR=VALUE]}") templates = getPaths(inputfilePath, '*.tmpl', 'template')
datafiles = getPaths('.', '*.data', 'data')
name = templates[0].split('/')[-1][:-5] #NOTE: choose first (alphanumeric order) *.tmpl file by default
if os.path.isfile(os.path.join('.',name+'.data')):
return name
datafiles = getPaths('.', '*.data', 'data')
print('Template and data filename do not match, '+name+'.data expected')
sys.exit()
def printUsage(): #EDIT: reorder for readibility, add --filename and shortcuts
print("./runOPAL.py [--help] [--filename=str] [--test] [--quiet] [--info=num] [--test] [--keep] [--queue=qname] [--hypert=num] [--nobatch] [ATTR=SCANVALUE] {[ATTR=VALUE]}")
print("") print("")
print("--help prints this message") print("--help prints this message")
print("--test does everything but submitting the job") print("--filename | -f=<str> sets base falie name for both *.data and *.tmpl")
print("--keep if same simulation has been run before, keep old data and abort") print("--test | -t does everything but submitting the job")
print("--nobatch run opal locally not using the batch system and waits until the job is done") print("--keep | -k if same simulation has been run before, keep old data and abort")
print("--quiet suppress debug printout") print("--nobatch run opal locally not using the batch system and waits until the job is done")
print("--info=<num> steers the std-output of OPAL. The range is 0 < num < 6 (default), from minimal to maximum output") print("--noopt ignore optimization template (if any) and perform regular simulation")
print("--quiet suppress debug printout")
print("--info | -i=<num> steers the std-output of OPAL. The range is 0 < num < 6 (default), from minimal to maximum output")
print("--queue=<qname> defines in which queue the job goes. Overwrites QUEUE (deprecated SGE_QUEUE)") print("--queue=<qname> defines in which queue the job goes. Overwrites QUEUE (deprecated SGE_QUEUE)")
print("--hypert=<num> defines the number of Hyper-Threads used. Default 0") print("--hypert=<num> defines the number of Hyper-Threads used. Default 0")
print("") print("")
print("SCANVALUE=start:end:step, scans a parameter space, e.g. example TFWHM=0.85:0.90:0.01 ") print("SCANVALUE=start:end:step, scans a parameter space, e.g. example TFWHM=0.85:0.90:0.01 ")
print("ATTR refers to a name in the data file") print("ATTR refers to a name in the data file")
print("") print("")
print("Recognized environment variables: DISTRIBUTIONS, FIELDMAPS, OPAL_EXE_PATH, TEMPLATES, QUEUE, RAM, TIME (deprecated SGE_)") print("Recognized environment variables: DISTRIBUTIONS, FIELDMAPS, OPTIMIZER, OPAL_EXE_PATH, TEMPLATES, QUEUE, RAM, TIME (deprecated SGE_)")
# temporary see issue #8 # temporary see issue #8
print("") print("")
print("Important: runOPAL is currently not compatible with the commands OPTIMIZE and SAMPLE") print("Important: runOPAL is currently not compatible with the commands OPTIMIZE and SAMPLE")
def checkCompat(tmplFile, incompatible): #NOTE: SAMPLE command not compatible with runOPAL (issue #8)
templateFile = open(tmplFile,'r')
for line in templateFile:
if line.startswith('//'):
continue
if any(command in line for command in incompatible):#'OPTIMIZE' in line or 'SAMPLE' in line:
print(', '.join(incompatible)+' command(s) currently not compatible with runOPAL')
sys.exit()
templateFile.close()
def traverseRanges(list, opaldict, args, doNobatch): def traverseRanges(list, opaldict, args, doNobatch):
""" """
Traverse all possible combinations of range variable values. Start simulation Traverse all possible combinations of range variable values. Start simulation
...@@ -90,6 +99,7 @@ def traverseRanges(list, opaldict, args, doNobatch): ...@@ -90,6 +99,7 @@ def traverseRanges(list, opaldict, args, doNobatch):
traverseRanges(tail, opaldict, args, doNobatch) traverseRanges(tail, opaldict, args, doNobatch)
curval = curval + step curval = curval + step
def main(argv): def main(argv):
""" """
main method main method
...@@ -99,73 +109,81 @@ def main(argv): ...@@ -99,73 +109,81 @@ def main(argv):
doTest = False doTest = False
doKeep = False doKeep = False
doNobatch = False doNobatch = False
doOptimize = True #NOTE: this flag is opposite of --noopt (I want to optimize by default)
queue = "" queue = ""
info = 6 info = 6
hypert = 0 hypert = 0
qid = -1 qid = -1
for arg in argv: inputfilePath = None
if arg.startswith("--test"): baseFileName = None
for arg in argv: #EDIT: reorder for readibility, add --filename and shortcuts
if arg.startswith("--help"):
printUsage()
exit()
elif arg.startswith("--filename") or arg.startswith("-f"):
baseFileName = arg.split("=")[1]
elif arg.startswith("--test") or arg.startswith("-t"):
doTest = True doTest = True
elif arg.startswith("--hypert"): elif arg.startswith("--keep") or arg.startswith("-k"):
hypert = int(arg.split("=")[1])
elif arg.startswith("--keep"):
doKeep = True doKeep = True
elif arg.startswith("--nobatch"): elif arg.startswith("--nobatch"):
doNobatch = True doNobatch = True
elif arg.startswith("--info"): elif arg.startswith("--noopt"):
doOptimize = False
elif arg.startswith("--quiet"):
quiet = True
elif arg.startswith("--info") or arg.startswith("-i"):
info = arg.split("=")[1] info = arg.split("=")[1]
elif arg.startswith("--queue"): elif arg.startswith("--queue"):
queue = arg.split("=")[1] queue = arg.split("=")[1]
elif arg.startswith("--help"): elif arg.startswith("--hypert"):
printUsage() hypert = int(arg.split("=")[1])
exit() elif arg.startswith("-"): #EDIT: ignore any unknown argument '-', not just arg.startswith("--")
elif arg.startswith("--quiet"):
quiet = True
elif arg.startswith("--"):
print(arg,'is not a valid option, see --help for the available options') print(arg,'is not a valid option, see --help for the available options')
exit() exit()
if (os.getcwd() == os.environ.get('TEMPLATES')): # safety check, EDIT: now for both TEMPLATES and OPTIMIZER
print('Working directory is the same as the TEMPLATES directory! This is not allowed... bye!') if os.getcwd() == os.environ.get('TEMPLATES') or os.getcwd() == os.environ.get('OPTIMIZER'):
print('Working directory is the same as the TEMPLATES or OPTIMIZER directory! This is not allowed... bye!')
sys.exit() sys.exit()
os.system('rm -f tmplbak17.tmpl') # make sure we have no junk around (NOTE: don't create tmplbak17.tmpl)
#EDIT: determine what kind of job should be ran
if doOptimize and os.environ.get('OPTIMIZER'):
print('job type: OPTIMIZATION')
inputfilePath = os.environ.get('OPTIMIZER')
#NOTE: maybe set tmpl dirctory for simulation
else: else:
# make sure we have no junk around print('job type: SIMULATION')
os.system('rm -f tmplbak17.tmpl') doOptimize = False
if os.environ.get('TEMPLATES'):
if (os.environ.get('TEMPLATES')): inputfilePath = os.environ.get('TEMPLATES')
inputfilePath = os.environ.get('TEMPLATES') #if not os.path.isfile(inputfilePath + ".tmpl"): #NOTE: Idk why this line is supposed to be here
# os.system('cp ' + inputfilePath + '/*.tmpl .')
if not os.path.isfile(inputfilePath + ".tmpl"): # #print('Put .tmpl in local directory')
os.system('cp ' + inputfilePath + '/*.tmpl .') elif (glob.glob(os.path.join('.','*.tmpl'))):
else:
ext1 ='*.tmpl'
if (glob.glob(os.path.join('.',ext1))):
inputfilePath = '../' inputfilePath = '../'
#print('.tmpl found in ./')
else: else:
print('Template file unknown -> exiting ...') print('Template file unknown -> exiting ...')
sys.exit() sys.exit()
baseFileName = getBaseName() #EDIT: check that tmpl and data files can be found or guessed
if not baseFileName:
baseFileName = getBaseName(inputfilePath)
elif not os.path.isfile(os.path.join(inputfilePath,baseFileName+'.tmpl')):
print(baseFileName+'.tmpl cannot be found! Check if it exists in '+inputfilePath)
sys.exit()
print('baseFileName = '+baseFileName)
dataFile = baseFileName + '.data' dataFile = baseFileName + '.data'
tmplFile = baseFileName + '.tmpl' tmplFile = os.path.join(inputfilePath,baseFileName+'.tmpl') #EDIT: .tmpl file is no longer copied to wd
oinpFile = baseFileName + '.in' # the resulting OPAL input file oinpFile = baseFileName + '.in' # the resulting OPAL input file
# Optimizer and Sample command currently not compatible with runOPAL (issue #8) checkCompat(tmplFile, ['SAMPLE']) #EDIT: check compatibility
templateFile = open(tmplFile,'r')
for line in templateFile:
if line.startswith('//'):
continue
if 'OPTIMIZE' in line or 'SAMPLE' in line:
print('OPTIMIZE and SAMPLE command currently not compatible with runOPAL')
sys.exit()
templateFile.close()
if os.environ.get('TEMPLATES'):
if os.path.isfile(baseFileName + ".tmpl"):
os.system("mv " + baseFileName + ".tmpl tmplbak17.tmpl")
#create the dictionary #create the dictionary
opaldict = OpalDict(dataFile) opaldict = OpalDict(dataFile)
# check if template values must be changed # check if template values must be changed
...@@ -175,7 +193,7 @@ def main(argv): ...@@ -175,7 +193,7 @@ def main(argv):
if not opaldict.hasRanges(): if not opaldict.hasRanges():
sim = Simulation(opaldict) sim = Simulation(opaldict)
qid = sim.run(N, baseFileName, inputfilePath, tmplFile, oinpFile, doTest, doKeep, doNobatch, info, queue, hypert, quiet) qid = sim.run(N, baseFileName, inputfilePath, tmplFile, oinpFile, doTest, doKeep, doNobatch, doOptimize, info, queue, hypert, quiet)
if doNobatch: if doNobatch:
print( "... finished!\n") print( "... finished!\n")
#else: #else:
...@@ -205,16 +223,13 @@ def main(argv): ...@@ -205,16 +223,13 @@ def main(argv):
print(ranges) print(ranges)
#run simulations of all possible combinations #run simulations of all possible combinations
args = [N, baseFileName, inputfilePath, tmplFile, oinpFile, doTest, doKeep, doNobatch, info, queue, hypert, quiet] args = [N, baseFileName, inputfilePath, tmplFile, oinpFile, doTest, doKeep, doNobatch, doOptimize, info, queue, hypert, quiet]
traverseRanges(list(ranges.items()), opaldict, args, doNobatch) traverseRanges(list(ranges.items()), opaldict, args, doNobatch)
# clean up # clean up
os.system("rm -f *.bak ") os.system("rm -f *.bak ")
os.chdir("..") os.chdir("..")
# clean up
if os.path.isfile("tmplbak17.tmpl"):
os.system("mv tmplbak17.tmpl " + "." + baseFileName + ".tmpl")
#call main #call main
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -10,7 +10,7 @@ import sys,os,shutil, subprocess ...@@ -10,7 +10,7 @@ import sys,os,shutil, subprocess
#import numpy as np #import numpy as np
# Helper methods ### Helper methods
def isInDirectory(filepath, directory): def isInDirectory(filepath, directory):
# From https://stackoverflow.com/questions/3812849/how-to-check-whether-a-directory-is-a-sub-directory-of-another-directory # From https://stackoverflow.com/questions/3812849/how-to-check-whether-a-directory-is-a-sub-directory-of-another-directory
''' Check if filepath is inside directory ''' ''' Check if filepath is inside directory '''
...@@ -23,14 +23,29 @@ def linkDirectory(path, name=''): ...@@ -23,14 +23,29 @@ def linkDirectory(path, name=''):
print (name + ' directory is subdirectory of working directory! runOPAL cannot handle this.. bye!') print (name + ' directory is subdirectory of working directory! runOPAL cannot handle this.. bye!')
sys.exit() sys.exit()
# lndir and if fails try cp # lndir and if fails try cp
if os.system('lndir ' + path) != 0: if os.system('lndir '+path) != 0:
#print("lndir failed (possibly doesn't exist on this system), using cp -rs..."), print("lndir failed (possibly doesn't exist on this system), using ln -rs... \n"), #EDIT: '\n' added
if os.listdir(path): if os.listdir(path):
os.system('cp -rs ' + path + '/* .') os.system('ln -rs '+path+'/* .') #EDIT: changed from cp to ln
def linkFile(path, name): #EDIT: new helper function for convenience
'''Make a file available in working directory with a symbolic link'''
path = os.path.join(path,name)
if not os.path.isfile(path):
print (name+' cannot be found')
sys.exit()
os.system('ln -s '+path+' .')
class Simulation: def extractStr(line, name):
zero = line.find(name)
if zero < 0:
return None
start = min(x for x in [line.find('"',zero ), line.find("'", zero )] if x > 0) +1
end = min(x for x in [line.find('"',start), line.find("'", start)] if x > 0)
return line[start:end]
class Simulation:
def __init__(self, opaldict): def __init__(self, opaldict):
self.opaldict = opaldict self.opaldict = opaldict
self.dirname = "" self.dirname = ""
...@@ -51,14 +66,13 @@ class Simulation: ...@@ -51,14 +66,13 @@ class Simulation:
os.mkdir(self.dirname) os.mkdir(self.dirname)
return True return True
def run(self,N, baseFileName, inputfilePath, tmplFile, oinpFile, doTest, doKeep, doNobatch, info, queue, hypert, quiet): def run(self,N, baseFileName, inputfilePath, tmplFile, oinpFile, doTest, doKeep, doNobatch, doOptimize, info, queue, hypert, quiet):
# make directory name indicating changed values # make directory name indicating changed values
self.dirname = baseFileName self.dirname = baseFileName
if N >= 0: if N >= 0:
self.dirname += str(N) self.dirname += str(N)
self.dirname += self.opaldict.generateDirectoryName() self.dirname += self.opaldict.generateDirectoryName()
try: try:
CORES = self.opaldict['CORES'] CORES = self.opaldict['CORES']
except KeyError: except KeyError:
...@@ -67,9 +81,9 @@ class Simulation: ...@@ -67,9 +81,9 @@ class Simulation:
if self.createDirectory(self.dirname, doKeep, quiet) == False: if self.createDirectory(self.dirname, doKeep, quiet) == False:
print( "Simulation results already exist") print( "Simulation results already exist")
return return
os.chdir(self.dirname) os.chdir(self.dirname)
# Linking magnet and RF files # Linking magnet and RF files
if (os.environ.get('FIELDMAPS')): if (os.environ.get('FIELDMAPS')):
fieldmapPath = os.environ.get('FIELDMAPS') fieldmapPath = os.environ.get('FIELDMAPS')
...@@ -78,29 +92,49 @@ class Simulation: ...@@ -78,29 +92,49 @@ class Simulation:
if not (os.path.isdir(fieldmapPath)): if not (os.path.isdir(fieldmapPath)):
print( 'Fieldmap directory unknown exiting ...') print( 'Fieldmap directory unknown exiting ...')
sys.exit() sys.exit()
linkDirectory(fieldmapPath,'Fieldmap') linkDirectory(fieldmapPath,'Fieldmap')
# Link distribution directory if present # Link distribution directory if present
if (os.environ.get('DISTRIBUTIONS')): if (os.environ.get('DISTRIBUTIONS')):
distributionPath = os.environ.get('DISTRIBUTIONS') distributionPath = os.environ.get('DISTRIBUTIONS')
if os.path.isdir(distributionPath): if os.path.isdir(distributionPath):
linkDirectory(distributionPath,'Distribution') linkDirectory(distributionPath,'Distribution')
# Read in the file # Read in the file
filedata = None filedata = None
with open(inputfilePath + tmplFile, 'r') as file : with open(tmplFile, 'r') as file :
filedata = file.read() filedata = file.read()
# do the replacements in the templatefile # do the replacements in the templatefile
for s,value in self.opaldict.items(): for s,value in self.opaldict.items():
# Replace the target string # Replace the target string
filedata = filedata.replace('_'+s+'_', str(value)) filedata = filedata.replace('_'+s+'_', str(value))
# Write the file out again # Write the file out again
with open(oinpFile, 'w') as file: with open(oinpFile, 'w') as file:
file.write(filedata) file.write(filedata)
#EDIT: Link .tmpl and .dat files or OPTIMIZE
#NOTE: What's the best place to link tmpl file? $TEMPLATES, _TEMPLATEDIR_, or parisng?
if doOptimize:
flag = False
tmplDir = None
tmplIn = None
templateFile = open(oinpFile,'r')
for line in templateFile:
if not line.startswith('//'):
if 'OPTIMIZE' in line:
flag = True
if flag and not tmplDir:
tmplDir = extractStr(line,'TEMPLATEDIR')
if flag and not tmplIn:
tmplIn = extractStr(line,'INPUT').split('/')[-1]
templateFile.close()
linkFile('..', tmplIn[:-5]+'.data')
os.mkdir(tmplDir)
os.chdir(tmplDir)
linkFile(os.path.join('../..',tmplDir), tmplIn)
os.chdir('..')
if os.environ.get('OPAL_EXE_PATH'): if os.environ.get('OPAL_EXE_PATH'):
if doNobatch: if doNobatch:
opalexe = os.environ.get('OPAL_EXE_PATH') + '/opal' opalexe = os.environ.get('OPAL_EXE_PATH') + '/opal'
...@@ -113,10 +147,9 @@ class Simulation: ...@@ -113,10 +147,9 @@ class Simulation:
print( 'Using templatefile at ' + inputfilePath) print( 'Using templatefile at ' + inputfilePath)
print( 'Using fieldmaps at ' + fieldmapPath) print( 'Using fieldmaps at ' + fieldmapPath)
print( 'Parameter set in ' + oinpFile + ' are:') print( 'Parameter set in ' + oinpFile + ' are:')
for s, value in sorted(self.opaldict.items()): #EDIT: fixed indentation
for s, value in sorted(self.opaldict.items()): if quiet == False:
if quiet == False: print( ' :::: ' + s + ' = ' + str(value))
print( ' :::: ' + s + ' = ' + str(value))
if not doNobatch: if not doNobatch:
#hostname = commands.getoutput("hostname") #hostname = commands.getoutput("hostname")
...@@ -241,7 +274,9 @@ class Simulation: ...@@ -241,7 +274,9 @@ class Simulation:
os.chdir('..') os.chdir('..')
return qid return qid
### Write for host
def WriteCori(self, opalexe, oinpFile, cores, time, ram, info, name): def WriteCori(self, opalexe, oinpFile, cores, time, ram, info, name):
title=oinpFile.partition(".")[0] title=oinpFile.partition(".")[0]
myfile = open(name,'w') myfile = open(name,'w')
...@@ -254,14 +289,15 @@ class Simulation: ...@@ -254,14 +289,15 @@ class Simulation:
s1 += "srun -n 1 .... \n" s1 += "srun -n 1 .... \n"
myfile.write(s1) myfile.write(s1)
myfile.close() myfile.close()
def WriteEngaging(self, opalexe, oinpFile, cores, time, ram, info, name): def WriteEngaging(self, opalexe, oinpFile, cores, time, ram, info, name):
print("Writing SLURM run file for Engaging cluster at MIT") print("Writing SLURM run file for Engaging cluster at MIT")
cores = int(cores) cores = int(cores)
coresPerNode = 32 coresPerNode = 32
partition = os.getenv("SLURM_PARTITION", "sched_mit_psfc") partition = os.getenv("SLURM_PARTITION", "sched_mit_psfc")
if ((cores%coresPerNode) is 0): if ((cores%coresPerNode) is 0):
nodes = int(cores/coresPerNode) nodes = int(cores/coresPerNode)
else: else:
...@@ -321,13 +357,14 @@ class Simulation: ...@@ -321,13 +357,14 @@ class Simulation:
outfile.write("#The job starts in the directory it was submitted from.\n") outfile.write("#The job starts in the directory it was submitted from.\n")
outfile.write("#Note that mpirun knows from SLURM how many processor we have\n") outfile.write("#Note that mpirun knows from SLURM how many processor we have\n")
outfile.write("mpirun {} {} --info {} --warn 6\n".format(opalexe, oinpFile, info)) outfile.write("mpirun {} {} --info {} --warn 6\n".format(opalexe, oinpFile, info))
def WriteEdison(self, opalexe, oinpFile, cores, time, ram, info, name): def WriteEdison(self, opalexe, oinpFile, cores, time, ram, info, name):
title=oinpFile.partition(".")[0] title=oinpFile.partition(".")[0]
coresPerNode = 24 coresPerNode = 24
cores = int(cores) cores = int(cores)
if cores % coresPerNode == 0: if cores % coresPerNode == 0:
nodes = int(cores / coresPerNode) nodes = int(cores / coresPerNode)
else: else:
...@@ -346,13 +383,9 @@ class Simulation: ...@@ -346,13 +383,9 @@ class Simulation:
myfile.write(s1) myfile.write(s1)
myfile.close() myfile.close()
def WriteMerlin5(self, opalexe, oinpFile, cores, time, ram, info, name, partition): def WriteMerlin5(self, opalexe, oinpFile, cores, time, ram, info, name, partition):
# ADA this is for the new PSI Merlin5
#
# ADA this is for the new PSI Merlin5
#
title = oinpFile.partition(".")[0] title = oinpFile.partition(".")[0]
myfile = open(name, 'w') myfile = open(name, 'w')
s1 = "#!/bin/bash -l \n" s1 = "#!/bin/bash -l \n"
...@@ -371,13 +404,12 @@ class Simulation: ...@@ -371,13 +404,12 @@ class Simulation:
s1 += "mpirun " + opalexe + " " + oinpFile + " --info " + str(info) + "\n" s1 += "mpirun " + opalexe + " " + oinpFile + " --info " + str(info) + "\n"
myfile.write(s1) myfile.write(s1)
myfile.close() myfile.close()
def WritePizDaint(self, opalexe, oinpFile, cores, time, ram, info, name, partition):
def WritePizDaint(self, opalexe, oinpFile, cores, time, ram, info, name, partition):
# XC40 Compute Nodes # XC40 Compute Nodes
# Intel Xeon E5-2696 v4 @ 2.10GHz (2x18 cores, 64/128 GB RAM) # Intel Xeon E5-2696 v4 @ 2.10GHz (2x18 cores, 64/128 GB RAM)
# http://user.cscs.ch/computing_systems/piz_daint/index.html # http://user.cscs.ch/computing_systems/piz_daint/index.html
coresPerNode = 36 coresPerNode = 36
title = oinpFile.partition(".")[0] title = oinpFile.partition(".")[0]
myfile = open(name, 'w') myfile = open(name, 'w')
...@@ -398,6 +430,7 @@ class Simulation: ...@@ -398,6 +430,7 @@ class Simulation:
myfile.write(s1) myfile.write(s1)
myfile.close() myfile.close()
def WritePBSBlues(self, opalexe, oinpFile, cores, time, ram, info, queue):