runSolver.py 10 KB

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