cModel.py 22 KB

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