runSolver.py 10 KB

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