cModel.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. import numpy
  2. import json
  3. import os
  4. import scipy.interpolate
  5. #for partial function specializations
  6. import functools
  7. import function
  8. import importlib
  9. importlib.reload(function)
  10. class model:
  11. def __init__(self):
  12. self.compartments={}
  13. self.seJ={}
  14. def add_source(self,compartmentName,formula):
  15. self.compartments[compartmentName]['source']=formula
  16. def add_compartment(self,compartmentName):
  17. self.compartments[compartmentName]={}
  18. self.compartments[compartmentName]['targets']={}
  19. self.compartments[compartmentName]['sensTargets']={}
  20. def getTimeUnit(self):
  21. try:
  22. return self.mod['timeUnit']
  23. except KeyError:
  24. return 's'
  25. def bind(self,src,target,qName,pcName,useVolume=0):
  26. #establish a flow from source compartment to the target
  27. #the source equation (where we subtract the current)
  28. #in fact, this is the diagonal element
  29. #get volume names
  30. srcVName=self.getVolumePar(src,useVolume)
  31. #generate coupling object (w/derivatives)
  32. pSrc=self.couplingObject(-1,qName,pcName,srcVName)
  33. #this includes derivatives and value!
  34. self.addValueObject(src,src,pSrc)
  35. #special target which is not part of calculation
  36. if target=='dump':
  37. return
  38. #the target equation (where we add the current)
  39. #get volume names
  40. targetVName=self.getVolumePar(target,useVolume)
  41. #generate coupling object
  42. pTarget=self.couplingObject(1,qName,pcName,targetVName)
  43. #equation is for target compartment, but binding for source
  44. self.addValueObject(target,src,pTarget)
  45. def addValueObject(self,targetName,srcName,cObject):
  46. #always binds equation id and a variable
  47. targetList=self.compartments[targetName]['targets']
  48. addValue(targetList,srcName,cObject["value"])
  49. der=cObject["derivatives"]
  50. for d in der:
  51. targetSE=self.getSEJ_comp(d,targetName)
  52. addValue(targetSE,srcName,der[d])
  53. def couplingObject(self,sign, qParName, pcParName, vParName):
  54. qPar=self.get(qParName)
  55. pcPar=self.get(pcParName)
  56. vPar=self.get(vParName)
  57. q=qPar['value']
  58. pc=pcPar['value']
  59. v=vPar['value']
  60. DPC=pcPar['derivatives']
  61. DQ=qPar['derivatives']
  62. DV=vPar['derivatives']
  63. if any(['function' in qPar,'function' in pcPar, 'function' in vPar]):
  64. fq=function.to(q)
  65. fpc=function.to(pc)
  66. fv=function.to(v)
  67. f=lambda t,q=fq,pc=fpc,v=fv,s=sign:s*q(t)/v(t)/pc(t)
  68. dfdPC=lambda t,f=f,pc=fpc:-f(t)/pc(t)
  69. dPC=function.generate(dfdPC,DPC)
  70. dfdQ=lambda t,f=f,q=fq: f(t)/q(t)
  71. dQ=function.generate(dfdQ,DQ)
  72. dfdV=lambda t,f=f,v=fv: -f(t)/v(t)
  73. dV=function.generate(dfdV,DV)
  74. return function.Object(f,[dPC,dQ,dV])
  75. else:
  76. f=sign*q/v/pc
  77. return derivedObject(sign*q/v/pc,\
  78. [{'df':-f/pc,'D':DPC},\
  79. {'df':sign/v/pc,'D':DQ},\
  80. {'df':-f/v,'D':DV}])
  81. #derivatives is the combination of the above
  82. def getVolumePar(self,compartment,useVolume=1):
  83. #returnis volume name, if found and useVolume is directed,
  84. #or a standard parameter one
  85. if not useVolume:
  86. return "one"
  87. try:
  88. return self.mod["volumes"][compartment]
  89. #parV=self.mod["parameters"][parVName]
  90. except KeyError:
  91. pass
  92. return "one"
  93. def build(self):
  94. comps=self.compartments
  95. self.n=len(comps)
  96. self.fu=[lambda t:0]*self.n
  97. self.lut={c:i for (i,c) in zip(range(self.n),comps.keys())}
  98. self.dM={}
  99. self.fM=numpy.zeros((self.n,self.n))
  100. for c in comps:
  101. comp=comps[c]
  102. if 'source' in comp:
  103. self.fu[self.lut[c]]=parseFunction(comp['source'])
  104. for t in comp['targets']:
  105. arr=comp['targets'][t]
  106. if function.contains(arr):
  107. try:
  108. self.dM[self.lut[c]][self.lut[t]]=\
  109. function.sumArray(arr)
  110. except (KeyError,TypeError):
  111. self.dM[self.lut[c]]={}
  112. self.dM[self.lut[c]][self.lut[t]]=\
  113. function.sumArray(arr)
  114. else:
  115. #just set once
  116. self.fM[self.lut[c],self.lut[t]]=sum(arr)
  117. #use fM to build static part of fJ
  118. N=system.n*(system.m+1)
  119. self.fJ=numpy.zeros((N,N))
  120. for i in range(system.m+1):
  121. fJ[i*system.n:(i+1)*system.n,i*system.n:(i+1)*system.n]=system.M(t)
  122. return system.jacobi(t)
  123. f
  124. #
  125. #build SE part
  126. self.buildSE()
  127. def buildSE(self):
  128. #check which parameterst to include
  129. parList=[]
  130. pars=self.parSetup['parameters']
  131. for parName in self.seJ:
  132. #print(par)
  133. par=pars[parName]
  134. usePar=calculateDerivative(par)
  135. #print('[{}]: {}'.format(usePar,par))
  136. if not usePar:
  137. continue
  138. parList.append(parName)
  139. #print(parList)
  140. self.m=len(parList)
  141. self.lutSE={c:i for (i,c) in zip(range(self.m),parList)}
  142. w=self.getWeights(self.lutSE)
  143. w=numpy.sqrt(w)
  144. self.qSS={}
  145. self.SS=numpy.zeros((self.m,self.n,self.n))
  146. for parName in parList:
  147. sources=self.seJ[parName]
  148. for compartment in sources:
  149. targets=sources[compartment]
  150. for t in targets:
  151. k=self.lutSE[parName]
  152. i=self.lut[compartment]
  153. j=self.lut[t]
  154. #print('[{} {} {}] {}'.format(parName,compartment,t,targets[t]))
  155. arr=targets[t]
  156. if not function.contains(arr):
  157. self.SS[k,i,j]=w[k]*sum(arr)
  158. continue
  159. ft=function.sumArray(arr,w[k])
  160. try:
  161. self.qSS[k][i][j]=ft
  162. except (KeyError,TypeError):
  163. try:
  164. self.qSS[k][i]={}
  165. self.qSS[k][i][j]=ft
  166. except (KeyError,TypeError):
  167. self.qSS[k]={}
  168. self.qSS[k][i]={}
  169. self.qSS[k][i][j]=ft
  170. #use fM to build static part of fJ
  171. N=self.n*(self.m+1)
  172. self.fJ=numpy.zeros((N,N))
  173. for i in range(self.m+1):
  174. self.fJ[i*self.n:(i+1)*self.n,i*self.n:(i+1)*self.n]=self.fM
  175. def inspect(self):
  176. comps=self.compartments
  177. pars=self.parSetup['parameters']
  178. #pars=self.mod['parameters']
  179. tu=self.getTimeUnit()
  180. print('Time unit: {}'.format(tu))
  181. print('Compartments')
  182. for c in comps:
  183. print('{}/{}:'.format(c,self.lut[c]))
  184. comp=comps[c]
  185. if 'source' in comp:
  186. print('\tsource\n\t\t{}'.format(comp['source']))
  187. print('\ttargets')
  188. for t in comp['targets']:
  189. print('\t\t{}[{},{}]: {}'.format(t,self.lut[c],self.lut[t],\
  190. comp['targets'][t]))
  191. print('Flows')
  192. for f in self.flows:
  193. fName=self.flows[f]
  194. fParName=self.mod['flows'][fName]
  195. fPar=pars[fParName]
  196. print('\t{}[{}]:{} [{}]'.format(f,fName,fParName,self.get(fParName)))
  197. print('Volumes')
  198. for v in self.mod['volumes']:
  199. vParName=self.mod['volumes'][v]
  200. vPar=pars[vParName]
  201. print('\t{}:{} [{}]'.format(v,vParName,self.get(vParName)))
  202. print('Partition coefficients')
  203. for pc in self.mod['partitionCoefficients']:
  204. pcParName=self.mod['partitionCoefficients'][pc]
  205. pcPar=pars[pcParName]
  206. print('\t{}:{} [{}]'.format(pc,pcParName,self.get(pcParName)))
  207. print('SE parameters')
  208. for p in self.seJ:
  209. print(p)
  210. sources=self.seJ[p]
  211. for compartment in sources:
  212. targets=sources[compartment]
  213. for t in targets:
  214. print('\t SE bind {}/{}:{}'.format(compartment,t,targets[t]))
  215. def parse(self,setupFile,parameterFile):
  216. with open(setupFile,'r') as f:
  217. self.mod=json.load(f)
  218. with open(parameterFile,'r') as f:
  219. self.parSetup=json.load(f)
  220. for m in self.mod['compartments']:
  221. self.add_compartment(m)
  222. self.add_default_parameters()
  223. #standard parameters such as one,zero etc.
  224. for s in self.mod['sources']:
  225. #src=mod['sources'][s]
  226. self.add_source(s,self.mod['sources'][s])
  227. self.flows={}
  228. #pars=self.mod['parameters']
  229. pars=self.parSetup['parameters']
  230. for f in self.mod['flows']:
  231. #skip comments
  232. if f.find(':')<0:
  233. continue
  234. comps=f.split(':')
  235. c0=splitVector(comps[0])
  236. c1=splitVector(comps[1])
  237. for x in c0:
  238. for y in c1:
  239. pairName='{}:{}'.format(x,y)
  240. self.flows[pairName]=f
  241. for b in self.mod['bindings']['diffusion']:
  242. #whether to scale transfer constants to organ volume
  243. #default is true, but changing here will assume no scaling
  244. useVolume=1
  245. comps=b.split('->')
  246. try:
  247. pcParName=self.mod['partitionCoefficients'][b]
  248. except KeyError:
  249. pcParName="one"
  250. kParName=self.mod['bindings']['diffusion'][b]
  251. #operate with names to allow for value/function/derived infrastructure
  252. self.bind(comps[0],comps[1],kParName,pcParName,useVolume)
  253. for q in self.mod['bindings']['flow']:
  254. comps=q.split('->')
  255. srcs=splitVector(comps[0])
  256. tgts=splitVector(comps[1])
  257. for cs in srcs:
  258. for ct in tgts:
  259. #get partition coefficient
  260. try:
  261. pcParName=self.mod['partitionCoefficients'][cs]
  262. except KeyError:
  263. pcParName="one"
  264. #get flow (direction could be reversed)
  265. try:
  266. qName=self.flows['{}:{}'.format(cs,ct)]
  267. except KeyError:
  268. qName=self.flows['{}:{}'.format(ct,cs)]
  269. flowParName=self.mod['flows'][qName]
  270. #flowPar=pars[flowParName]
  271. self.bind(cs,ct,flowParName,pcParName,1)
  272. self.build()
  273. def add_default_parameters(self):
  274. pars=self.parSetup['parameters']
  275. pars['one']={'value':1}
  276. pars['zero']={'value':0}
  277. def M(self,t):
  278. for i in self.dM:
  279. for j in self.dM[i]:
  280. self.fM[i,j]=self.dM[i][j](t)
  281. #create an array and fill it with outputs of function at t
  282. return self.fM
  283. def u(self,t):
  284. ub=[f(t) for f in self.fu]
  285. return numpy.array(ub)
  286. def jacobiFull(self,t):
  287. #update jacobi created during build phase with time dependent values
  288. for i in self.dM:
  289. for j in self.dM[i]:
  290. for k in range(system.m+1):
  291. self.fJ[k*system.n+i,k*system.n+j]=self.dM[i][j](t)
  292. return self.fJ
  293. def fSS(self,t):
  294. for k in self.qSS:
  295. for i in self.qSS[k]:
  296. for j in self.qSS[k][i]:
  297. #print('[{},{},{}] {}'.format(k,i,j,self.qSS[k][i][j]))
  298. self.SS[k,i,j]=(self.qSS[k][i][j])(t)
  299. return self.SS
  300. def fSY(self,y,t):
  301. #M number of sensitivity parameters
  302. #N number of equations
  303. #fSS is MxNxN
  304. #assume a tabulated solution y(t) at t spaced intervals
  305. qS=self.fSS(t).dot(y)
  306. #qS is MxN
  307. #but NxM is expected, so do a transpose
  308. #for simultaneous calculation, a Nx(M+1) matrix is expected
  309. tS=numpy.zeros((self.n,self.m+1))
  310. #columns from 2..M+1 are the partial derivatives
  311. tS[:,1:]=numpy.transpose(qS)
  312. #first column is the original function
  313. tS[:,0]=self.u(t)
  314. return tS
  315. def fS(self,t):
  316. #M number of sensitivity parameters
  317. #N number of equations
  318. #fSS is MxNxN
  319. #assume a tabulated solution y(t) at t spaced intervals
  320. qS=self.fSS(t).dot(self.getY(t))
  321. return numpy.transpose(qS)
  322. def getSEJ(self,parName):
  323. #find the sensitivity (SE) derivative of Jacobi with
  324. #respect to parameter
  325. try:
  326. return self.seJ[parName]
  327. except KeyError:
  328. self.seJ[parName]={}
  329. return self.seJ[parName]
  330. def getSEJ_comp(self,parName,compartmentName):
  331. #find equation dictating concentration in compartmentName
  332. #for jacobi-parameter derivative
  333. seJ=self.getSEJ(parName)
  334. try:
  335. return seJ[compartmentName]
  336. except KeyError:
  337. seJ[compartmentName]={}
  338. return seJ[compartmentName]
  339. def setY(self,t,y):
  340. self.tck=[None]*self.n
  341. for i in range(self.n):
  342. self.tck[i] = scipy.interpolate.splrep(t, y[:,i], s=0)
  343. def getY(self,t):
  344. fY=numpy.zeros(self.n)
  345. for i in range(self.n):
  346. fY[i]=scipy.interpolate.splev(t, self.tck[i], der=0)
  347. return fY
  348. def getWeight(self,parName):
  349. pars=self.parSetup['parameters']
  350. par=pars[parName]
  351. #self.get parses the units
  352. v=self.get(parName)["value"]
  353. if par['dist']=='lognormal':
  354. #this is sigma^2_lnx
  355. sln2=numpy.log(par["cv"]*par["cv"]+1)
  356. #have to multiplied by value to get the derivative
  357. #with respect to lnx
  358. return sln2*v*v
  359. else:
  360. #for Gaussian, cv is sigma/value; get sigma by value multiplication
  361. return par["cv"]*par["cv"]*v*v
  362. def getMax(lutSE):
  363. fm=-1
  364. for x in lutSE:
  365. if int(lutSE[x])>fm:
  366. fm=lutSE[x]
  367. return fm
  368. def getWeights(self,lutSE):
  369. #pars=self.parSetup['parameters']
  370. wts=numpy.zeros((model.getMax(lutSE)+1))
  371. for parName in lutSE:
  372. j=lutSE[parName]
  373. wts[j]=self.getWeight(parName)
  374. return wts
  375. def getDerivatives(self,se,i):
  376. #return latest point derivatives
  377. fse=se[-1][i]
  378. #fse is an m-vector
  379. return fse*fse
  380. def calculateUncertainty(self,se):
  381. s2out=numpy.zeros(se.shape[1:])
  382. se2=numpy.multiply(se,se)
  383. #w=self.getWeights(self.lutSE)
  384. w=numpy.ones((self.m))
  385. return numpy.sqrt(numpy.dot(se2,w))
  386. def get(self,parName):
  387. pars=self.parSetup['parameters']
  388. par=pars[parName]
  389. par['name']=parName
  390. if "value" in par:
  391. return self.getValue(par)
  392. if "function" in par:
  393. return self.getFunction(par)
  394. if "derived" in par:
  395. return self.getDerived(par)
  396. print('Paramter {} not found!'.format(parName))
  397. def getValue(self,par):
  398. v=par["value"]
  399. parName=par['name']
  400. #convert to seconds
  401. try:
  402. parUnits=par['unit'].split('/')
  403. except (KeyError,IndexError):
  404. #no unit given
  405. return valueObject(v,parName)
  406. timeUnit=self.getTimeUnit()
  407. try:
  408. if parUnits[1]==timeUnit:
  409. return valueObject(v,parName)
  410. except IndexError:
  411. #no / in unit name
  412. return valueObject(v,parName)
  413. if parUnits[1]=='min' and timeUnit=='s':
  414. return valueObject(v/60,parName)
  415. if parUnits[1]=='s' and timeUnit=='min':
  416. return valueObject(60*v,parName)
  417. if parUnits[1]=='day' and timeUnit=='min':
  418. return valueObject(v/24/60,parName)
  419. if parUnits[1]=='hour' and timeUnit=='min':
  420. return valueObject(v/60,parName)
  421. #no idea what to do
  422. return valueObject(v,parName)
  423. def getFunction(self,par):
  424. fName=par['function']
  425. #print('[{}]: getFunction({})'.format(par['name'],par['function']))
  426. df=self.parSetup['functions'][fName]
  427. skip=['type']
  428. par1={x:self.get(df[x]) for x in df if x not in skip}
  429. if df['type']=='linearGrowth':
  430. #print(par1)
  431. return function.linearGrowth(par1)
  432. if df['type']=='linearGrowthFixedSlope':
  433. return function.linearGrowthFixedSlope(par1)
  434. print('Function {} not found!'.format(fName))
  435. def getDerived(self,par):
  436. dName=par['derived']
  437. d=self.parSetup['derivedParameters'][dName]
  438. #print('Derived [{}]: type {}'.format(dName,d['type']))
  439. if d['type']=='product':
  440. #print('{}*{}'.format(d['a'],d['b']))
  441. pA=self.get(d['a'])
  442. a=pA['value']
  443. DA=pA['derivatives']
  444. pB=self.get(d['b'])
  445. b=pB['value']
  446. DB=pB['derivatives']
  447. #even more generic -> df/dp=[df/dA*dA/dp+df/dB*dB/dp]
  448. if any(['function' in pA,'function' in pB]):
  449. fa=function.to(a)
  450. fb=function.to(b)
  451. f=lambda t,a=fa,b=fb:a(t)*b(t)
  452. dfdA=lambda t,b=fb: b(t)
  453. dfdB=lambda t,a=fa: a(t)
  454. dA=function.generate(dfdA,DA)
  455. dB=function.generate(dfdB,DB)
  456. return function.Object(f,[dA,dB])
  457. else:
  458. return derivedObject(a*b,[{'df':b,'D':DA},{'df':a,'D':DB}])
  459. if d['type']=='power':
  460. #print('{}^{}'.format(d['a'],d['n']))
  461. pA=self.get(d['a'])
  462. a=pA['value']
  463. DA=pA['derivatives']
  464. pN=self.get(d['n'])
  465. n=pN['value']
  466. DN=pN['derivatives']
  467. if any(['function' in pA,'function' in pN]):
  468. fa=function.to(a)
  469. fn=function.to(n)
  470. f=lambda t,a=fa,n=fn:numpy.power(a(t),n(t))
  471. dfdA=lambda t,n=fn,f=f,a=fa:n(t)*f(t)/a(t)
  472. dfdN=lambda t,a=fa,f=f:numpy.log(a(t))*f(t)
  473. dA=function.generate(dfdA,DA)
  474. dN=function.generate(dfdN,DN)
  475. return function.Object(f,[dA,dN])
  476. else:
  477. f=numpy.power(a,n)
  478. return derivedObject(f,[{'df':n*f/a,'D':DA},{'df':f*numpy.log(a),'D':DN}])
  479. if d['type']=='ratio':
  480. #print('{}/{}'.format(d['a'],d['b']))
  481. pA=self.get(d['a'])
  482. a=pA['value']
  483. DA=pA['derivatives']
  484. pB=self.get(d['b'])
  485. b=pB['value']
  486. DB=pB['derivatives']
  487. #even more generic -> df/dp=[df/dA*dA/dp+df/dB*dB/dp]
  488. if any(['function' in pA,'function' in pB]):
  489. fa=function.to(a)
  490. fb=function.to(b)
  491. f=lambda t,a=fa,b=fb,:a(t)/b(t)
  492. dfdA=lambda t,f=f,a=fa: f(t)/a(t)
  493. dfdB=lambda t,f=f,b=fb: -f(t)/b(t)
  494. dA=function.generate(dfdA,DA)
  495. dB=function.generate(dfdB,DB)
  496. return function.Object(f,[dA,dB])
  497. else:
  498. return derivedObject(a/b,[{'df':1/b,'D':DA},{'df':-a/b/b,'D':DB}])
  499. if d['type']=='sum':
  500. #print('{}+{}'.format(d['a'],d['b']))
  501. pA=self.get(d['a'])
  502. a=pA['value']
  503. DA=pA['derivatives']
  504. pB=self.get(d['b'])
  505. b=pB['value']
  506. DB=pB['derivatives']
  507. #even more generic -> df/dp=[df/dA*dA/dp+df/dB*dB/dp]
  508. if any(['function' in pA,'function' in pB]):
  509. fa=function.to(a)
  510. fb=function.to(b)
  511. f=lambda t,a=fa,b=fb,:a(t)+b(t)
  512. dfdA=lambda t: 1
  513. dfdB=lambda t: 1
  514. dA=function.generate(dfdA,DA)
  515. dB=function.generate(dfdB,DB)
  516. return function.Object(f,[dA,dB])
  517. else:
  518. return derivedObject(a+b,[{'df':1,'D':DA},{'df':1,'D':DB}])
  519. def calculateDerivative(par):
  520. #add derivatives if dist(short for distribution) is specified
  521. return "dist" in par
  522. def sumValues(dArray,x):
  523. s=0
  524. for a in dArray:
  525. try:
  526. s+=a[x]
  527. except KeyError:
  528. continue
  529. return s
  530. def valueObject(v,parName):
  531. #convert everything to functions
  532. d0={parName:1}
  533. return {'value':v,'derivatives':{parName:1}}
  534. def derivedObject(f,ders):
  535. o={'value':f}
  536. DD=[]
  537. for d in ders:
  538. df=d['df']
  539. D=d['D']
  540. DD.append({x:df*D[x] for x in D})
  541. allKeys=[]
  542. for x in DD:
  543. allKeys.extend(x.keys())
  544. allKeys=list(set(allKeys))
  545. o['derivatives']={x:sumValues(DD,x) for x in allKeys}
  546. return o
  547. def splitVector(v):
  548. if v.find('(')<0:
  549. return [v]
  550. return v[1:-1].split(',')
  551. def parseFunction(formula):
  552. if formula['name']=='exponential':
  553. c0=formula['constant']
  554. k=formula['k']
  555. return lambda t,c=c0,k=k:c*numpy.exp(k*t)
  556. if formula['name']=='constant':
  557. c0=formula['value']
  558. return lambda t,c0=c0:c0
  559. if formula['name']=='Heavyside':
  560. t0=formula['limit']
  561. v=formula['value']
  562. return lambda t,v=v,t0=t0:v if t<t0 else 0
  563. return lambda t:1
  564. def addValue(qdict,compName,v):
  565. #add function to compName of dictionary qdict,
  566. #check if compName exists and handle the potential error
  567. #lambda functions can't be summed directly, so qdict is a list
  568. #that will be merged at matrix generation time
  569. try:
  570. qdict[compName].append(v)
  571. except KeyError:
  572. qdict[compName]=[v]
  573. #also add derivatives
  574. #
  575. # for d in dTarget:
  576. # ctarget=self.getSEJ_comp(d,target)
  577. # addValue(ctarget,target,dTarget[d])
  578. def get(timeUnit,par):
  579. v=par["value"]
  580. #convert to seconds
  581. try:
  582. parUnits=par['unit'].split('/')
  583. except (KeyError,IndexError):
  584. #no unit given
  585. return v
  586. try:
  587. if parUnits[1]==timeUnit:
  588. return v
  589. except IndexError:
  590. #no / in unit name
  591. return v
  592. if parUnits[1]=='min' and timeUnit=='s':
  593. return v/60
  594. if parUnits[1]=='s' and timeUnit=='min':
  595. return 60*v
  596. #no idea what to do
  597. return v