ivp.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import numpy
  2. import scipy.integrate
  3. #scipy solve_ivp interface using system of n equations
  4. #infrastructure for sensitivity analysis of y with respect
  5. #to model parameters p also integrated
  6. #
  7. #General form of equation:
  8. # dy/dt=M*y+u
  9. #where y is an n-vector and M is nxn Jacobi matrix
  10. #
  11. #implementation of system has to provide attributes:
  12. # - n, number of variables
  13. # - m, number of parameters
  14. # - M(t), nxn, a time dependent Jacobi matrix,
  15. # - u(t), 1xn, a time dependent source term
  16. # - fS(t) mxn, a time dependent array of derivatives
  17. # fS(t)[p,:] = d(M*y)/dp (t)
  18. # where y is internal interpolated y-only ivp solution
  19. # - fSY(t,y), mxn, a time dependent array of derivatives:
  20. # fS(t)[p,:] = d(M*y)/dp (t)
  21. # where current y is supplied at each step
  22. def dfdy(t,y,system):
  23. dfdy=system.M(t).dot(y)+system.u(t)
  24. return dfdy
  25. def jacobi(t,y,system):
  26. return system.M(t)
  27. #SE post calculation
  28. def dfdyS(t,S,system):
  29. #unwrap S to NxM where M is number of parameters
  30. mS=numpy.reshape(S,(system.n,system.m))
  31. mOut=system.M(t).dot(mS)+system.fS(t)
  32. return numpy.ravel(mOut)
  33. def jacobiSE(t,S,system):
  34. N=system.n*(system.m)
  35. fJ=numpy.zeros((N,N))
  36. #print('fJ shape {}'.format(fJ.shape))
  37. for i in range(system.m):
  38. fJ[i*system.n:(i+1)*system.n,i*system.n:(i+1)*system.n]=system.M(t)
  39. return fJ
  40. #SE simultaeneous calculation
  41. def dfdySFull(t,S,system):
  42. #unwrap S to NxM where M is number of parameters
  43. mS=numpy.reshape(S,(system.n,system.m+1))
  44. #system.fS(y,t) is NxM matrix where M are parameters
  45. y=mS[:,0]
  46. mOut=system.M(t).dot(mS)+system.fSY(y,t)
  47. try:
  48. system.iPrint+=1
  49. except AttributeError:
  50. system.iPrint=0
  51. if system.iPrint>1000:
  52. print('At t={:.2f}'.format(t),flush=True)
  53. system.iPrint=0
  54. return numpy.ravel(mOut)
  55. def jacobiSEFull(t,S,system):
  56. N=system.n*(system.m+1)
  57. fJ=numpy.zeros((N,N))
  58. #print('fJ shape {}'.format(fJ.shape))
  59. for i in range(system.m+1):
  60. fJ[i*system.n:(i+1)*system.n,i*system.n:(i+1)*system.n]=system.M(t)
  61. return fJ
  62. def solveSimultaneous(model,tmax,atol,rtol,method='LSODA',nt=201,\
  63. t0=0,y0=numpy.array([]), sIn=numpy.array([])):
  64. t_eval=numpy.linspace(t0,tmax,nt)
  65. s0=numpy.zeros((model.n,model.m+1))
  66. if sIn.size!=0:
  67. s0[:,1:]=sIn
  68. if y0.size==0:
  69. y0=numpy.zeros(model.n)
  70. #set initial condition
  71. s0[:,0]=y0
  72. s0=s0.ravel()
  73. sol=scipy.integrate.solve_ivp(dfdySFull,[t0, tmax],s0, args=(model,),\
  74. jac=jacobiSEFull, method=method, \
  75. atol=atol, rtol=rtol, t_eval=t_eval)
  76. t=sol.t
  77. sFull=numpy.reshape(numpy.transpose(sol.y),(len(t),model.n,model.m+1))
  78. s1=sFull[:,:,1:]
  79. ysol=sFull[:,:,0]
  80. se=model.calculateUncertainty(s1)
  81. print('Done simultaneous LSODA SE')
  82. return t,ysol,se,s1
  83. def solveSequential(model,tmax,atol,rtol,method='LSODA',nt=201,\
  84. t0=0,y0=numpy.array([]), sIn=numpy.array([])):
  85. t_eval=numpy.linspace(t0,tmax,nt)
  86. if y0.size==0:
  87. y0=numpy.zeros(model.n)
  88. solIVP=scipy.integrate.solve_ivp(dfdy,[t0, tmax],y0, args=(model,), jac=jacobi,
  89. method=method, atol=atol, rtol=rtol, t_eval=t_eval)
  90. #y is n x nt (odeint nt x n)
  91. sol=numpy.transpose(solIVP.y)
  92. t=solIVP.t
  93. print('shape (y) {}'.format(sol.shape))
  94. model.setY(t,sol)
  95. s0=numpy.zeros((model.n,model.m))
  96. if sIn.size==0:
  97. s0=sIn
  98. s0=s0.ravel()
  99. solIVPSE=scipy.integrate.solve_ivp(dfdyS,[0, tmax],s0, args=(model,), jac=jacobiSE,
  100. method=method, atol=atol, rtol=rtol,t_eval=t_eval)
  101. sraw=numpy.reshape(numpy.transpose(solIVPSE.y),(len(solIVPSE.t),model.n,model.m))
  102. #interpolate on t
  103. s1=numpy.zeros((len(t),model.n,model.m))
  104. for i in range(model.n):
  105. for j in range(model.m):
  106. tck = scipy.interpolate.splrep(solIVPSE.t, sraw[:,i,j], s=0)
  107. s1[:,i,j]=scipy.interpolate.splev(t, tck, der=0)
  108. se=model.calculateUncertainty(s1)
  109. return t,sol,se,s1
  110. def solveSimultaneousOdeint(model,tmax,nt=201,t0=0,y0=numpy.array([]), sIn=numpy.array([])):
  111. t = numpy.linspace(t0,tmax, nt)
  112. if y0.size==0:
  113. y0=numpy.zeros(model.n)
  114. s0=numpy.zeros((model.n,model.m+1))
  115. if sIn.size!=0:
  116. s0[:,1:]=sIn
  117. #set initial condition
  118. s0[:,0]=y0
  119. s0=s0.ravel()
  120. solSE1=scipy.integrate.odeint(dfdySFull, s0, t, args=(model,),Dfun=jacobiSEFull,tfirst=True)
  121. sFull=numpy.reshape(solSE1,(len(t),model.n,model.m+1))
  122. s1=sFull[:,:,1:]
  123. sol=sFull[:,:,0]
  124. se=sys.calculateUncertainty(s1)
  125. print('Done simultaneous SE')
  126. return t,sol,se,s1
  127. def solveSequentialOdeint(model,tmax,nt=201,t0=0,y0=numpy.array([]),sIn=numpy.array([])):
  128. t = numpy.linspace(t0,tmax, nt)
  129. if y0.size==0:
  130. y0=numpy.zeros(sys.n)
  131. sol = scipy.integrate.odeint(dfdy, y0=y0, t=t, args=(model,),Dfun=jacobi,tfirst=True)
  132. print('shape (y) {}'.format(sol.shape))
  133. model.setY(t,sol)
  134. s0=numpy.zeros((model.n,model.m))
  135. if sIn.size==0:
  136. s0=sIn
  137. s0=s0.ravel()
  138. solSE=scipy.integrate.odeint(dfdyS, s0, t, args=(model,),Dfun=jacobiSE,tfirst=True)
  139. s1=numpy.reshape(solSE,(len(t),model.n,model.m))
  140. se=model.calculateUncertainty(s1)
  141. print('Done sequential SE')
  142. return t,sol,se,s1