runSolver.py 13 KB

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