runSolver.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. import ivp
  2. import os
  3. import cModel
  4. import sys
  5. import json
  6. import numpy
  7. import time
  8. import scipy.interpolate
  9. import shutil
  10. defaultValues={\
  11. 'method':'LSODA',\
  12. 'atol':1e-4,\
  13. 'rtol':1e-4,\
  14. 't0':0,\
  15. 'tmax':1,\
  16. 'mode':'IVPSimultaneous',\
  17. 'nt':201,
  18. 'tUnit':'day',\
  19. 'network':'labkey-klimt.json',\
  20. 'startFromRef':'None',\
  21. 'project':'Analysis/Run',\
  22. 'schema':'lists',\
  23. 'query':'runs',\
  24. 'view':'WithStrings'}
  25. def get(setup,par):
  26. try:
  27. return setup[par]
  28. except KeyError:
  29. pass
  30. return defaultValues[par]
  31. def parseSetup(setupFile):
  32. with open(setupFile,'r') as f:
  33. setup=json.load(f)
  34. out={}
  35. for p in defaultValues:
  36. out[p]=get(setup,p)
  37. #q=[x if not x in out for x in setup]
  38. q=[x for x in setup if not x in out]
  39. for x in q:
  40. out[x]=setup[x]
  41. return out
  42. def getScale(setup):
  43. tUnit=setup['tUnit']
  44. return convertUnit(tUnit)
  45. def convertUnit(tUnit):
  46. if tUnit=='min':
  47. return 1
  48. if tUnit=='hour':
  49. return 60
  50. if tUnit=='day':
  51. return 24*60
  52. if tUnit=='month':
  53. return 24*60*30
  54. if tUnit=='year':
  55. return 24*60*365
  56. return 1
  57. def getStrides(setup):
  58. tmax=float(setup['tmax'])*convertUnit(setup['tUnit'])
  59. t0=setup['t0']
  60. print('t0 {} tmax {}'.format(t0,tmax))
  61. try:
  62. stride=setup['stride']
  63. except KeyError:
  64. return [{'label':'','length':tmax-t0}]
  65. #cover tmax with steps of length stride['length']
  66. strides=[]
  67. strideLength=stride['length']*convertUnit(stride['tUnit'])
  68. i=0
  69. while t0<tmax:
  70. strides.append({'length':strideLength,'label':'_step{:03d}'.format(i)})
  71. t0+=strideLength
  72. i+=1
  73. return strides
  74. def getFiles(setup, getAll=False):
  75. strides=getStrides(setup)
  76. readable=['t','sol','se','qt','sOut']
  77. #get last stride
  78. steps=[strides[-1]]
  79. if getAll:
  80. steps=strides
  81. inFiles=[]
  82. for s in steps:
  83. l=s['label']
  84. inFiles.append({x:'{}{}.txt'.format(x,l) for x in readable})
  85. return inFiles
  86. def main(parFiles,jobDir,startDir='NONE'):
  87. model=cModel.model()
  88. setupFile=parFiles[0]
  89. modelFile=parFiles[1]
  90. parameterFile=parFiles[2]
  91. model.parse(modelFile,parameterFile)
  92. setup=parseSetup(setupFile)
  93. scale=getScale(setup)
  94. tmax=float(setup['tmax'])*scale
  95. print('Using {}'.format(jobDir))
  96. if startDir!='NONE':
  97. print('Using solution from {}'.format(startDir))
  98. #t0,y0,s0,lut,lutSE=getStartPointFromDir(startDir)
  99. startPoint=getStartPointFromDir(startDir)
  100. print('t0={}'.format(startPoint['t0']))
  101. else:
  102. try:
  103. print('Using solution from {}'.format(setup['startFromRef']))
  104. except KeyError:
  105. pass
  106. startPoint=getStartPoint(setup)
  107. print('t0={}'.format(startPoint['t0']))
  108. #we should reorder S1 to match model.lutSE
  109. #S1 is nxm, so we should reorder columns
  110. t0=startPoint['t0']
  111. y0=startPoint['y0']
  112. s0=startPoint['s0']
  113. sIn=numpy.zeros(s0.shape)
  114. if s0.size>0:
  115. for x in model.lutSE:
  116. #this could be modified for different ordering of lut as well
  117. sIn[:,model.lutSE[x]]=s0[:,startPoint['lutSE'][x]]
  118. #copy t0 to setup to help get the strides right
  119. setup['t0']=t0
  120. strides=getStrides(setup)
  121. for step in strides:
  122. t1=t0+step['length']
  123. start_time=time.time()
  124. if setup['mode']=='SequentialOdeint':
  125. t,sol,se,s1=ivp.solveSequentialOdeint(model,t1,setup['nt'],t0,y0,sIn)
  126. if setup['mode']=='SimultaneousOdeint':
  127. t,sol,se,s1=ivp.solveSimultaneousOdeint(model,t1,setup['nt'],t0,y0,sIn)
  128. if setup['mode']=='IVP':
  129. t,sol,se,s1=ivp.solveSequential(model,t1,\
  130. nt=setup['nt'],\
  131. atol=setup['atol'],\
  132. rtol=setup['rtol'],method=setup['method'],\
  133. t0=t0,y0=y0,sIn=sIn)
  134. if setup['mode']=='IVPSimultaneous':
  135. t,sol,se,s1=ivp.solveSimultaneous(model,t1,\
  136. nt=setup['nt'],\
  137. atol=setup['atol'],\
  138. rtol=setup['rtol'],method=setup['method'],\
  139. t0=t0,y0=y0,sIn=sIn)
  140. end_time=time.time()
  141. #interpolate on s1
  142. #s1 has shape ft x n x m
  143. #where ft is LSODE driven number of points
  144. qt,sOut=interpolate(setup,model,t,s1,t0,t1)
  145. print('Time: {:.3f} s'.format(end_time-start_time))
  146. y0=sol[-1]
  147. t0=t[-1]
  148. sIn=sOut[-1]
  149. #store
  150. writeable={'t':t,'sol':sol,'se':se,'qt':qt,'sOut':sOut}
  151. l=step['label']
  152. writeable={os.path.join(jobDir,'{}{}.txt'.format(x,l)):writeable[x]\
  153. for x in writeable}
  154. for x in writeable:
  155. if x.find('sOut')>-1:
  156. write3D(x,writeable[x],model.lut,model.lutSE)
  157. else:
  158. numpy.savetxt(x,writeable[x])
  159. print('Completed step {}'.format(l))
  160. print('Completed run')
  161. #update setup with new content (particularly t0)
  162. setupOut=os.path.join(jobDir,'setup.json')
  163. with open(setupOut,'w+') as f:
  164. f.write(json.dumps(setup))
  165. #this is 3D, so new routines
  166. #write model and parameter file
  167. origFiles=[modelFile,parameterFile]
  168. outFiles=["model.json","parameters.json"]
  169. for x in zip(origFiles,outFiles):
  170. xOut=os.path.join(jobDir,x[1])
  171. shutil.copyfile(x[0],x[1])
  172. def interpolate(setup,model,t,s1,t0,tmax):
  173. #interpolate on s1
  174. #s1 has shape ft x n x m
  175. #where ft is LSODE driven number of points
  176. sOut=numpy.zeros((setup['nt'],model.n,model.m))
  177. qt=numpy.linspace(t0,tmax,setup['nt'])
  178. for i in range(model.n):
  179. for j in range(model.m):
  180. y=s1[:,i,j]
  181. f = scipy.interpolate.interp1d(t, y, kind='cubic')
  182. sOut[:,i,j]=f(qt)
  183. return qt,sOut
  184. def invertLUT(lut):
  185. fcode=[None]*len(lut)
  186. for x in lut:
  187. fcode[lut[x]]=x
  188. return fcode
  189. def write3D(fName,a,lut,lutSE):
  190. with open(fName,'w') as f:
  191. f.write('#Shape {}\n'.format(a.shape))
  192. f.write('#Lut {}\n'.format(invertLUT(lut)))
  193. f.write('#Lut (SE) {}\n'.format(invertLUT(lutSE)))
  194. i=0
  195. for data_slice in a:
  196. f.write('#\t Slice {}\n'.format(i))
  197. numpy.savetxt(f,data_slice)
  198. i+=1
  199. def parseLUT(line,isSE=False):
  200. SE=''
  201. if isSE:
  202. SE='(SE)'
  203. header='#Lut {}'.format(SE)
  204. names=line.replace(header,'').\
  205. replace('[','').\
  206. replace(']','').\
  207. replace(' ','').\
  208. replace('\n','').\
  209. replace("'",'').\
  210. split(',')
  211. lut={names[i]:i for i in range(len(names))}
  212. return lut
  213. def parseShape(line):
  214. shape=line.replace('#Shape ','').\
  215. replace('\n','').\
  216. replace('(','').\
  217. replace(')','').\
  218. split(',')
  219. return [int(x) for x in shape]
  220. def read3D(fName):
  221. new_data=numpy.loadtxt(fName)
  222. #read and parse first three lines to get shape,lut and lutSE
  223. nLines=3
  224. with open(fName,'r') as f:
  225. lines=[f.readline() for i in range(nLines)]
  226. shape=parseShape(lines[0])
  227. lut=parseLUT(lines[1])
  228. lutSE=parseLUT(lines[2],isSE=True)
  229. return new_data.reshape(shape),lut,lutSE
  230. def loadSolutionFromRef(setup, loadAll=False):
  231. if setup['startFromRef']=='None':
  232. return {'t':0,'sol':numpy.array([]),'sOut':numpy.array([]),'x':numpy.array([]),'y':numpy.array([])}
  233. ref=setup['startFromRef']
  234. return loadSolutionFromRunRef(setup,ref,loadAll)
  235. def connect(setup):
  236. nixSuite=os.path.join(os.path.expanduser('~'),'software','src','nixSuite')
  237. sys.path.append(os.path.join(nixSuite,'wrapper'))
  238. import nixWrapper
  239. nixWrapper.loadLibrary('labkeyInterface')
  240. import labkeyInterface
  241. import labkeyFileBrowser
  242. import labkeyDatabaseBrowser
  243. net=labkeyInterface.labkeyInterface()
  244. fconfig=os.path.join(os.path.expanduser('~'),'.labkey',setup['network'])
  245. net.init(fconfig)
  246. #debug
  247. net.getCSRF()
  248. db=labkeyDatabaseBrowser.labkeyDB(net)
  249. fb=labkeyFileBrowser.labkeyFileBrowser(net)
  250. return db,fb
  251. def getRunRef(setup,key):
  252. #get solution from key rather than runRef (easier to copy, understand)
  253. db,fb=connect(setup)
  254. keyFilter={'variable':'Key','value':'{}'.format(key),'oper':'eq'}
  255. ds=db.selectRows(setup['project'],setup['schema'],setup['query'],[keyFilter],setup['view'])
  256. try:
  257. if len(ds['rows'])==0:
  258. return 'NONE'
  259. except KeyError:
  260. print(ds)
  261. return 'NONE'
  262. print('Found {} rows for {}'.format(len(ds['rows']),key))
  263. return ds['rows'][0]['runRef']
  264. def loadSolutionFromRunRef(setup,ref,loadAll=False):
  265. db,fb=connect(setup)
  266. remoteDir=fb.formatPathURL(setup['project'],'/'.join(['jobs',ref]))
  267. try:
  268. localDir=setup['localDir']
  269. except KeyError:
  270. localDir=os.path.join(os.path.expanduser('~'),'temp','jobDir')
  271. if not os.path.isdir(localDir):
  272. os.mkdir(localDir)
  273. #download setup
  274. f='setup.json'
  275. localPath=os.path.join(localDir,f)
  276. remotePath='/'.join([remoteDir,f])
  277. fb.readFileToFile(remotePath,localPath)
  278. setupFile=os.path.join(localDir,f)
  279. setupOld=parseSetup(setupFile)
  280. inFiles=getFiles(setupOld,loadAll)
  281. #inFiles.extend(parFiles)
  282. i=0
  283. n=len(inFiles)
  284. for fSet in inFiles:
  285. for x in fSet:
  286. f=fSet[x]
  287. localPath=os.path.join(localDir,f)
  288. remotePath='/'.join([remoteDir,f])
  289. fb.readFileToFile(remotePath,localPath)
  290. print('Loaded {}/{} {}'.format(i+1,n,localDir))
  291. i+=1
  292. return loadSolutionFromDir(localDir,loadAll)
  293. def merge(a1,a2):
  294. try:
  295. return numpy.concatenate(a1,a2)
  296. except ValueError:
  297. return a2
  298. def loadSolutionFromDir(jobDir,loadAll=False):
  299. setupFile=os.path.join(jobDir,'setup.json')
  300. setup=parseSetup(setupFile)
  301. inFiles=getFiles(setup,loadAll)
  302. isFirst=True
  303. operators={x:numpy.loadtxt for x in inFiles[0]}
  304. operators['sOut']=read3D
  305. data={}
  306. i=0
  307. n=len(inFiles)
  308. for fSet in inFiles:
  309. idata={}
  310. print('Parsing [{}/{}]'.format(i+1,n))
  311. for f in fSet:
  312. path=os.path.join(jobDir,fSet[f])
  313. idata=operators[f](os.path.join(jobDir,fSet[f]))
  314. if f=='sOut':
  315. lut=idata[1]
  316. lutSE=idata[2]
  317. idata=idata[0]
  318. try:
  319. data[f]=numpy.concatenate((data[f],idata))
  320. except KeyError:
  321. data[f]=idata
  322. i+=1
  323. data['lut']=lut
  324. data['lutSE']=lutSE
  325. data['setup']=setup
  326. return data
  327. def getStartPointFromDir(jobDir):
  328. data=loadSolutionFromDir(jobDir)
  329. return startPointObject(data)
  330. def startPointObject(data):
  331. vrs={'t':'t0','sol':'y0','sOut':'s0'}
  332. out={vrs[x]:data[x][-1] for x in vrs}
  333. addVrs=['lut','lutSE']
  334. for x in addVrs:
  335. out[x]=data[x]
  336. return out
  337. def getStartPoint(setup):
  338. if setup['startFromRef']=='None':
  339. return {'t0':0,'y0':numpy.array([]),'s0':numpy.array([]),'lut':[],'lutSE':[]}
  340. data=loadSolutionFromRef(setup)
  341. return startPointObject(data)
  342. if __name__=="__main__":
  343. parFiles=sys.argv[1].split(';')
  344. jobDir=sys.argv[2]
  345. main(parFiles,jobDir)