runSolver.py 12 KB

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