def simulate(self, Tend, nIntervals, gridWidth): problem = Explicit_Problem(self.rhs, self.y0) problem.name = 'CVode' # solver.rhs = self.right_hand_side problem.handle_result = self.handle_result problem.state_events = self.state_events problem.handle_event = self.handle_event problem.time_events = self.time_events problem.finalize = self.finalize simulation = CVode(problem) # Change multistep method: 'adams' or 'VDF' if self.discr == 'Adams': simulation.discr = 'Adams' simulation.maxord = 12 else: simulation.discr = 'BDF' simulation.maxord = 5 # Change iteration algorithm: functional(FixedPoint) or newton if self.iter == 'FixedPoint': simulation.iter = 'FixedPoint' else: simulation.iter = 'Newton' # Sets additional parameters simulation.atol = self.atol simulation.rtol = self.rtol simulation.verbosity = self.verbosity if hasattr(simulation, 'continuous_output'): simulation.continuous_output = False # default 0, if one step approach should be used elif hasattr(simulation, 'report_continuously'): simulation.report_continuously = False # default 0, if one step approach should be used # '''Initialize problem ''' # self.t_cur = self.t0 # self.y_cur = self.y0 # Calculate nOutputIntervals: if gridWidth <> None: nOutputIntervals = int((Tend - self.t0) / gridWidth) else: nOutputIntervals = nIntervals # Check for feasible input parameters if nOutputIntervals == 0: print 'Error: gridWidth too high or nIntervals set to 0! Continue with nIntervals=1' nOutputIntervals = 1 # Perform simulation simulation.simulate(Tend, nOutputIntervals) # to get the values: t_new, y_new = simulation.simulate
def x_cvode(self,params): from assimulo.problem import Explicit_Problem from assimulo.solvers import CVode func = self.create_dx(self.create_input_vector(params)) problem = Explicit_Problem(lambda t,x:func(t,x)['f'](), [1.0,1.0,1.0],0) sim = CVode(problem) t,x = sim.simulate(250,len(self.time)-1) dataframe = pandas.DataFrame(x, columns=['population','burden','economy'],index=self.time) return dataframe
def s_cvode(self,params): from assimulo.problem import Explicit_Problem from assimulo.solvers import CVode func = self.create_ds(self.create_input_vector(params)) s0 = np.ones(21) s0[3:]=0 problem = Explicit_Problem(lambda t,s:func(t,s),s0,1900) sim = CVode(problem) t,s = sim.simulate(1900+250,self.time.shape[0]-1) dataframe = pandas.DataFrame( s,columns=self.cols+self.sensitivities,index=self.time) return dataframe
def s_cvode_natural(self,params): from assimulo.problem import Explicit_Problem from assimulo.solvers import CVode problem = Explicit_Problem(lambda t,x,p:self.create_dx(p)(t,x)['f'](), [1.0,1.0,1.0],0, [params[p] for p in self.params]) sim = CVode(problem) sim.report_continuously = True t,x = sim.simulate(250,self.time.shape[0]-1) dataframe = pandas.DataFrame(x, columns=['population','burden','economy']) d = {} sens = np.array(sim.p_sol) for i,col in enumerate(self.cols): for j,param in enumerate( ('birthrate','deathrate','regenerationrate', 'burdenrate','economyaim','growthrate')): d['{0},{1}'.format(col,param)] = sens[j,:,i] dataframe_sens = pandas.DataFrame(d,index=self.time) return dataframe_sens
def mySolve(xf,boltz_eqs,rtol,atol,verbosity=50): """Sets the main options for the ODE solver and solve the equations. Returns the array of x,y points for all components. If numerical instabilities are found, re-do the problematic part of the evolution with smaller steps""" boltz_solver = CVode(boltz_eqs) #Define solver method boltz_solver.rtol = rtol boltz_solver.atol = atol boltz_solver.verbosity = verbosity boltz_solver.linear_solver = 'SPGMR' boltz_solver.maxh = xf/300. xfinal = xf xres = [] yres = [] sw = boltz_solver.sw[:] while xfinal <= xf: try: boltz_solver.re_init(boltz_eqs.t0,boltz_eqs.y0) boltz_solver.sw = sw[:] x,y = boltz_solver.simulate(xfinal) xres += x for ypt in y: yres.append(ypt) if xfinal == xf: break #Evolution has been performed until xf -> exit except Exception,e: print e if not e.t or 'first call' in e.msg[e.value]: logger.error("Error solving equations:\n "+str(e)) return False xfinal = max(e.t*random.uniform(0.85,0.95),boltz_eqs.t0+boltz_solver.maxh) #Try again, but now only until the error logger.warning("Numerical instability found. Restarting evolution from x = " +str(boltz_eqs.t0)+" to x = "+str(xfinal)) continue xfinal = xf #In the next step try to evolve from xfinal -> xf sw = boltz_solver.sw[:] x0 = float(x[-1]) y0 = [float(yval) for yval in y[-1]] boltz_eqs.updateValues(x0,y0,sw)
def run_example(with_plots=True): """ Example of the use of CVode for a differential equation with a iscontinuity (state event) and the need for an event iteration. on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ #Create an instance of the problem exp_mod = Extended_Problem() #Create the problem exp_sim = CVode(exp_mod) #Create the solver exp_sim.verbosity = 0 exp_sim.report_continuously = True #Simulate t, y = exp_sim.simulate(10.0,1000) #Simulate 10 seconds with 1000 communications points exp_sim.print_event_data() #Basic test nose.tools.assert_almost_equal(y[-1][0],8.0) nose.tools.assert_almost_equal(y[-1][1],3.0) nose.tools.assert_almost_equal(y[-1][2],2.0) #Plot if with_plots: P.plot(t,y) P.title(exp_mod.name) P.ylabel('States') P.xlabel('Time') P.show() return exp_mod, exp_sim
# Define the rhs def rhs(t, y): k = 10 A = np.array([[0, 0, 1, 0], [0, 0, 0, 1], [-L(y, k), 0, 0, 0], [0, -L(y, k), 0, 0]]) b = np.array([0, 0, 0, -1]) yd = np.dot(A, y) + b return yd def L(y, k): norm = ln.norm(y[0:2]) return k * (norm - 1) / norm pend_mod = Explicit_Problem(rhs, y0 = np.array([1.0, 1.0, 1.0, 1.0])) pend_mod.name = 'Nonlinear Pendulum' # Define an explicit solver exp_sim = BDF_3(pend_mod) # Create a BDF solver t, y = exp_sim.simulate(4) exp_sim.plot() mpl.show() toltmp=0.1 cvod= CVode(pend_mod) cvod.atol=toltmp cvod.rtol=toltmp cvod.maxord=19 cvod.simulate(5) cvod.plot()
#Create an Assimulo Problem mod = Explicit_Problem(pendulum, y0, t0, sw0=switches0) mod.state_events = state_events #Sets the state events to the problem mod.handle_event = handle_event #Sets the event handling to the problem mod.name = 'Pendulum with events' #Sets the name of the problem #Create an Assimulo solver (CVode) sim = CVode(mod) #Specifies options sim.discr = 'Adams' #Sets the discretization method sim.iter = 'FixedPoint' #Sets the iteration method sim.rtol = 1.e-8 #Sets the relative tolerance sim.atol = 1.e-6 #Sets the absolute tolerance #Simulation ncp = 200 #Number of communication points tfinal = 10.0 #Final time t, y = sim.simulate(tfinal, ncp) #Simulate #Plots the result P.plot(t,y) P.show()
def ode_gen(t, y, num_speci, num_eqn, rindx, pindx, rstoi, pstoi, H2Oi, TEMP, RO2_indices, num_sb, Psat, mfp, accom_coeff, surfT, y_dens, N_perbin, DStar_org, y_mw, x, core_diss, Varr, Vbou, RH, rad0, Vol0, end_sim_time, pconc, save_step, rbou, therm_sp, Cw, light_time, light_stat, nreac, nprod, prodn, reacn, new_partr, MV, nucv1, nucv2, nucv3, inflectDp, pwl_xpre, pwl_xpro, inflectk, nuc_comp, ChamR, Rader, PInit, testf, kwgt): # ---------------------------------------------------------- # inputs # t - suggested time step length (s) # num_speci - number of components # num_eqn - number of equations # Psat - saturation vapour pressures (molecules/cm3 (air)) # y_dens - components' densities (g/cc) # y_mw - components' molecular weights (g/mol) # x - radii of particle size bins (um) (excluding walls) # therm_sp - thermal speed of components (m/s) # DStar_org - gas-phase diffusion coefficient of components (m2/s) # Cw - concentration of wall (molecules/cm3 (air)) # light_time - times (s) of when lights on and lights off (corresponding to light # status in light_stat) # light_stat - order of lights on (1) and lights off (0) # chamA - chamber area (m2) # nreac - number of reactants per equation # nprod - number of products per equation # pindx - indices of equation products (cols) in each equation (rows) # prodn - pindx no. of columns # reacn - rindx no. of columns # rindx - index of reactants per equation # rstoi - stoichometry of reactants per equation # pstoi - stoichometry of products # pconc - concentration of seed particles (#/cc (air)) (1) # new_partr - radius of two ELVOC molecules together in a newly nucleating # particle (cm) # MV - molar volume (cc/mol) (1D array) # nuc_comp - index of the nucleating component # ChamR - spherical equivalent radius of chamber (below eq. 2 Charan (2018)) (m) # Rader - flag of whether or not to use Rader and McMurry approach # PInit - pressure inside chamber (Pa) # testf - flag to say whether in normal mode (0) or testing mode (1) # kgwt - mass transfer coefficient for vapour-wall partitioning (cm3/molecule.s) # ---------------------------------------------------------- if testf == 1: return (0, 0, 0, 0) # return dummies R_gas = si.R # ideal gas constant (kg.m2.s-2.K-1.mol-1) NA = si.Avogadro # Avogadro's number (molecules/mol) step = 0 # ode time interval step number t0 = t # remember original suggested time step (s) # final +1 for ELVOC in newly nucleating particles y0 = np.zeros((num_speci + num_sb * num_speci)) y0[:] = y[:] # initial concentrations (molecules/cc (air)) y00 = np.zeros((num_speci + num_sb * num_speci)) y00[:] = y[:] # initial concentrations (molecules/cc (air)) # initial volumes of particles in size bins at start of time steps if num_sb > 1: Vstart = np.zeros((num_sb - 1)) Vstart[:] = Vol0[:] * N_perbin else: Vstart = 0.0 sumt = 0.0 # total time integrated over (s) # record initial values if num_sb > 0: # particle-phase concentrations (molecules/cc (air)) yp = np.transpose(y[num_speci:-(num_speci)].reshape( num_sb - 1, num_speci)) else: yp = 0.0 [t_out, y_mat, Nresult, x2] = recording(y, N_perbin, x, step, sumt, 0, 0, 0, 0, int(end_sim_time / save_step), num_speci, num_sb, y_mw[:, 0], y_dens[:, 0] * 1.0e-3, yp, Vbou) tnew = 0.46875 # relatively small time step (s) for first bit # number concentration of nucleated particles formed (# particles/cc (air)) new_part_sum1 = 0.0 save_count = int(1) # count on number of times saving code called # count in number of time steps since time interval was last reduced tinc_count = 10 from rate_valu_calc import rate_valu_calc # function to update rate coefficients print('starting ode solver') while sumt < end_sim_time: # step through time intervals to do ode # increase time step if time step not been decreased for 10 steps if tinc_count == 0 and tnew < t0: tnew = tnew * 2.0 # check whether lights on or off timediff = sumt - np.array(light_time) timedish = (timediff == np.min(timediff[timediff >= 0]) ) # reference time index lightm = ( np.array(light_stat))[timedish] # whether lights on or off now # update reaction rate coefficients reac_coef = rate_valu_calc(RO2_indices, y[H2Oi], TEMP, lightm, y) y0[:] = y[:] # update initial concentrations (molecules/cc (air)) # update particle volumes at start of time step (um3) Vstart = Varr * N_perbin redt = 1 # reset time reduction flag t = tnew # reset integration time (s) if num_sb > 1: # update partitioning coefficients [kimt, kelv_fac ] = kimt_calc(y, mfp, num_sb, num_speci, accom_coeff, y_mw, surfT, R_gas, TEMP, NA, y_dens, N_perbin, DStar_org, x.reshape(1, -1) * 1.0e-6, Psat, therm_sp, H2Oi) # ensure no confusion that components are present due to low value fillers for # concentrations (molecules/cc (air)) y0[y0 == 1.0e-40] = 0.0 print() # enter a while loop that continues to decrease the time step until particle # size bins don't change by more than one size bin (given by moving centre) while redt == 1: print('cumulative time', sumt) # numba compiler to convert to machine code @jit(f8[:](f8, f8[:]), nopython=True) # ode solver ------------------------------------------------------------- def dydt(t, y): # empty array to hold rate of change (molecules/cc(air).s) dydt = np.zeros((len(y))) # gas-phase rate of change ------------------------------------ for i in range(num_eqn): # equation loop # gas-phase rate of change (molecules/cc (air).s) gprate = ((y[rindx[i, 0:nreac[i]]]** rstoi[i, 0:nreac[i]]).prod()) * reac_coef[i] dydt[rindx[i, 0:nreac[i]]] -= gprate # loss of reactants dydt[pindx[i, 0:nprod[i]]] += gprate # gain of products if num_sb > 1: # ----------------------------------------------------------- for ibin in range(num_sb - 1): # size bin loop Csit = y[num_speci * (ibin + 1):num_speci * (ibin + 2)] # sum of molecular concentrations per bin (molecules/cc (air)) conc_sum = np.zeros((1)) if pconc > 0.0: # if seed particles present conc_sum[0] = ((Csit[0:-1].sum()) + Csit[-1] * core_diss) else: conc_sum[0] = Csit.sum() # prevent numerical error due to division by zero ish = conc_sum == 0.0 conc_sum[ish] = 1.0e-40 # particle surface gas-phase concentration (molecules/cc (air)) Csit = (Csit / conc_sum) * Psat[:, 0] * kelv_fac[ibin] # partitioning rate (molecules/cc.s) dydt_all = kimt[:, ibin] * (y[0:num_speci] - Csit) # gas-phase change dydt[0:num_speci] -= dydt_all # particle-phase change dydt[num_speci * (ibin + 1):num_speci * (ibin + 2)] += dydt_all # ----------------------------------------------------------- # gas-wall partitioning eq. 14 of Zhang et al. # (2015) (https://www.atmos-chem-phys.net/15/4197/2015/ # acp-15-4197-2015.pdf) (molecules/cc.s (air)) # concentration at wall (molecules/cc (air)) Csit = y[num_speci * num_sb:num_speci * (num_sb + 1)] Csit = (Psat[:, 0] * (Csit / Cw)) # eq. 14 of Zhang et al. (2015) # (https://www.atmos-chem-phys.net/15/4197/2015/ # acp-15-4197-2015.pdf) (molecules/cc.s (air)) dydt_all = (kwgt * Cw) * (y[0:num_speci] - Csit) # gas-phase change dydt[0:num_speci] -= dydt_all # wall concentration change dydt[num_speci * num_sb:num_speci * (num_sb + 1)] += dydt_all return (dydt) mod = Explicit_Problem(dydt, y0) mod_sim = CVode(mod) # define a solver instance mod_sim.atol = 1.0e-3 # absolute tolerance mod_sim.rtol = 1.0e-3 # relative tolerance mod_sim.discr = 'BDF' # the integration approach, default is 'Adams' t_array, res = mod_sim.simulate(t) y = res[-1, :] # low value filler for concentrations (molecules/cc (air)) to prevent # numerical errors y0[y0 == 0.0] = 1.0e-40 y[y == 0.0] = 1.0e-40 if num_sb > 1 and (N_perbin > 1.0e-10).sum() > 0: # call on the moving centre method for rebinning particles (N_perbin, Varr, y[num_speci::], x, redt, t, tnew) = movcen( N_perbin, Vbou, np.transpose(y[num_speci::].reshape(num_sb, num_speci)), (np.squeeze(y_dens * 1.0e-3)), num_sb, num_speci, y_mw, x, Vol0, t, t0, tinc_count, y0[num_speci::], MV) else: redt = 0 # if time step needs reducing then reset gas-phase concentrations to their # values preceding the ode, this will already have been done inside moving # centre module for particle-phase if redt == 1: y[0:num_speci] = y0[0:num_speci] # start counter to determine when to next try increasing time interval if redt == 1: tinc_count = 10 if redt == 0 and tinc_count > -1: tinc_count -= 1 if tinc_count == -1: tinc_count = 10 sumt += t # total time covered (s) step += 1 # ode time interval step number if num_sb > 1: if (N_perbin > 1.0e-10).sum() > 0: # coagulation # y indices due to final element in y being number of ELVOC molecules # contributing to newly nucleated particles [N_perbin, y[num_speci:-(num_speci)], x, Gi, eta_ai, Varr] = coag( RH, TEMP, x * 1.0e-6, (Varr * 1.0e-18).reshape(1, -1), y_mw.reshape(-1, 1), x * 1.0e-6, np.transpose(y[num_speci::].reshape(num_sb, num_speci)), (N_perbin).reshape(1, -1), t, (Vbou * 1.0e-18).reshape(1, -1), num_speci, 0, (np.squeeze(y_dens * 1.0e-3)), rad0, PInit) # particle loss to walls [N_perbin, y[num_speci:-(num_speci)] ] = wallloss(N_perbin.reshape(-1, 1), y[num_speci:-(num_speci)], Gi, eta_ai, x * 2.0e-6, y_mw, Varr * 1.0e-18, num_sb, num_speci, TEMP, t, inflectDp, pwl_xpre, pwl_xpro, inflectk, ChamR, Rader) # particle nucleation if sumt < 3600.0 and pconc == 0.0: [N_perbin, y, x[0], Varr[0], new_part_sum1] = nuc(sumt, new_part_sum1, N_perbin, y, y_mw.reshape(-1, 1), np.squeeze(y_dens * 1.0e-3), num_speci, x[0], new_partr, t, MV, nucv1, nucv2, nucv3, nuc_comp) if sumt >= save_step * save_count: # save at every time step given by save_step (s) if num_sb > 0: # particle-phase concentrations (molecules/cc (air)) yp = np.transpose(y[num_speci:-(num_speci)].reshape( num_sb - 1, num_speci)) else: yp = 0.0 # record new values [t_out, y_mat, Nresult, x2] = recording(y, N_perbin, x, save_count, sumt, y_mat, Nresult, x2, t_out, int(end_sim_time / save_step), num_speci, num_sb, y_mw[:, 0], y_dens[:, 0] * 1.0e-3, yp, Vbou) save_count += int(1) return (t_out, y_mat, Nresult, x2)
def run_example(with_plots=True): """ Simulations for the Gyro (Heavy Top) example in Celledoni/Safstrom: Journal of Physics A, Vol 39, 5463-5478, 2006 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ def curl(v): return array([[0, v[2], -v[1]], [-v[2], 0, v[0]], [v[1], -v[0], 0]]) #Defines the rhs def f(t, u): """ Simulations for the Gyro (Heavy Top) example in Celledoni/Safstrom: Journal of Physics A, Vol 39, 5463-5478, 2006 """ I1 = 1000. I2 = 5000. I3 = 6000. u0 = [0, 0, 1.] pi = u[0:3] Q = (u[3:12]).reshape((3, 3)) Qu0 = dot(Q, u0) f = array([Qu0[1], -Qu0[0], 0.]) f = 0 omega = array([pi[0] / I1, pi[1] / I2, pi[2] / I3]) pid = dot(curl(omega), pi) + f Qd = dot(curl(omega), Q) return hstack([pid, Qd.reshape((9, ))]) def energi(state): energi = [] for st in state: Q = (st[3:12]).reshape((3, 3)) pi = st[0:3] u0 = [0, 0, 1.] Qu0 = dot(Q, u0) V = Qu0[2] # potential energy T = 0.5 * (pi[0]**2 / 1000. + pi[1]**2 / 5000. + pi[2]**2 / 6000.) energi.append([T]) return energi #Initial conditions y0 = hstack([[1000. * 10, 5000. * 10, 6000 * 10], eye(3).reshape((9, ))]) #Create an Assimulo explicit problem exp_mod = Explicit_Problem(f, y0, name="Gyroscope Example") #Create an Assimulo explicit solver (CVode) exp_sim = CVode(exp_mod) #Sets the parameters exp_sim.discr = 'BDF' exp_sim.iter = 'Newton' exp_sim.maxord = 2 #Sets the maxorder exp_sim.atol = 1.e-10 exp_sim.rtol = 1.e-10 #Simulate t, y = exp_sim.simulate(0.1) #Plot if with_plots: import pylab as P P.plot(t, y / 10000.) P.xlabel('Time') P.ylabel('States, scaled by $10^4$') P.title(exp_mod.name) P.show() #Basic tests nose.tools.assert_almost_equal(y[-1][0], 692.800241862) nose.tools.assert_almost_equal(y[-1][8], 7.08468221e-1) return exp_mod, exp_sim
def run_sim(Y, time, Y_AER1, YICE): def dy_dt_func(t, Y): dy_dt = np.zeros(len(Y)) # add condensed semi-vol mass into bins if n.SV_flag: MBIN2[-1 * n.n_sv:, :] = np.reshape(Y[INDSV1:INDSV2], [n.n_sv, nbins * nmodes]) # calculate saturation vapour pressure over liquid svp1 = f.svp_liq(Y[ITEMP]) # saturation ratio SL = svp1 * Y[IRH] / (Y[IPRESS] - svp1) SL = (SL * Y[IPRESS] / (1 + SL)) / svp1 # water vapour mixing ratio WV = c.eps * Y[IRH] * svp1 / (Y[IPRESS] - svp1) WL = np.sum(Y[IND1:IND2] * Y[:IND1]) # LIQUID MIXING RATIO WI = np.sum(YICE[IND1:IND2] * YICE[:IND1]) # ice mixing ratio RM = c.RA + WV * c.RV CPM = c.CP + WV * c.CPV + WL * c.CPW + WI * c.CPI if simulation_type.lower() == 'chamber': # CHAMBER MODEL - pressure change dy_dt[IPRESS] = -100 * PRESS1 * PRESS2 * np.exp(-PRESS2 * (time + t)) elif simulation_type.lower() == 'parcel': # adiabatic parcel dy_dt[IPRESS] = -Y[IPRESS] / RM / Y[ ITEMP] * c.g * w #! HYDROSTATIC EQUATION else: print('simulation type unknown') return # ----------------------------change in vapour content: ----------------------- # 1. equilibruim size of particles if n.kappa_flag: #if n.SV_flag: # need to recalc kappa taking into acount the condensed semi-vols Kappa = np.sum( (MBIN2[:, :] / RHOBIN2[:, :]) * KAPPABIN2[:, :], axis=0) / np.sum(MBIN2[:, :] / RHOBIN2[:, :], axis=0) # print(Kappa) # print(MBIN2/RHOBIN2) KK01 = f.kk01(Y[0:IND1], Y[ITEMP], MBIN2, RHOBIN2, Kappa) else: KK01 = f.K01(Y[0:IND1], Y[ITEMP], MBIN2, n.n_sv, RHOBIN2, NUBIN2, MOLWBIN2) # print(KK01[0]) Dw = KK01[2] # wet diameter RHOAT = KK01[1] # density of particles inc water and aerosol mass RH_EQ = KK01[0] # equilibrium RH # print(MBIN2/MOLWBIN2) # 2. growth rate of particles, Jacobson p455 # rate of change of radius growth_rate = f.DROPGROWTHRATE(Y[ITEMP], Y[IPRESS], SL, RH_EQ, RHOAT, Dw) growth_rate[np.isnan(growth_rate)] = 0 # get rid of nans growth_rate = np.where(Y[IND1:IND2] < 1e-9, 0.0, growth_rate) # 3. Mass of water condensing # change in mass of water per particle dy_dt[:IND1] = (np.pi * RHOAT * Dw**2) * growth_rate # 4. Change in vapour content # change in water vapour mixing ratio dwv_dt = -1 * np.sum( Y[IND1:IND2] * dy_dt[:IND1]) # change to np.sum for speed # mass # ----------------------------------------------------------------------------- if simulation_type.lower() == 'chamber': # CHAMBER MODEL - temperature change dy_dt[ITEMP] = -Temp1 * Temp2 * np.exp(-Temp2 * (time + t)) elif simulation_type.lower() == 'parcel': # adiabatic parcel dy_dt[ITEMP] = RM / Y[IPRESS] * dy_dt[IPRESS] * Y[ ITEMP] / CPM # TEMPERATURE CHANGE: EXPANSION dy_dt[ITEMP] = dy_dt[ITEMP] - c.LV / CPM * dwv_dt else: print('simulation type unknown') return # --------------------------------RH change------------------------------------ dy_dt[IRH] = svp1 * dwv_dt * (Y[IPRESS] - svp1) dy_dt[IRH] = dy_dt[IRH] + svp1 * WV * dy_dt[IPRESS] dy_dt[IRH] = ( dy_dt[IRH] - WV * Y[IPRESS] * derivative(f.svp_liq, Y[ITEMP], dx=1.0) * dy_dt[ITEMP]) dy_dt[IRH] = dy_dt[IRH] / (c.eps * svp1**2) # ----------------------------------------------------------------------------- # ------------------------------ SEMI-VOLATILES ------------------------------- if n.SV_flag: # SV_mass = np.reshape(Y[INDSV1:INDSV2],[n.n_sv,n.nmodes*n.nbins]) # SV_mass = np.where(SV_mass == 0.0,1e-30,SV_mass) # MBIN2[n.n_sv*-1:,:] = SV_mass RH_EQ_SV = f.K01SV(Y[:IND1], Y[ITEMP], MBIN2, n.n_sv, RHOBIN2, NUBIN2, MOLWBIN2) RH_EQ = RH_EQ_SV[0] RHOAT = RH_EQ_SV[1] DW = RH_EQ_SV[2] SVP_ORG = f.SVP_GASES(n.semi_vols, Y[ITEMP], n.n_sv) #C-C equation #RH_ORG = [x*Y[IPRESS]/c.RA/Y[ITEMP] for x in Y[IRH_SV]] RH_ORG = [x for x in Y[IRH_SV]] RH_ORG = [(x / c.aerosol_dict[key][0]) * c.R * Y[ITEMP] for x, key in zip(RH_ORG, n.semi_vols[:n.n_sv]) ] # just for n_sv keys in dictionary RH_ORG = [RH_ORG[x] / SVP_ORG[x] for x in range(n.n_sv)] dy_dt[INDSV1:INDSV2] = f.SVGROWTHRATE(Y[ITEMP], Y[IPRESS], SVP_ORG, RH_ORG, RH_EQ, DW, n.n_sv, n.nbins, n.nmodes, MOLWBIN2) dy_dt[IRH_SV] = -np.sum(np.reshape( dy_dt[INDSV1:INDSV2], [n.n_sv, IND1]) * Y[IND1:IND2], axis=1) #see line 137 return dy_dt #--------------------- SET-UP solver ------------------------------------------ y0 = Y t0 = 0.0 #define assimulo problem exp_mod = Explicit_Problem(dy_dt_func, y0, t0) # define an explicit solver exp_sim = CVode(exp_mod) exp_sim.iter = 'Newton' exp_sim.discr = 'BDF' #set parameters tol_list = np.zeros_like(Y) tol_list[0:IND1] = 1e-40 # this is now different to ACPIM (1e-25) tol_list[IND1:IND2] = 10 # number tol_list[IND2:IND3] = 1e-30 # capacitance tol_list[IND3:INDSV2] = 1e-26 #condendensed semi-vol mass tol_list[IRH_SV] = 1e-26 # RH of each semi-vol compound tol_list[IPRESS] = 10 tol_list[ITEMP] = 1e-4 tol_list[IRH] = 1e-8 # set tolerance for each dydt function exp_sim.atol = tol_list exp_sim.rtol = 1.0e-8 exp_sim.inith = 0 # initial time step-size exp_sim.usejac = False exp_sim.maxncf = 100 # max number of convergence failures allowed by solver exp_sim.verbosity = 40 t_output, y_output = exp_sim.simulate(1) return y_output[-1, :], t_output[:]
def simulate(self, Tend, nIntervals, gridWidth): # define assimulo problem:(has to be done here because of the starting value in Explicit_Problem solver = Explicit_Problem(self.rhs, self.y0) ''' *******DELETE LATER ''''''''' # problem.handle_event = handle_event # problem.state_events = state_events # problem.init_mode = init_mode solver.handle_result = self.handle_result solver.name = 'Simple Explicit Example' simulation = CVode(solver) # Create a RungeKutta34 solver # simulation.inith = 0.1 #Sets the initial step, default = 0.01 # Change multistep method: 'adams' or 'VDF' if self.discr == 'Adams': simulation.discr = 'Adams' simulation.maxord = 12 else: simulation.discr = 'BDF' simulation.maxord = 5 # Change iteration algorithm: functional(FixedPoint) or newton if self.iter == 'FixedPoint': simulation.iter = 'FixedPoint' else: simulation.iter = 'Newton' # Sets additional parameters simulation.atol = self.atol simulation.rtol = self.rtol simulation.verbosity = 0 if hasattr(simulation, 'continuous_output'): simulation.continuous_output = False # default 0, if one step approach should be used elif hasattr(simulation, 'report_continuously'): simulation.report_continuously = False # default 0, if one step approach should be used # Create Solver and set settings # noRootFunctions = np.size(self.state_events(self.t0, np.array(self.y0))) # solver = sundials.CVodeSolver(RHS = self.f, ROOT = self.rootf, SW = [False]*noRootFunctions, # abstol = self.atol, reltol = self.rtol) # solver.settings.JAC = None #Add user-dependent jacobian here '''Initialize problem ''' # solver.init(self.t0, self.y0) self.handle_result(self.t0, self.y0) nextTimeEvent = self.time_events(self.t0, self.y0) self.t_cur = self.t0 self.y_cur = self.y0 state_event = False # # if gridWidth <> None: nOutputIntervals = int((Tend - self.t0) / gridWidth) else: nOutputIntervals = nIntervals # Define step length depending on if gridWidth or nIntervals has been chosen if nOutputIntervals > 0: # Last point on grid (does not have to be Tend:) if(gridWidth <> None): dOutput = gridWidth else: dOutput = (Tend - self.t0) / nIntervals else: dOutput = Tend outputStepCounter = long(1) nextOutputPoint = min(self.t0 + dOutput, Tend) while self.t_cur < Tend: # Time-Event detection and step time adjustment if nextTimeEvent is None or nextOutputPoint < nextTimeEvent: time_event = False self.t_cur = nextOutputPoint else: time_event = True self.t_cur = nextTimeEvent try: # #Integrator step # self.y_cur = solver.step(self.t_cur) # self.y_cur = np.array(self.y_cur) # state_event = False # Simulate # take a step to next output point: t_new, y_new = simulation.simulate(self.t_cur) # 5, 10) #5, 10 self.t_cur self.t_cur 2. argument nsteps Simulate 5 seconds # t_new, y_new are both vectors of the time and states at t_cur and all intermediate # points before it! So take last values: self.t_cur = t_new[-1] self.y_cur = y_new[-1] state_event = False except: import sys print "Unexpected error:", sys.exc_info()[0] # except CVodeRootException, info: # self.t_cur = info.t # self.y_cur = info.y # self.y_cur = np.array(self.y_cur) # time_event = False # state_event = True # # # Depending on events have been detected do different tasks if time_event or state_event: event_info = [state_event, time_event] if not self.handle_event(self, event_info): break solver.init(self.t_cur, self.y_cur) nextTimeEvent = self.time_events(self.t_cur, self.y_cur) # If no timeEvent happens: if nextTimeEvent <= self.t_cur: nextTimeEvent = None if self.t_cur == nextOutputPoint: # Write output if not happened before: if not time_event and not state_event: self.handle_result(nextOutputPoint, self.y_cur) outputStepCounter += 1 nextOutputPoint = min(self.t0 + outputStepCounter * dOutput, Tend) self.finalize()
def run_example(with_plots=True): r""" Example to demonstrate the use of a preconditioner .. math:: \dot y_1 & = 2 t \sin y_1 + t \sin y_2 \\ \dot y_2 & = 3 t \sin y_1 + 2 t \sin y_2 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ #Define the rhs def rhs(t, y): A = np.array([[2.0, 1.0], [3.0, 2.0]]) yd = np.dot(A * t, np.sin(y)) return yd #Define the preconditioner setup function def prec_setup(t, y, fy, jok, gamma, data): A = np.array([[2.0, 1.0], [3.0, 2.0]]) #If jok is false the jacobian data needs to be recomputed if jok == False: #Extract the diagonal of the jacobian to form a Jacobi preconditioner a0 = A[0, 0] * t * np.cos(y[0]) a1 = A[1, 1] * t * np.cos(y[1]) a = np.array([(1. - gamma * a0), (1. - gamma * a1)]) #Return true (jacobian data was recomputed) and the new data return [True, a] #If jok is true the existing jacobian data can be reused if jok == True: #Return false (jacobian data was reused) and the old data return [False, data] #Define the preconditioner solve function def prec_solve(t, y, fy, r, gamma, delta, data): #Solve the system Pz = r z0 = r[0] / data[0] z1 = r[1] / data[1] z = np.array([z0, z1]) return z #Initial conditions y0 = [1.0, 2.0] #Define an Assimulo problem exp_mod = Explicit_Problem( rhs, y0, name="Example of using a preconditioner in SUNDIALS") #Set the preconditioner setup and solve function for the problem exp_mod.prec_setup = prec_setup exp_mod.prec_solve = prec_solve #Create a CVode solver exp_sim = CVode(exp_mod) #Set the parameters for the solver exp_sim.iter = 'Newton' exp_sim.discr = 'BDF' exp_sim.atol = 1e-5 exp_sim.rtol = 1e-5 exp_sim.linear_solver = 'SPGMR' exp_sim.precond = "PREC_RIGHT" #Set the desired type of preconditioning #Simulate t, y = exp_sim.simulate(5) if with_plots: exp_sim.plot() #Basic verification nose.tools.assert_almost_equal(y[-1, 0], 3.11178295, 4) nose.tools.assert_almost_equal(y[-1, 1], 3.19318992, 4) return exp_mod, exp_sim
def run_sim_ice(Y, YLIQ): def dy_dt_func(t, Y): dy_dt = np.zeros(len(Y)) svp = f.svp_liq(Y[ITEMP]) svp_ice = f.svp_ice(Y[ITEMP]) # vapour mixing ratio WV = c.eps * Y[IRH_ICE] * svp / (Y[IPRESS_ICE] - svp) # liquid mixing ratio WL = sum(YLIQ[IND1:IND2] * YLIQ[0:IND1]) # ice mixing ratio WI = sum(Y[IND1:IND2] * Y[0:IND1]) Cpm = c.CP + WV * c.CPV + WL * c.CPW + WI * c.CPI # RH with respect to ice RH_ICE = WV / (c.eps * svp_ice / (Y[IPRESS_ICE] - svp_ice)) # ------------------------- growth rate of ice -------------------------- RH_EQ = 1e0 # from ACPIM, FPARCELCOLD - MICROPHYSICS.f90 CAP = f.CAPACITANCE01(Y[0:IND1], np.exp(Y[IND2:IND3])) growth_rate = f.ICEGROWTHRATE(Y[ITEMP_ICE], Y[IPRESS_ICE], RH_ICE, RH_EQ, Y[0:IND1], np.exp(Y[IND2:IND3]), CAP) growth_rate[np.isnan(growth_rate)] = 0 # get rid of nans growth_rate = np.where(Y[IND1:IND2] < 1e-6, 0.0, growth_rate) # Mass of water condensing dy_dt[:IND1] = growth_rate #---------------------------aspect ratio--------------------------------------- DELTA_RHO = c.eps * svp / (Y[IPRESS_ICE] - svp) DELTA_RHOI = c.eps * svp_ice / (Y[IPRESS_ICE] - svp_ice) DELTA_RHO = Y[IRH_ICE] * DELTA_RHO - DELTA_RHOI DELTA_RHO = DELTA_RHO * Y[IPRESS_ICE] / Y[ITEMP_ICE] / c.RA RHO_DEP = f.DEP_DENSITY(DELTA_RHO, Y[ITEMP_ICE]) # this is the rate of change of LOG of the aspect ratio dy_dt[IND2:IND3] = (dy_dt[0:IND1] * ((f.INHERENTGROWTH(Y[ITEMP_ICE]) - 1) / (f.INHERENTGROWTH(Y[ITEMP_ICE]) + 2)) / (Y[0:IND1] * c.rhoi * RHO_DEP)) #------------------------------------------------------------------------------ # Change in vapour content dwv_dt = -1 * sum(Y[IND1:IND2] * dy_dt[0:IND1]) # change in water vapour mixing ratio DRI = -1 * dwv_dt dy_dt[ITEMP_ICE] = 0.0 #+c.LS/Cpm*DRI # if n.Simulation_type.lower() == 'parcel': # dy_dt[ITEMP_ICE]=dy_dt[ITEMP_ICE] + c.LS/Cpm*DRI #---------------------------RH change------------------------------------------ dy_dt[IRH_ICE] = (Y[IPRESS_ICE] - svp) * svp * dwv_dt dy_dt[IRH_ICE] = ( dy_dt[IRH_ICE] - WV * Y[IPRESS_ICE] * derivative(f.svp_liq, Y[ITEMP_ICE], dx=1.0) * dy_dt[ITEMP_ICE]) dy_dt[IRH_ICE] = dy_dt[IRH_ICE] / (c.eps * svp**2) #------------------------------------------------------------------------------ return dy_dt #--------------------- SET-UP solver -------------------------------------- y0 = Y t0 = 0.0 #define assimulo problem exp_mod = Explicit_Problem(dy_dt_func, y0, t0) # define an explicit solver exp_sim = CVode(exp_mod) exp_sim.iter = 'Newton' exp_sim.discr = 'BDF' # set tolerance for each dydt function tol_list = np.zeros_like(Y) tol_list[0:IND1] = 1e-30 # mass tol_list[IND1:IND2] = 10 # number tol_list[IND2:IND3] = 1e-30 # aspect ratio # tol_list[IND3:IRH_SV_ICE] = 1e-26 #tol_list[IRH_SV_ICE] = 1e-26 tol_list[IPRESS_ICE] = 10 tol_list[ITEMP_ICE] = 1e-4 tol_list[IRH_ICE] = 1e-8 exp_sim.atol = tol_list exp_sim.rtol = 1.0e-8 exp_sim.inith = 1.0e-2 # initial time step-size exp_sim.usejac = False exp_sim.maxncf = 100 # max number of convergence failures allowed by solver exp_sim.verbosity = 40 t_output, y_output = exp_sim.simulate(1) return y_output[-1, :]
def run_example(with_plots=True): r""" Example for demonstrating the use of a user supplied Jacobian (sparse). Note that this will only work if Assimulo has been configured with Sundials + SuperLU. Based on the SUNDIALS example cvRoberts_sps.c ODE: .. math:: \dot y_1 &= -0.04y_1 + 1e4 y_2 y_3 \\ \dot y_2 &= - \dot y_1 - \dot y_3 \\ \dot y_3 &= 3e7 y_2^2 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ #Defines the rhs def f(t, y): yd_0 = -0.04 * y[0] + 1e4 * y[1] * y[2] yd_2 = 3e7 * y[1] * y[1] yd_1 = -yd_0 - yd_2 return N.array([yd_0, yd_1, yd_2]) #Defines the Jacobian def jac(t, y): colptrs = [0, 3, 6, 9] rowvals = [0, 1, 2, 0, 1, 2, 0, 1, 2] data = [ -0.04, 0.04, 0.0, 1e4 * y[2], -1e4 * y[2] - 6e7 * y[1], 6e7 * y[1], 1e4 * y[1], -1e4 * y[1], 0.0 ] J = SP.csc_matrix((data, rowvals, colptrs)) return J #Defines an Assimulo explicit problem y0 = [1.0, 0.0, 0.0] #Initial conditions exp_mod = Explicit_Problem(f, y0, name='Example using analytic (sparse) Jacobian') exp_mod.jac = jac #Sets the Jacobian exp_mod.jac_nnz = 9 exp_sim = CVode(exp_mod) #Create a CVode solver #Set the parameters exp_sim.iter = 'Newton' #Default 'FixedPoint' exp_sim.discr = 'BDF' #Default 'Adams' exp_sim.atol = [1e-8, 1e-14, 1e-6] #Default 1e-6 exp_sim.rtol = 1e-4 #Default 1e-6 exp_sim.linear_solver = "sparse" #Simulate t, y = exp_sim.simulate(0.4) #Simulate 0.4 seconds #Basic tests nose.tools.assert_almost_equal(y[-1][0], 0.9851, 3) #Plot if with_plots: P.plot(t, y[:, 1], linestyle="dashed", marker="o") #Plot the solution P.xlabel('Time') P.ylabel('State') P.title(exp_mod.name) P.show() return exp_mod, exp_sim
Run simulation """ #Create solver object sim_pen = CVode(mod_pen) #Simulation parameters #atol = 1.e-6*ones(shape(y0)) atol = rtol * N.array([1, 1, 1, 1]) #default 1e-06 sim_pen.atol = atol sim_pen.rtol = rtol sim_pen.maxh = hmax sim_pen.maxord = maxord sim_pen.inith = h0 #Simulate t, y = sim_pen.simulate(tf) """ Plot results """ #Plot #P.plot(t,y) #P.legend(['$x$','$y$','$\dot{x}$','$\dot{y}$']) #P.title('Elastic pendulum with k = ' + str(k) + ', stretch = ' + str(stretch)) #P.xlabel('time') #P.ylabel('state') #show() P.plot(y[:, 0], y[:, 1]) P.plot(y[0, 0], y[0, 1], 'ro') P.title('Elastic pendulum with k = ' + str(k) + ', stretch = ' + str(stretch) + '\nCVode with rtol = ' + str(rtol) # + ', atol = ' + str(atol)
class Assimulate(Simulate): """Provides simulation routines Attributes ---------- model : class modelbase.Model or modelbase.LabelModel instance **kwargs : dict User defined dynamic changes for rate functions Methods ------- generate_integrator(y0, name, verbosity) Initializes CVODE integrator set_initial_value(y0, t0) Sets initial values integrate(t, integrator, minstep, maxstep, nsteps) Integrates model. Returns values at time point t timeCourse(Torig, y0, integrator, minstep, maxstep, nsteps) Integration over time vector """ def __init__(self, model, **kwargs): self.model = model def dydt(t, y, m): return m.model(y, t, **kwargs) def f(t, y): return (self.dydt(t, y, self.model)) self.dydt = dydt self.f = f self._successful = True self._monitor = True self._warnings = False self.clearResults() if "verbosity" in kwargs: self.generate_integrator(verbosity=kwargs["verbosity"]) else: self.generate_integrator() def generate_integrator(self, y0=None, name='---', verbosity=50): """Initializes CVODE integrator Parameters ---------- y0 : list or numpy.array Initial values name : str Name of the assimulo problem verbosity : int Verbosity of the integrator. Possible values = [10, 20, 30, 40, 50] Returns ------- None """ if y0 is None: y0 = np.zeros(len(self.model.cpdNames)) self.problem = Explicit_Problem(self.f, y0=y0, name=name) self.integrator = CVode(self.problem) self.integrator.atol = 1e-8 self.integrator.rtol = 1e-8 self.integrator.verbosity = verbosity def set_initial_value(self, y0, t0=0): """Sets initial values Parameters ---------- y0 : list or numpy.array Initial values t0 : int or float Initial time. Defaults to 0. Returns ------- None """ self.integrator.y = y0 self.integrator.t = t0 def integrate(self, t, integrator=None, minstep=None, maxstep=None, nsteps=None): """Integrates model. Returns values at time point t. .. deprecated:: 0.23 `minstep` will be removed in version 1.0 `maxstep` will be removed in version 1.0 `nsteps` will be removed in version 1.0 Parameters ---------- t : int or float Integration end point minstep : float Minimal step size maxstep : float Maximal step size nsteps : int Number of steps Returns ------- y : list integration values """ self._successful = True try: T, Y = self.integrator.simulate(t) except: print("Error while integrating with CVode") self._successful = False if len(Y.shape) == 1: Ylast = Y[-1] else: Ylast = Y[-1, :] return Ylast def timeCourse(self, Torig, y0, integrator=None, minstep=None, maxstep=None, nsteps=None): """Integration over time vector. .. deprecated:: 0.23 `minstep` will be removed in version 1.0 `maxstep` will be removed in version 1.0 `nsteps` will be removed in version 1.0 Parameters ---------- Torig : list or numpy.array Time array y0 : list or numpy.array Initial values integrator : str Scipy integrator. Defaults to "lsoda" minstep : float Minimal step size. Defaults to 1e-8 maxstep : float Maximal step size. Defaults to 0.1 nsteps : int Number of integration steps Returns ------- Y : numpy.array Array of integrated values """ self._successful = True T = Torig.copy() if y0 is not None: Y = [y0] self.set_initial_value(y0, t0=T[0]) else: Y = [np.array(self.integrator.y)] if T[0] == 0: T += self.integrator.t try: t, Y = self.integrator.simulate(T[-1], ncp_list=T) except: print("Error in timeCourse while integrating with CVode") self._successful = False if self.doesMonitor() and self.successful(): self.results.append({'t': T, 'y': Y}) return np.vstack(Y)
yd = np.dot(A, y) + b return yd #def rhs(t,y): # A =np.array([[0, 1],[-2, -1]]) # yd=np.dot(A, y) # # return yd def L(y, k): norm = ln.norm(y[0:2]) return k * (norm - 1) / norm y0 = np.array([1.0, 1.0, 1.0, 1.0]) t0 = 0.0 model = Explicit_Problem(rhs, y0, t0) # Create an Assimulo problem model.name = 'Linear Test ODE' # Specifies the name of problem sim = CVode(model) tfinal = 100.0 #Specify the final time t, y = sim.simulate( tfinal) #Use the .simulate method to simulate and provide the final time sim.plot()
def run_example(with_plots=True): r""" This example shows how to use Assimulo and CVode for simulating sensitivities for initial conditions. .. math:: \dot y_1 &= -(k_{01}+k_{21}+k_{31}) y_1 + k_{12} y_2 + k_{13} y_3 + b_1\\ \dot y_2 &= k_{21} y_1 - (k_{02}+k_{12}) y_2 \\ \dot y_3 &= k_{31} y_1 - k_{13} y_3 with the parameter dependent inital conditions :math:`y_1(0) = 0, y_2(0) = 0, y_3(0) = 0` . The initial values are taken as parameters :math:`p_1,p_2,p_3` for the computation of the sensitivity matrix, see http://sundials.2283335.n4.nabble.com/Forward-sensitivities-for-initial-conditions-td3239724.html on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ def f(t, y, p): y1, y2, y3 = y k01 = 0.0211 k02 = 0.0162 k21 = 0.0111 k12 = 0.0124 k31 = 0.0039 k13 = 0.000035 b1 = 49.3 yd_0 = -(k01 + k21 + k31) * y1 + k12 * y2 + k13 * y3 + b1 yd_1 = k21 * y1 - (k02 + k12) * y2 yd_2 = k31 * y1 - k13 * y3 return N.array([yd_0, yd_1, yd_2]) #The initial conditions y0 = [0.0, 0.0, 0.0] #Initial conditions for y p0 = [0.0, 0.0, 0.0] #Initial conditions for parameters yS0 = N.array([[1, 0, 0], [0, 1, 0], [0, 0, 1.]]) #Create an Assimulo explicit problem exp_mod = Explicit_Problem(f, y0, p0=p0, name='Example: Computing Sensitivities') #Sets the options to the problem exp_mod.yS0 = yS0 #Create an Assimulo explicit solver (CVode) exp_sim = CVode(exp_mod) #Sets the paramters exp_sim.iter = 'Newton' exp_sim.discr = 'BDF' exp_sim.rtol = 1e-7 exp_sim.atol = 1e-6 exp_sim.pbar = [ 1, 1, 1 ] #pbar is used to estimate the tolerances for the parameters exp_sim.report_continuously = True #Need to be able to store the result using the interpolate methods exp_sim.sensmethod = 'SIMULTANEOUS' #Defines the sensitvity method used exp_sim.suppress_sens = False #Dont suppress the sensitivity variables in the error test. #Simulate t, y = exp_sim.simulate(400) #Simulate 400 seconds #Basic test nose.tools.assert_almost_equal(y[-1][0], 1577.6552477, 5) nose.tools.assert_almost_equal(y[-1][1], 611.9574565, 5) nose.tools.assert_almost_equal(y[-1][2], 2215.88563217, 5) nose.tools.assert_almost_equal(exp_sim.p_sol[0][1][0], 1.0) #Plot if with_plots: title_text = r"Sensitivity w.r.t. ${}$" legend_text = r"$\mathrm{{d}}{}/\mathrm{{d}}{}$" P.figure(1) P.subplot(221) P.plot(t, N.array(exp_sim.p_sol[0])[:, 0], t, N.array(exp_sim.p_sol[0])[:, 1], t, N.array(exp_sim.p_sol[0])[:, 2]) P.title(title_text.format('p_1')) P.legend((legend_text.format('y_1', 'p_1'), legend_text.format('y_1', 'p_2'), legend_text.format('y_1', 'p_3'))) P.subplot(222) P.plot(t, N.array(exp_sim.p_sol[1])[:, 0], t, N.array(exp_sim.p_sol[1])[:, 1], t, N.array(exp_sim.p_sol[1])[:, 2]) P.title(title_text.format('p_2')) P.legend((legend_text.format('y_2', 'p_1'), legend_text.format('y_2', 'p_2'), legend_text.format('y_2', 'p_3'))) P.subplot(223) P.plot(t, N.array(exp_sim.p_sol[2])[:, 0], t, N.array(exp_sim.p_sol[2])[:, 1], t, N.array(exp_sim.p_sol[2])[:, 2]) P.title(title_text.format('p_3')) P.legend((legend_text.format('y_3', 'p_1'), legend_text.format('y_3', 'p_2'), legend_text.format('y_3', 'p_3'))) P.subplot(224) P.title('ODE Solution') P.plot(t, y) P.suptitle(exp_mod.name) P.show() return exp_mod, exp_sim
def RunModel(flagD,th,STIM,xoutS,xoutG,dataS,dataG,kTCleak,kTCmaxs, inds_to_watch = []): # going to return [tout_all,xoutG_all,xoutS_all] # This function runs the model and outputs timecourse simulation results. # Required Inputs: # flagD: 1 for deterministic simulations, 0 for stochastic simulations. # th: simulation time (hours) # STIM: stimulus vector # # Outputs: # tout_all: n-by-1 vector of time values (seconds) # xoutG_all: n-by-g matrix of species (g) through time (n) (g indices lines up to gm tab in Names.xls sheet) # xoutS_all: n-by-p matrix of speices (p) through time (n) (p indices lines up to PARCDL tab in Names.xls sheet) # # %% RUN ts=dataS.ts; ts_up=ts; N_STEPS=th*3600/ts; N_STEPS = int(N_STEPS) # % IMPORT INITIALIZED PARAMETERS pathi='initialized/'; # % for PARCDL kbR0 = float(open(pathi + "i_kbR0.txt").read()) kTL = [] with open(pathi + 'i_kTLF.txt') as f: for line in f: kTL.append(float(line)) kTL = np.array(kTL) kC173 = float(open(pathi + "i_kC173.txt").read()) kC82 = float(open(pathi + "i_kC82.txt").read()) kA77 = float(open(pathi + "i_kA77.txt").read())*5 # ^ forgot to add the *5 to this line and spent sooooo long looking for this mistake lol kA87 = float(open(pathi + "i_kA87.txt").read()) Rt = float(open(pathi + "i_Rt.txt").read()) EIF4Efree = float(open(pathi + "i_EIF4Efree.txt").read()) kDDbasal = float(open(pathi + "i_kDDbasal.txt").read()) Vc = dataS.kS[2] # % for gm if len(kTCleak)==0: for line in open(pathi + "i_kTCleakF.txt").readlines(): kTCleak.append(float(line)) kTCleak = np.matrix.transpose(np.matrix(kTCleak)) if len(kTCmaxs)==0: for line in open(pathi + "i_kTCmaxsF.txt").readlines(): kTCmaxs.append(float(line)) kTCmaxs = np.matrix.transpose(np.matrix(kTCmaxs)) kTCmaxs = np.array(kTCmaxs) # % modifying data.S structure dataS.kS[0]=Rt; dataS.kS[1]=EIF4Efree; dataS.kS[11]=kbR0; dataS.kS[16:157]=kTL; dataS.kS[631]=kC173; dataS.kS[540]=kC82; dataS.kS[708]=kA77; dataS.kS[718]=kA87; dataS.kS[449]=kDDbasal; # % modifying data.G structure dataG.kTCleak=kTCleak; dataG.kTCmaxs=kTCmaxs; # %species if len(xoutS) == 0: xoutS = [] with open(pathi + 'i_xoutF.csv', newline='') as csvfile: spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|') for row in spamreader: x = ', '.join(row) x = x.split(',') to_append = [] for item in x: to_append.append(float(item)) xoutS.append(to_append) xoutS = np.matrix(xoutS) xoutS = xoutS[24,:] if len(xoutG) == 0: if flagD: xoutG = dataG.x0gm_mpc_D else: xoutG = dataG.x0gm_mpc indsD=dataG.indsD xoutG[indsD] = dataG.x0gm_mpc_D[indsD] xoutG[indsD+141] = dataG.x0gm_mpc_D[indsD+141] xoutG[indsD+141*2] = dataG.x0gm_mpc_D[indsD+141*2] # % Apply STIM Etop = STIM[len(STIM)-1] STIM = STIM[0:len(STIM)-1] # code for logical if np.any(STIM): xoutS[0,STIM.astype(bool)] = STIM[STIM.astype(bool)] dataS.kS[452] = Etop # NOTE - matlab code # % Instantiation # t0 = 0; # optionscvodes = CVodeSetOptions('UserData', dataS,... # 'RelTol',1.e-3,... # 'LinearSolver','Dense',... # 'JacobianFn',@Jeval774); # CVodeInit(@createODEs, 'BDF', 'Newton', t0, xoutS', optionscvodes); # # %ODE15s options # %optionsode15s=odeset('RelTol',1e-3,'Jacobian',@Jeval774ode15s); # tout_all = np.zeros(shape=(N_STEPS+1)) xoutG_all = np.zeros(shape=(N_STEPS+1,len(xoutG))) xoutS_all = np.zeros(shape=(N_STEPS+1,xoutS.shape[1])) tout_all[0] = 0 xoutG_all[0,:] = np.matrix.transpose(xoutG) xoutS_all[0,:] = xoutS # % Starting simulations print("... Starting Sims") start_time = time.time() for i in range(0,int(N_STEPS)+1): # gm [xginN,xgacN,AllGenesVecN,xmN,vTC] = gm(flagD,dataG,ts,xoutG,xoutS); xoutG = np.append(np.append(np.squeeze(np.asarray(xgacN)),np.squeeze(np.asarray(xginN))),np.squeeze(np.asarray(xmN))) # NOTE - matrix to array syntax xoutG = np.matrix.transpose(np.matrix(xoutG)) dataS.mMod=xmN*(1E9/(Vc*6.023E+23)); #convert mRNAs from mpc to nM dataG.AllGenesVec=AllGenesVecN; xoutG_all[i,:] = np.matrix.transpose(xoutG) try: xoutS_all[i,:] = np.squeeze(np.asarray(xoutS)) except: xoutS_all[i,:] = np.squeeze(np.asarray(xoutS[1])) if xoutS[0,103]<xoutS[0,105]: print("Apoptosis happened") tout_all = tout_all[0:i+1] xoutG_all = xoutG_all[0:i+1] xoutS_all = xoutS_all[0:i+1] return [tout_all, xoutG_all, xoutS_all] # scipy.odeint -- takes forever # xoutS = odeint(createODEs, xoutS_all[i,:],np.array([ts_up-ts, ts_up]), args=(dataS.kS,dataS.VvPARCDL,dataS.VxPARCDL,dataS.S_PARCDL,dataS.mExp_nM.as_matrix(),dataS.mMod,dataS.flagE)) # assimulo -- much faster ode_start_time = time.time() exp_mod = MyProblem(y0=xoutS_all[i,:],dataS=dataS, Jeval774 = Jeval774) exp_sim = CVode(exp_mod) exp_sim.verbosity=50 exp_sim.re_init(ts_up-ts,xoutS_all[i,:] ) t1, xoutS = exp_sim.simulate(ts_up, 1) try: print(xoutS[1,inds_to_watch]) except: print(xoutS) print("--- %s seconds ---" % (time.time() - ode_start_time)) print("Percent complete: " + str(i/N_STEPS)) # xoutG_all[i,:] = np.matrix.transpose(xoutG); try: tout_all[i+1] = ts_up except: pass ts_up = ts_up + ts print("ODEs done") print("--- %s seconds ---" % (time.time() - start_time)) return [tout_all, xoutG_all, xoutS_all]
gamma = p[3] flux = np.array( [alpha * y[0], beta * y[0] * y[1], delta * y[0] * y[1], gamma * y[1]]) rhs = np.array([flux[0] - flux[1], flux[2] - flux[3]]) return rhs if __name__ == '__main__': p = np.array([.5, .02, .4, .004]) ode_function = lambda t, x: rhs_fun(t, x, p) # define explicit assimulo problem prob = Explicit_Problem(ode_function, y0=np.array([10, .0001])) # create solver instance solver = CVode(prob) solver.iter = 'Newton' solver.discr = 'Adams' solver.atol = 1e-10 solver.rtol = 1e-10 solver.display_progress = True solver.verbosity = 10 # simulate system time_course, y_result = solver.simulate(10, 200) print time_course print y_result
def run_simulation(filename, start_time, save_output, temp, RH, RO2_indices, H2O, input_dict, simulation_time, batch_step): from assimulo.solvers import RodasODE, CVode #Choose solver accoring to your need. from assimulo.problem import Explicit_Problem # In this function, we import functions that have been pre-compiled for use in the ODE solver # The function that calculates the RHS of the ODE is also defined within this function, such # that it can be used by the Assimulo solvers # The variables passed to this function are defined as follows: #------------------------------------------------------------------------------------- # define the ODE function to be called def dydt_func(t, y): """ This function defines the right-hand side [RHS] of the ordinary differential equations [ODEs] to be solved input: • t - time variable [internal to solver] • y - array holding concentrations of all compounds in both gas and particulate [molecules/cc] output: dydt - the dy_dt of each compound in both gas and particulate phase [molecules/cc.sec] """ #pdb.set_trace() # Calculate time of day time_of_day_seconds = start_time + t # make sure the y array is not a list. Assimulo uses lists y_asnumpy = numpy.array(y) #Calculate the concentration of RO2 species, using an index file created during parsing RO2 = numpy.sum(y[RO2_indices]) #Calculate reaction rate for each equation. # Note that H2O will change in parcel mode # The time_of_day_seconds is used for photolysis rates - need to change this if want constant values rates = evaluate_rates_fortran(RO2, H2O, temp, time_of_day_seconds) #pdb.set_trace() # Calculate product of all reactants and stochiometry for each reaction [A^a*B^b etc] reactants = reactants_fortran(y_asnumpy) #pdb.set_trace() #Multiply product of reactants with rate coefficient to get reaction rate reactants = numpy.multiply(reactants, rates) #pdb.set_trace() # Now use reaction rates with the loss_gain matri to calculate the final dydt for each compound # With the assimulo solvers we need to output numpy arrays dydt = loss_gain_fortran(reactants) #pdb.set_trace() return dydt #------------------------------------------------------------------------------------- #------------------------------------------------------------------------------------- # define jacobian function to be called def jacobian(t, y): """ This function defines Jacobian of the ordinary differential equations [ODEs] to be solved input: • t - time variable [internal to solver] • y - array holding concentrations of all compounds in both gas and particulate [molecules/cc] output: dydt_dydt - the N_compounds x N_compounds matrix of Jacobian values """ # Different solvers might call jacobian at different stages, so we have to redo some calculations here # Calculate time of day time_of_day_seconds = start_time + t # make sure the y array is not a list. Assimulo uses lists y_asnumpy = numpy.array(y) #Calculate the concentration of RO2 species, using an index file created during parsing RO2 = numpy.sum(y[RO2_indices]) #Calculate reaction rate for each equation. # Note that H2O will change in parcel mode rates = evaluate_rates_fortran(RO2, H2O, temp, time_of_day_seconds) #pdb.set_trace() # Now use reaction rates with the loss_gain matrix to calculate the final dydt for each compound # With the assimulo solvers we need to output numpy arrays dydt_dydt = jacobian_fortran(rates, y_asnumpy) #pdb.set_trace() return dydt_dydt #------------------------------------------------------------------------------------- #import static compilation of Fortran functions for use in ODE solver print("Importing pre-compiled Fortran modules") from rate_coeff_f2py import evaluate_rates as evaluate_rates_fortran from reactants_conc_f2py import reactants as reactants_fortran from loss_gain_f2py import loss_gain as loss_gain_fortran from jacobian_f2py import jacobian as jacobian_fortran # 'Unpack' variables from input_dict species_dict = input_dict['species_dict'] species_dict2array = input_dict['species_dict2array'] species_initial_conc = input_dict['species_initial_conc'] equations = input_dict['equations'] #Specify some starting concentrations [ppt] Cfactor = 2.55e+10 #ppb-to-molecules/cc # Create variables required to initialise ODE num_species = len(species_dict.keys()) y0 = [0] * num_species #Initial concentrations, set to 0 t0 = 0.0 #T0 # Define species concentrations in ppb # You have already set this in the front end script, and now we populate the y array with those concentrations for specie in species_initial_conc.keys(): y0[species_dict2array[specie]] = species_initial_conc[ specie] * Cfactor #convert from pbb to molcules/cc #Set the total_time of the simulation to 0 [havent done anything yet] total_time = 0.0 # Now run through the simulation in batches. # I do this to enable testing of coupling processes. Some initial investigations with non-ideality in # the condensed phase indicated that even defining a maximum step was not enough for ODE solvers to # overshoot a stable region. It also helps with in-simulation debugging. Its up to you if you want to keep this. # To not run in batches, just define one batch as your total simulation time. This will reduce any overhead with # initialising the solvers # Set total simulation time and batch steps in seconds # Note also that the current module outputs solver information after each batch step. This can be turned off and the # the batch step change for increased speed #simulation_time= 3600.0 #batch_step=100.0 t_array = [] time_step = 0 number_steps = int( simulation_time / batch_step) # Just cycling through 3 steps to get to a solution # Define a matrix that stores values as outputs from the end of each batch step. Again, you can remove # the need to run in batches. You can tell the Assimulo solvers the frequency of outputs. y_matrix = numpy.zeros((int(number_steps), len(y0))) print("Starting simulation") # In the following, we can while total_time < simulation_time: if total_time == 0.0: #Define an Assimulo problem #Define an explicit solver exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename) else: y0 = y_output[ -1, :] # Take the output from the last batch as the start of this exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename) # Define ODE parameters. # Initial steps might be slower than mid-simulation. It varies. #exp_mod.jac = dydt_jac # Define which ODE solver you want to use exp_sim = CVode(exp_mod) tol_list = [1.0e-3] * num_species exp_sim.atol = tol_list #Default 1e-6 exp_sim.rtol = 0.03 #Default 1e-6 exp_sim.inith = 1.0e-6 #Initial step-size #exp_sim.discr = 'Adams' exp_sim.maxh = 100.0 # Use of a jacobian makes a big differece in simulation time. This is relatively # easy to define for a gas phase - not sure for an aerosol phase with composition # dependent processes. exp_sim.usejac = True # To be provided as an option in future update. #exp_sim.fac1 = 0.05 #exp_sim.fac2 = 50.0 exp_sim.report_continuously = True exp_sim.maxncf = 1000 #Sets the parameters t_output, y_output = exp_sim.simulate( batch_step) #Simulate 'batch' seconds total_time += batch_step t_array.append( total_time ) # Save the output from the end step, of the current batch, to a matrix y_matrix[time_step, :] = y_output[-1, :] #now save this information into a matrix for later plotting. time_step += 1 # Do you want to save the generated matrix of outputs? if save_output: numpy.save(filename + '_output', y_matrix) df = pd.DataFrame(y_matrix) df.to_csv(filename + "_output_matrix.csv") w = csv.writer(open(filename + "_output_names.csv", "w")) for specie, number in species_dict2array.items(): w.writerow([specie, number]) with_plots = True #pdb.set_trace() #Plot the change in concentration over time for a given specie. For the user to change / remove #In a future release I will add this as a seperate module if with_plots: try: P.plot(t_array, numpy.log10(y_matrix[:, species_dict2array['APINENE']]), marker='o', label="APINENE") P.plot(t_array, numpy.log10(y_matrix[:, species_dict2array['PINONIC']]), marker='o', label="PINONIC") P.title(exp_mod.name) P.legend(loc='upper left') P.ylabel("Concetration log10[molecules/cc]") P.xlabel("Time [seconds] since start of simulation") P.show() except: print( "There is a problem using Matplotlib in your environment. If using this within a docker container, you will need to transfer the data to the host or configure your container to enable graphical displays. More information can be found at http://wiki.ros.org/docker/Tutorials/GUI " )
def run_simulation(filename, save_output, start_time, temp, RH, RO2_indices, H2O, input_dict, simulation_time, batch_step): from assimulo.solvers import RodasODE, CVode #Choose solver accoring to your need. from assimulo.problem import Explicit_Problem # In this function, we import functions that have been pre-compiled for use in the ODE solver # The function that calculates the RHS of the ODE is also defined within this function, such # that it can be used by the Assimulo solvers # In the standard Python version [not using Numba] I use Sparse matrix operations in calculating loss/gain of each compound. # This function loads the matrix created at the beginning of the module. def load_sparse_csr(filename): loader = numpy.load('loss_gain_' + filename + '.npz') return csr_matrix( (loader['data'], loader['indices'], loader['indptr']), shape=loader['shape']) def load_sparse_csr_reactants(filename): loader = numpy.load('reactants_indices_sparse_' + filename + '.npz') return csr_matrix( (loader['data'], loader['indices'], loader['indptr']), shape=loader['shape']) #------------------------------------------------------------------------------------- # define the ODE function to be called def dydt_func(t, y): """ This function defines the right-hand side [RHS] of the ordinary differential equations [ODEs] to be solved input: • t - time variable [internal to solver] • y - array holding concentrations of all compounds in both gas and particulate [molecules/cc] output: dydt - the dy_dt of each compound in both gas and particulate phase [molecules/cc.sec] """ #pdb.set_trace() #Here we use the pre-created Numba based functions to arrive at our value for dydt # Calculate time of day time_of_day_seconds = start_time + t # make sure the y array is not a list. Assimulo uses lists y_asnumpy = numpy.array(y) #pdb.set_trace() # reactants=numpy.zeros((equations),) #pdb.set_trace() #Calculate the concentration of RO2 species, using an index file created during parsing RO2 = numpy.sum(y[RO2_indices]) #Calculate reaction rate for each equation. # Note that H2O will change in parcel mode [to be changed in the full aerosol mode] # The time_of_day_seconds is used for photolysis rates - need to change this if want constant values #pdb.set_trace() rates = evaluate_rates(time_of_day_seconds, RO2, H2O, temp, numpy.zeros((equations)), numpy.zeros((63))) # Calculate product of all reactants and stochiometry for each reaction [A^a*B^b etc] reactants = reactant_product(y_asnumpy, equations, numpy.zeros((equations))) #Multiply product of reactants with rate coefficient to get reaction rate #pdb.set_trace() reactants = numpy.multiply(reactants, rates) # Now use reaction rates with the loss_gain information in a pre-created Numba file to calculate the final dydt for each compound dydt = dydt_eval(numpy.zeros((len(y_asnumpy))), reactants) #pdb.set_trace() ############ Development place-holder ############## # ---------------------------------------------------------------------------------- # The following demonstrates the same procedure but using only Numpy and pure python # For the full MCM this is too slow, but is useful for demonstrations and testing #Calculate reaction rate for each equation. ## rates=test(time_of_day_seconds,RO2,H2O,temp) # Calculate product of all reactants and stochiometry for each reaction [A^a*B^b etc] # Take the approach of using sparse matrix operations from a python perspective # This approach uses the rule of logarithms and sparse matrix multiplication ##temp_array=reactants_indices_sparse @ numpy.log(y_asnumpy) ##indices=numpy.where(temp_array > 0.0) ##reactants[indices]=numpy.exp(temp_array[indices]) #Multiply product of reactants with rate coefficient to get reaction rate ## reactants = numpy.multiply(reactants,rates) # Now use reaction rates with the loss_gain matri to calculate the final dydt for each compound # With the assimulo solvers we need to output numpy arrays ##dydt=numpy.array(loss_gain @ reactants) # ---------------------------------------------------------------------------------- return dydt #------------------------------------------------------------------------------------- print( "Importing Numba modules [compiling if first import or clean build...please be patient]" ) #import Numba functions for use in ODE solver from Rate_coefficients_numba import evaluate_rates from Reactants_conc_numba import reactants as reactant_product from Loss_Gain_numba import dydt as dydt_eval # 'Unpack' variables from input_dict species_dict = input_dict['species_dict'] species_dict2array = input_dict['species_dict2array'] species_initial_conc = input_dict['species_initial_conc'] equations = input_dict['equations'] # Set dive by zero to ignore for use of any sparse matrix multiplication numpy.errstate(divide='ignore') # --- For Numpy and pure Python runs ---- # Load the sparse matrix used in calculating the reactant products and dydt function ## reactants_indices_sparse = load_sparse_csr_reactants(filename) ## loss_gain = load_sparse_csr(filename) #Specify some starting concentrations [ppt] Cfactor = 2.55e+10 #ppb-to-molecules/cc # Create variables required to initialise ODE num_species = len(species_dict.keys()) y0 = [0] * num_species #Initial concentrations, set to 0 t0 = 0.0 #T0 # Define species concentrations in ppb # You have already set this in the front end script, and now we populate the y array with those concentrations for specie in species_initial_conc.keys(): y0[species_dict2array[specie]] = species_initial_conc[ specie] * Cfactor #convert from pbb to molcules/cc #Set the total_time of the simulation to 0 [havent done anything yet] total_time = 0.0 # Now run through the simulation in batches. # I do this to enable testing of coupling processes. Some initial investigations with non-ideality in # the condensed phase indicated that even defining a maximum step was not enough for ODE solvers to # overshoot a stable region. It also helps with in-simulation debugging. Its up to you if you want to keep this. # To not run in batches, just define one batch as your total simulation time. This will reduce any overhead with # initialising the solvers # Set total simulation time and batch steps in seconds # Note also that the current module outputs solver information after each batch step. This can be turned off and the # the batch step change for increased speed # simulation_time= 3600.0 # seconds # batch_step=100.0 # seconds t_array = [] time_step = 0 number_steps = int( simulation_time / batch_step) # Just cycling through 3 steps to get to a solution # Define a matrix that stores values as outputs from the end of each batch step. Again, you can remove # the need to run in batches. You can tell the Assimulo solvers the frequency of outputs. y_matrix = numpy.zeros((int(number_steps), len(y0))) print("Starting simulation") #pdb.set_trace() while total_time < simulation_time: if total_time == 0.0: #Define an Assimulo problem #Define an explicit solver #pdb.set_trace() exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename) else: y0 = y_output[ -1, :] # Take the output from the last batch as the start of this exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename) # Define ODE parameters. # Initial steps might be slower than mid-simulation. It varies. #exp_mod.jac = dydt_jac # Define which ODE solver you want to use exp_sim = CVode(exp_mod) tol_list = [1.0e-3] * num_species exp_sim.atol = tol_list #Default 1e-6 exp_sim.rtol = 1e-6 #Default 1e-6 exp_sim.inith = 1.0e-6 #Initial step-size #exp_sim.discr = 'Adams' exp_sim.maxh = 100.0 # Use of a jacobian makes a big differece in simulation time. This is relatively # easy to define for a gas phase - not sure for an aerosol phase with composition # dependent processes. exp_sim.usejac = False # To be provided as an option in future update. See Fortran variant for use of Jacobian #exp_sim.fac1 = 0.05 #exp_sim.fac2 = 50.0 exp_sim.report_continuously = True exp_sim.maxncf = 1000 #Sets the parameters t_output, y_output = exp_sim.simulate( batch_step) #Simulate 'batch' seconds total_time += batch_step t_array.append( total_time ) # Save the output from the end step, of the current batch, to a matrix y_matrix[time_step, :] = y_output[-1, :] #pdb.set_trace() #now save this information into a matrix for later plotting. time_step += 1 # Do you want to save the generated matrix of outputs? if save_output: numpy.save(filename + '_output', y_matrix) df = pd.DataFrame(y_matrix) df.to_csv(filename + "_output_matrix.csv") w = csv.writer(open(filename + "_output_names.csv", "w")) for specie, number in species_dict2array.items(): w.writerow([specie, number]) with_plots = True #pdb.set_trace() #Plot the change in concentration over time for a given specie. For the user to change / remove #In a future release I will add this as a seperate module if with_plots: try: P.plot(t_array, numpy.log10(y_matrix[:, species_dict2array['APINENE']]), marker='o', label="APINENE") P.plot(t_array, numpy.log10(y_matrix[:, species_dict2array['PINONIC']]), marker='o', label="PINONIC") P.title(exp_mod.name) P.legend(loc='upper left') P.ylabel("Concetration log10[molecules/cc]") P.xlabel("Time [seconds] since start of simulation") P.show() except: print( "There is a problem using Matplotlib in your environment. If using this within a docker container, you will need to transfer the data to the host or configure your container to enable graphical displays. More information can be found at http://wiki.ros.org/docker/Tutorials/GUI " )
def run_example(with_plots=True): """ This is the same example from the Sundials package (cvsRoberts_FSA_dns.c) This simple example problem for CVode, due to Robertson, is from chemical kinetics, and consists of the following three equations: .. math:: \dot y_1 &= -p_1 y_1 + p_2 y_2 y_3 \\ \dot y_2 &= p_1 y_1 - p_2 y_2 y_3 - p_3 y_2^2 \\ \dot y_3 &= p_3 y_2^2 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ def f(t, y, p): p3 = 3.0e7 yd_0 = -p[0] * y[0] + p[1] * y[1] * y[2] yd_1 = p[0] * y[0] - p[1] * y[1] * y[2] - p3 * y[1]**2 yd_2 = p3 * y[1]**2 return N.array([yd_0, yd_1, yd_2]) #The initial conditions y0 = [1.0, 0.0, 0.0] #Initial conditions for y #Create an Assimulo explicit problem exp_mod = Explicit_Problem(f, y0, name='Sundials test example: Chemical kinetics') #Sets the options to the problem exp_mod.p0 = [0.040, 1.0e4] #Initial conditions for parameters exp_mod.pbar = [0.040, 1.0e4] #Create an Assimulo explicit solver (CVode) exp_sim = CVode(exp_mod) #Sets the paramters exp_sim.iter = 'Newton' exp_sim.discr = 'BDF' exp_sim.rtol = 1.e-4 exp_sim.atol = N.array([1.0e-8, 1.0e-14, 1.0e-6]) exp_sim.sensmethod = 'SIMULTANEOUS' #Defines the sensitvity method used exp_sim.suppress_sens = False #Dont suppress the sensitivity variables in the error test. exp_sim.report_continuously = True #Simulate t, y = exp_sim.simulate( 4, 400) #Simulate 4 seconds with 400 communication points #Plot if with_plots: import pylab as P P.plot(t, y) P.xlabel('Time') P.ylabel('State') P.title(exp_mod.name) P.show() #Basic test nose.tools.assert_almost_equal(y[-1][0], 9.05518032e-01, 4) nose.tools.assert_almost_equal(y[-1][1], 2.24046805e-05, 4) nose.tools.assert_almost_equal(y[-1][2], 9.44595637e-02, 4) nose.tools.assert_almost_equal( exp_sim.p_sol[0][-1][0], -1.8761, 2) #Values taken from the example in Sundials nose.tools.assert_almost_equal(exp_sim.p_sol[1][-1][0], 2.9614e-06, 8) return exp_mod, exp_sim
def run_example(with_plots=True): r""" Example for the use of the stability limit detection algorithm in CVode. .. math:: \dot y_1 &= y_2 \\ \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1) \\ \dot y_3 &= sin(ty_2) with :math:`\mu=\frac{1}{5} 10^3`. on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ class Extended_Problem(Explicit_Problem): order = [] def handle_result(self, solver, t, y): Explicit_Problem.handle_result(self, solver, t, y) self.order.append(solver.get_last_order()) eps = 5.e-3 my = 1./eps #Define the rhs def f(t,y): yd_0 = y[1] yd_1 = my*((1.-y[0]**2)*y[1]-y[0]) yd_2 = N.sin(t*y[1]) return N.array([yd_0,yd_1, yd_2]) y0 = [2.0,-0.6, 0.1] #Initial conditions #Define an Assimulo problem exp_mod = Extended_Problem(f,y0, name = "CVode: Stability problem") #Define an explicit solver exp_sim = CVode(exp_mod) #Create a CVode solver #Sets the parameters exp_sim.stablimdet = True exp_sim.report_continuously = True #Simulate t, y = exp_sim.simulate(2.0) #Simulate 2 seconds #Plot if with_plots: P.subplot(211) P.plot(t,y[:,2]) P.ylabel("State: $y_1$") P.subplot(212) P.plot(t,exp_mod.order) P.ylabel("Order") P.suptitle(exp_mod.name) P.xlabel("Time") P.show() #Basic test x1 = y[:,0] assert N.abs(x1[-1]-1.8601438) < 1e-1 #For test purpose return exp_mod, exp_sim
def run_simulation(filename, save_output, start_time, temp, RH, RO2_indices, H2O, PInit, y_cond, input_dict, simulation_time, batch_step, plot_mass): from assimulo.solvers import RodasODE, CVode, RungeKutta4, LSODAR #Choose solver accoring to your need. from assimulo.problem import Explicit_Problem # In this function, we import functions that have been pre-compiled for use in the ODE solver # The function that calculates the RHS of the ODE is also defined within this function, such # that it can be used by the Assimulo solvers # The variables passed to this function are defined as follows: #------------------------------------------------------------------------------------- #------------------------------------------------------------------------------------- # define the ODE function to be called def dydt_func(t, y): """ This function defines the right-hand side [RHS] of the ordinary differential equations [ODEs] to be solved input: • t - time variable [internal to solver] • y - array holding concentrations of all compounds in both gas and particulate [molecules/cc] output: dydt - the dy_dt of each compound in both gas and particulate phase [molecules/cc.sec] """ dy_dt = numpy.zeros((total_length_y, 1), ) #pdb.set_trace() # Calculate time of day time_of_day_seconds = start_time + t #pdb.set_trace() # make sure the y array is not a list. Assimulo uses lists y_asnumpy = numpy.array(y) Model_temp = temp #pdb.set_trace() #Calculate the concentration of RO2 species, using an index file created during parsing RO2 = numpy.sum(y[RO2_indices]) #Calculate reaction rate for each equation. # Note that H2O will change in parcel mode # The time_of_day_seconds is used for photolysis rates - need to change this if want constant values rates = evaluate_rates_fortran(RO2, H2O, Model_temp, time_of_day_seconds) #pdb.set_trace() # Calculate product of all reactants and stochiometry for each reaction [A^a*B^b etc] reactants = reactants_fortran(y_asnumpy[0:num_species - 1]) #pdb.set_trace() #Multiply product of reactants with rate coefficient to get reaction rate reactants = numpy.multiply(reactants, rates) #pdb.set_trace() # Now use reaction rates with the loss_gain matri to calculate the final dydt for each compound # With the assimulo solvers we need to output numpy arrays dydt_gas = loss_gain_fortran(reactants) #pdb.set_trace() dy_dt[0:num_species - 1, 0] = dydt_gas # Change the saturation vapour pressure of water # Need to re-think the change of organic vapour pressures with temperature. # At the moment this is kept constant as re-calulation using UManSysProp very slow sat_vap_water = numpy.exp((-0.58002206E4 / Model_temp) + 0.13914993E1 - \ (0.48640239E-1 * Model_temp) + (0.41764768E-4 * (Model_temp**2.0E0))- \ (0.14452093E-7 * (Model_temp**3.0E0)) + (0.65459673E1 * numpy.log(Model_temp))) sat_vp[-1] = (numpy.log10(sat_vap_water * 9.86923E-6)) Psat = numpy.power(10.0, sat_vp) # Convert the concentration of each component in the gas phase into a partial pressure using the ideal gas law # Units are Pascals Pressure_gas = (y_asnumpy[0:num_species, ] / NA) * 8.314E+6 * Model_temp #[using R] core_mass_array = numpy.multiply(ycore_asnumpy / NA, core_molw_asnumpy) ####### Calculate the thermal conductivity of gases according to the new temperature ######## K_water_vapour = ( 5.69 + 0.017 * (Model_temp - 273.15)) * 1e-3 * 4.187 #[W/mK []has to be in W/m.K] # Use this value for all organics, for now. If you start using a non-zero enthalpy of # vapourisation, this needs to change. therm_cond_air = K_water_vapour #---------------------------------------------------------------------------- #F2c) Extract the current gas phase concentrations to be used in pressure difference calculations C_g_i_t = y_asnumpy[0:num_species, ] #Set the values for oxidants etc to 0 as will force no mass transfer #C_g_i_t[ignore_index]=0.0 C_g_i_t = C_g_i_t[include_index] #pdb.set_trace() total_SOA_mass,aw_array,size_array,dy_dt_calc = dydt_partition_fortran(y_asnumpy,ycore_asnumpy,core_dissociation, \ core_mass_array,y_density_array_asnumpy,core_density_array_asnumpy,ignore_index_fortran,y_mw,Psat, \ DStar_org_asnumpy,alpha_d_org_asnumpy,C_g_i_t,N_perbin,gamma_gas_asnumpy,Latent_heat_asnumpy,GRAV, \ Updraft,sigma,NA,kb,Rv,R_gas,Model_temp,cp,Ra,Lv_water_vapour) #pdb.set_trace() # Add the calculated gains/losses to the complete dy_dt array dy_dt[0:num_species + (num_species_condensed * num_bins), 0] += dy_dt_calc[:] #pdb.set_trace() #---------------------------------------------------------------------------- #F4) Now calculate the change in water vapour mixing ratio. #To do this we need to know what the index key for the very last element is #pdb.set_trace() #pdb.set_trace() #print "elapsed time=", elapsedTime dydt_func.total_SOA_mass = total_SOA_mass dydt_func.size_array = size_array dydt_func.temp = Model_temp dydt_func.RH = Pressure_gas[-1] / (Psat[-1] * 101325.0) dydt_func.water_activity = aw_array #---------------------------------------------------------------------------- return dy_dt #------------------------------------------------------------------------------------- #------------------------------------------------------------------------------------- #import static compilation of Fortran functions for use in ODE solver print("Importing pre-compiled Fortran modules") from rate_coeff_f2py import evaluate_rates as evaluate_rates_fortran from reactants_conc_f2py import reactants as reactants_fortran from loss_gain_f2py import loss_gain as loss_gain_fortran from partition_f2py import dydt_partition as dydt_partition_fortran # 'Unpack' variables from input_dict species_dict = input_dict['species_dict'] species_dict2array = input_dict['species_dict2array'] species_initial_conc = input_dict['species_initial_conc'] equations = input_dict['equations'] num_species = input_dict['num_species'] num_species_condensed = input_dict['num_species_condensed'] y_density_array_asnumpy = input_dict['y_density_array_asnumpy'] y_mw = input_dict['y_mw'] sat_vp = input_dict['sat_vp'] Delta_H = input_dict['Delta_H'] Latent_heat_asnumpy = input_dict['Latent_heat_asnumpy'] DStar_org_asnumpy = input_dict['DStar_org_asnumpy'] alpha_d_org_asnumpy = input_dict['alpha_d_org_asnumpy'] gamma_gas_asnumpy = input_dict['gamma_gas_asnumpy'] Updraft = input_dict['Updraft'] GRAV = input_dict['GRAV'] Rv = input_dict['Rv'] Ra = input_dict['Ra'] R_gas = input_dict['R_gas'] R_gas_other = input_dict['R_gas_other'] cp = input_dict['cp'] sigma = input_dict['sigma'] NA = input_dict['NA'] kb = input_dict['kb'] Lv_water_vapour = input_dict['Lv_water_vapour'] ignore_index = input_dict['ignore_index'] ignore_index_fortran = input_dict['ignore_index_fortran'] ycore_asnumpy = input_dict['ycore_asnumpy'] core_density_array_asnumpy = input_dict['core_density_array_asnumpy'] y_cond = input_dict['y_cond_initial'] num_bins = input_dict['num_bins'] core_molw_asnumpy = input_dict['core_molw_asnumpy'] core_dissociation = input_dict['core_dissociation'] N_perbin = input_dict['N_perbin'] include_index = input_dict['include_index'] # pdb.set_trace() #Specify some starting concentrations [ppt] Cfactor = 2.55e+10 #ppb-to-molecules/cc # Create variables required to initialise ODE y0 = [0] * (num_species + num_species_condensed * num_bins ) #Initial concentrations, set to 0 t0 = 0.0 #T0 # Define species concentrations in ppb fr the gas phase # You have already set this in the front end script, and now we populate the y array with those concentrations for specie in species_initial_conc.keys(): if specie is not 'H2O': y0[species_dict2array[specie]] = species_initial_conc[ specie] * Cfactor #convert from pbb to molcules/cc elif specie is 'H2O': y0[species_dict2array[specie]] = species_initial_conc[specie] # Now add the initial condensed phase [including water] #pdb.set_trace() y0[num_species:num_species + ((num_bins) * num_species_condensed)] = y_cond[:] #pdb.set_trace() #Set the total_time of the simulation to 0 [havent done anything yet] total_time = 0.0 # Define a 'key' that represents the end of the composition variables to track total_length_y = len(y0) key = num_species + ((num_bins) * num_species) - 1 #pdb.set_trace() # Now run through the simulation in batches. # I do this to enable testing of coupling processes. Some initial investigations with non-ideality in # the condensed phase indicated that even defining a maximum step was not enough for ODE solvers to # overshoot a stable region. It also helps with in-simulation debugging. Its up to you if you want to keep this. # To not run in batches, just define one batch as your total simulation time. This will reduce any overhead with # initialising the solvers # Set total simulation time and batch steps in seconds # Note also that the current module outputs solver information after each batch step. This can be turned off and the # the batch step change for increased speed # simulation_time= 3600.0 # batch_step=300.0 t_array = [] time_step = 0 number_steps = int( simulation_time / batch_step) # Just cycling through 3 steps to get to a solution # Define a matrix that stores values as outputs from the end of each batch step. Again, you can remove # the need to run in batches. You can tell the Assimulo solvers the frequency of outputs. y_matrix = numpy.zeros((int(number_steps), len(y0))) # Also define arrays and matrices that hold information such as total SOA mass SOA_matrix = numpy.zeros((int(number_steps), 1)) size_matrix = numpy.zeros((int(number_steps), num_bins)) print("Starting simulation") # In the following, we can while total_time < simulation_time: if total_time == 0.0: #Define an Assimulo problem #Define an explicit solver exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename) else: y0 = y_output[ -1, :] # Take the output from the last batch as the start of this exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename) # Define ODE parameters. # Initial steps might be slower than mid-simulation. It varies. #exp_mod.jac = dydt_jac # Define which ODE solver you want to use exp_sim = CVode(exp_mod) tol_list = [1.0e-2] * len(y0) exp_sim.atol = tol_list #Default 1e-6 exp_sim.rtol = 1.0e-4 #Default 1e-6 exp_sim.inith = 1.0e-6 #Initial step-size #exp_sim.discr = 'Adams' exp_sim.maxh = 100.0 # Use of a jacobian makes a big differece in simulation time. This is relatively # easy to define for a gas phase - not sure for an aerosol phase with composition # dependent processes. exp_sim.usejac = False # To be provided as an option in future update. #exp_sim.fac1 = 0.05 #exp_sim.fac2 = 50.0 exp_sim.report_continuously = True exp_sim.maxncf = 1000 #Sets the parameters t_output, y_output = exp_sim.simulate( batch_step) #Simulate 'batch' seconds total_time += batch_step t_array.append( total_time ) # Save the output from the end step, of the current batch, to a matrix y_matrix[time_step, :] = y_output[-1, :] SOA_matrix[time_step, 0] = dydt_func.total_SOA_mass size_matrix[time_step, :] = dydt_func.size_array print("SOA [micrograms/m3] = ", dydt_func.total_SOA_mass) #now save this information into a matrix for later plotting. time_step += 1 if save_output is True: print( "Saving the model output as a pickled object for later retrieval") # save the dictionary to a file for later retrieval - have to do each seperately. with open(filename + '_y_output.pickle', 'wb') as handle: pickle.dump(y_matrix, handle, protocol=pickle.HIGHEST_PROTOCOL) with open(filename + '_t_output.pickle', 'wb') as handle: pickle.dump(t_array, handle, protocol=pickle.HIGHEST_PROTOCOL) with open(filename + '_SOA_output.pickle', 'wb') as handle: pickle.dump(SOA_matrix, handle, protocol=pickle.HIGHEST_PROTOCOL) with open(filename + '_size_output.pickle', 'wb') as handle: pickle.dump(size_matrix, handle, protocol=pickle.HIGHEST_PROTOCOL) with open(filename + 'include_index.pickle', 'wb') as handle: pickle.dump(include_index, handle, protocol=pickle.HIGHEST_PROTOCOL) #pdb.set_trace() #Plot the change in concentration over time for a given specie. For the user to change / remove #In a future release I will add this as a seperate module if plot_mass is True: try: P.plot(t_array, SOA_matrix[:, 0], marker='o') P.title(exp_mod.name) P.ylabel("SOA mass [micrograms/m3]") P.xlabel("Time [seconds] since start of simulation") P.show() except: print( "There is a problem using Matplotlib in your environment. If using this within a docker container, you will need to transfer the data to the host or configure your container to enable graphical displays. More information can be found at http://wiki.ros.org/docker/Tutorials/GUI " )
ydot[3] = -y[1] * lambda_fkt(y[0], y[1]) - 1 return ydot # ydot = -y[0] # return np.array([ydot]) def lambda_fkt(y1, y2): k = 100 return k * (np.sqrt(y1**2 + y2**2) - 1) / np.sqrt(y1**2 + y2**2) #Define an Assimulo problem exp_mod = Explicit_Problem(f, 4) exp_mod.name = 'Simple BDF-2 Example' #Define another Assimulo problem def pend(t,y): #g=9.81 l=0.7134354980239037 gl=13.7503671 return np.array([y[1],-gl*np.sin(y[0])]) #pend_mod=Explicit_Problem(pend, y0=np.array([2.*np.pi,1.])) pend_mod=Explicit_Problem(f, y0=np.array([1., 0., 0., 0.])) pend_mod.name='Nonlinear Pendulum' #Define an explicit solver #exp_sim = BDF_2(pend_mod) #Create a BDF solver exp_sim = CVode(pend_mod) t, y = exp_sim.simulate(10) exp_sim.plot() mpl.show()
# pretend that boundary conditions change at t_tot=5.0 if t_tot == 5.0: num_eqn = 10.0 # write dydt code f = open('test_numba_func.py', mode='w') f.write('import numpy as np\n') f.write('\n') f.write('def dydt(t, y): # define function:\n') f.write(' dydt = np.zeros((len(y)))\n') f.write(' num_eqn = ' + str(num_eqn) + '\n') f.write(' if num_eqn == 1:\n') f.write(' dydt[0] = y[0]*0.1\n') f.write(' else:\n') f.write(' dydt[0] = y[0]*0.2\n') f.write(' return(dydt)') f.close() test_numba_func = importlib.reload(test_numba_func) # imports updated version dydt = test_numba_func.dydt dydt = jit(f8[:](f8, f8[:]), nopython=True)(dydt) mod = Explicit_Problem(dydt, y_rec[count, 1]) mod_sim = CVode(mod) # define a solver instance t_array, res = mod_sim.simulate(t) t_tot += 1.0 count += 1 y_rec[count, 0] = t_tot+t y_rec[count, 1] = res[-1] plt.plot(y_rec[:, 0], y_rec[:, 1]) plt.show()
def ode_solv(y, integ_step, rindx, pindx, rstoi, pstoi, nreac, nprod, rrc, jac_stoi, njac, jac_den_indx, jac_indx, Cinfl_now, y_arr, y_rind, uni_y_rind, y_pind, uni_y_pind, reac_col, prod_col, rstoi_flat, pstoi_flat, rr_arr, rr_arr_p, rowvals, colptrs, num_comp, num_sb, wall_on, Psat, Cw, act_coeff, kw, jac_wall_indx, seedi, core_diss, kelv_fac, kimt, num_asb, jac_part_indx, rindx_aq, pindx_aq, rstoi_aq, pstoi_aq, nreac_aq, nprod_aq, jac_stoi_aq, njac_aq, jac_den_indx_aq, jac_indx_aq, y_arr_aq, y_rind_aq, uni_y_rind_aq, y_pind_aq, uni_y_pind_aq, reac_col_aq, prod_col_aq, rstoi_flat_aq, pstoi_flat_aq, rr_arr_aq, rr_arr_p_aq, eqn_num): # inputs: ------------------------------------- # y - initial concentrations (moleucles/cm3) # integ_step - the maximum integration time step (s) # rindx - index of reactants per equation # pindx - index of products per equation # rstoi - stoichiometry of reactants # pstoi - stoichiometry of products # nreac - number of reactants per equation # nprod - number of products per equation # rrc - reaction rate coefficient # jac_stoi - stoichiometries relevant to Jacobian # njac - number of Jacobian elements affected per equation # jac_den_indx - index of component denominators for Jacobian # jac_indx - index of Jacobian to place elements per equation (rows) # Cinfl_now - influx of components with constant influx # (molecules/cc/s) # y_arr - index for matrix used to arrange concentrations, # enabling calculation of reaction rate coefficients # y_rind - index of y relating to reactants for reaction rate # coefficient equation # uni_y_rind - unique index of reactants # y_pind - index of y relating to products # uni_y_pind - unique index of products # reac_col - column indices for sparse matrix of reaction losses # prod_col - column indices for sparse matrix of production gains # rstoi_flat - 1D array of reactant stoichiometries per equation # pstoi_flat - 1D array of product stoichiometries per equation # rr_arr - index for reaction rates to allow reactant loss # calculation # rr_arr_p - index for reaction rates to allow reactant loss # calculation # rowvals - row indices of Jacobian elements # colptrs - indices of rowvals corresponding to each column of the # Jacobian # num_comp - number of components # num_sb - number of size bins # wall_on - flag saying whether to include wall partitioning # Psat - pure component saturation vapour pressures (molecules/cc) # Cw - effective absorbing mass concentration of wall (molecules/cc) # act_coeff - activity coefficient of components # kw - mass transfer coefficient to wall (/s) # jac_wall_indx - index of inputs to Jacobian by wall partitioning # seedi - index of seed material # core_diss - dissociation constant of seed material # kelv_fac - kelvin factor for particles # kimt - mass transfer coefficient for gas-particle partitioning (s) # num_asb - number of actual size bins (excluding wall) # jac_part_indx - index for sparse Jacobian for particle influence # eqn_num - number of gas- and aqueous-phase reactions # --------------------------------------------- def dydt(t, y): # define the ODE(s) # empty array to hold rate of change per component dd = np.zeros((len(y))) # gas-phase reactions ------------------------- # empty array to hold relevant concentrations for # reaction rate coefficient calculation rrc_y = np.ones((rindx.shape[0] * rindx.shape[1])) rrc_y[y_arr] = y[y_rind] rrc_y = rrc_y.reshape(rindx.shape[0], rindx.shape[1], order='C') # reaction rate (molecules/cc/s) rr = rrc[0:rindx.shape[0]] * ((rrc_y**rstoi).prod(axis=1)) # loss of reactants data = rr[rr_arr] * rstoi_flat # prepare loss values # convert to sparse matrix loss = SP.csc_matrix((data, y_rind, reac_col)) # register loss of reactants dd[uni_y_rind] -= np.array((loss.sum(axis=1))[uni_y_rind])[:, 0] # gain of products data = rr[rr_arr_p] * pstoi_flat # prepare loss values # convert to sparse matrix loss = SP.csc_matrix((data, y_pind, prod_col)) # register gain of products dd[uni_y_pind] += np.array((loss.sum(axis=1))[uni_y_pind])[:, 0] # particle-phase reactions ------------------------- if (eqn_num[1] > 0): # empty array to hold relevant concentrations for # reaction rate coefficient calculation # tile aqueous-phase reaction rate coefficients rr_aq = np.tile(rrc[rindx.shape[0]::], num_asb) # prepare aqueous-phase concentrations rrc_y = np.ones((rindx_aq.shape[0] * rindx_aq.shape[1])) rrc_y[y_arr_aq] = y[y_rind_aq] rrc_y = rrc_y.reshape(rindx_aq.shape[0], rindx_aq.shape[1], order='C') # reaction rate (molecules/cc/s) rr = rr_aq * ((rrc_y**rstoi_aq).prod(axis=1)) # loss of reactants data = rr[rr_arr_aq] * rstoi_flat_aq # prepare loss values # convert to sparse matrix loss = SP.csc_matrix((data[0, :], y_rind_aq, reac_col_aq)) # register loss of reactants dd[uni_y_rind_aq] -= np.array((loss.sum(axis=1))[uni_y_rind_aq])[:, 0] # gain of products data = rr[rr_arr_p_aq] * pstoi_flat_aq # prepare loss values # convert to sparse matrix loss = SP.csc_matrix((data[0, :], y_pind_aq, prod_col_aq)) # register gain of products dd[uni_y_pind_aq] += np.array((loss.sum(axis=1))[uni_y_pind_aq])[:, 0] # gas-particle partitioning----------------- # transform particle phase concentrations into # size bins in rows, components in columns ymat = (y[num_comp:num_comp * (num_asb + 1)]).reshape( num_asb, num_comp) # total particle-phase concentration per size bin (molecules/cc (air)) csum = ((ymat.sum(axis=1) - ymat[:, seedi].sum(axis=1)) + ( (ymat[:, seedi] * core_diss).sum(axis=1)).reshape(-1)).reshape( -1, 1) # size bins with contents isb = (csum[:, 0] > 0.) # container for gas-phase concentrations at particle surface Csit = np.zeros((num_asb, num_comp)) # mole fraction of components at particle surface Csit[isb, :] = (ymat[isb, :] / csum[isb, :]) # gas-phase concentration of components at # particle surface (molecules/cc (air)) Csit[isb, :] = Csit[isb, :] * Psat[isb, :] * kelv_fac[isb] * act_coeff[ isb, :] # partitioning rate (molecules/cc/s) dd_all = kimt * (y[0:num_comp].reshape(1, -1) - Csit) # gas-phase change dd[0:num_comp] -= dd_all.sum(axis=0) # particle change dd[num_comp:num_comp * (num_asb + 1)] += (dd_all.flatten()) # gas-wall partitioning ---------------- # concentration on wall (molecules/cc (air)) Csit = y[num_comp * num_sb:num_comp * (num_sb + 1)] # saturation vapour pressure on wall (molecules/cc (air)) # note, just using the top rows of Psat and act_coeff # as do not need the repetitions over size bins if (Cw > 0.): Csit = Psat[0, :] * (Csit / Cw) * act_coeff[0, :] # rate of transfer (molecules/cc/s) dd_all = kw * (y[0:num_comp] - Csit) dd[0:num_comp] -= dd_all # gas-phase change dd[num_comp * num_sb:num_comp * (num_sb + 1)] += dd_all # wall change return (dd) def jac(t, y): # define the Jacobian # elements of sparse Jacobian matrix data = np.zeros((94)) for i in range(rindx.shape[0]): # gas-phase reaction loop # reaction rate (molecules/cc/s) rr = rrc[i] * (y[rindx[i, 0:nreac[i]]].prod()) # prepare Jacobian inputs jac_coeff = np.zeros((njac[i, 0])) # only fill Jacobian if reaction rate sufficient if (rr != 0.): jac_coeff = (rr * (jac_stoi[i, 0:njac[i, 0]]) / (y[jac_den_indx[i, 0:njac[i, 0]]])) data[jac_indx[i, 0:njac[i, 0]]] += jac_coeff n_aqr = nreac_aq.shape[0] # number of aqueous-phase reactions aqi = 0 # aqueous-phase reaction counter for i in range(rindx.shape[0], rrc.shape[0]): # aqueous-phase reaction loop # reaction rate (molecules/cc/s) rr = rrc[i] * (y[rindx_aq[aqi::n_aqr, 0:nreac_aq[aqi]]].prod(axis=1)) # spread along affected components rr = rr.reshape(-1, 1) rr = (np.tile(rr, int(njac_aq[aqi, 0] / (num_sb - wall_on)))).flatten(order='C') # prepare Jacobian inputs jac_coeff = np.zeros((njac_aq[aqi, 0])) nzi = (rr != 0) jac_coeff[nzi] = ( rr[nzi] * ((jac_stoi_aq[aqi, 0:njac_aq[aqi, 0]])[nzi]) / ((y[jac_den_indx_aq[aqi, 0:njac_aq[aqi, 0]]])[nzi])) # stack size bins jac_coeff = jac_coeff.reshape(int(num_sb - wall_on), int(njac_aq[aqi, 0] / (num_sb - wall_on)), order='C') data[jac_indx_aq[aqi::n_aqr, 0:(int(njac_aq[aqi, 0] / (num_sb - wall_on)))]] += jac_coeff aqi += 1 # gas-particle partitioning part_eff = np.zeros((56)) part_eff[0:24:3] = -kimt.sum(axis=0) # effect of gas on gas # transform particle phase concentrations into # size bins in rows, components in columns ymat = (y[num_comp:num_comp * (num_asb + 1)]).reshape( num_asb, num_comp) # total particle-phase concentration per size bin (molecules/cc (air)) csum = ymat.sum(axis=1) - ymat[:, seedi].sum( axis=1) + (ymat[:, seedi] * core_diss).sum(axis=1) # effect of particle on gas for isb in range(int(num_asb)): # size bin loop if csum[isb] > 0: # if particles present # effect of gas on particle part_eff[1 + isb:num_comp * (num_asb + 1):num_asb + 1] = +kimt[isb, :] # start and finish index sti = int((num_asb + 1) * num_comp + isb * (num_comp * 2)) fii = int(sti + (num_comp * 2.)) # diagonal index diag_indxg = sti + np.arange(0, num_comp * 2, 2).astype('int') diag_indxp = sti + np.arange(1, num_comp * 2, 2).astype('int') # prepare for diagonal (component effect on itself) diag = kimt[isb, :] * Psat[0, :] * act_coeff[0, :] * kelv_fac[ isb, 0] * (csum[isb] - ymat[isb, :]) / (csum[isb]**2.) # implement to part_eff part_eff[diag_indxg] = +diag part_eff[diag_indxp] = -diag data[jac_part_indx] += part_eff if (Cw > 0.): wall_eff = np.zeros((32)) wall_eff[0:16:2] = -kw # effect of gas on gas wall_eff[1:16:2] = +kw # effect of gas on wall # effect of wall on gas wall_eff[16:32:2] = +kw * (Psat[0, :] * act_coeff[0, :] / Cw) # effect of wall on wall wall_eff[16 + 1:32:2] = -kw * (Psat[0, :] * act_coeff[0, :] / Cw) data[jac_wall_indx] += wall_eff # create Jacobian j = SP.csc_matrix((data, rowvals, colptrs)) return (j) mod = Explicit_Problem(dydt, y) # instantiate solver mod.jac = jac # set the Jacobian mod_sim = CVode(mod) # define a solver instance # there is a general trend of an inverse relationship # between tolerances and computation time mod_sim.atol = 0.001 mod_sim.rtol = 0.0001 # integration approach (backward differentiation formula) mod_sim.discr = 'BDF' # call solver res_t, res = mod_sim.simulate(integ_step) # return concentrations following integration return (res, res_t)
def simulate(self, Tend, nIntervals, gridWidth): # define assimulo problem:(has to be done here because of the starting value in Explicit_Problem solver = Explicit_Problem(self.rhs, self.y0) ''' *******DELETE LATER ''''''''' # problem.handle_event = handle_event # problem.state_events = state_events # problem.init_mode = init_mode solver.handle_result = self.handle_result solver.name = 'Simple Explicit Example' simulation = CVode(solver) # Create a RungeKutta34 solver # simulation.inith = 0.1 #Sets the initial step, default = 0.01 # Change multistep method: 'adams' or 'VDF' if self.discr == 'Adams': simulation.discr = 'Adams' simulation.maxord = 12 else: simulation.discr = 'BDF' simulation.maxord = 5 # Change iteration algorithm: functional(FixedPoint) or newton if self.iter == 'FixedPoint': simulation.iter = 'FixedPoint' else: simulation.iter = 'Newton' # Sets additional parameters simulation.atol = self.atol simulation.rtol = self.rtol simulation.verbosity = 0 if hasattr(simulation, 'continuous_output'): simulation.continuous_output = False # default 0, if one step approach should be used elif hasattr(simulation, 'report_continuously'): simulation.report_continuously = False # default 0, if one step approach should be used # Create Solver and set settings # noRootFunctions = np.size(self.state_events(self.t0, np.array(self.y0))) # solver = sundials.CVodeSolver(RHS = self.f, ROOT = self.rootf, SW = [False]*noRootFunctions, # abstol = self.atol, reltol = self.rtol) # solver.settings.JAC = None #Add user-dependent jacobian here '''Initialize problem ''' # solver.init(self.t0, self.y0) self.handle_result(self.t0, self.y0) nextTimeEvent = self.time_events(self.t0, self.y0) self.t_cur = self.t0 self.y_cur = self.y0 state_event = False # # if gridWidth <> None: nOutputIntervals = int((Tend - self.t0) / gridWidth) else: nOutputIntervals = nIntervals # Define step length depending on if gridWidth or nIntervals has been chosen if nOutputIntervals > 0: # Last point on grid (does not have to be Tend:) if(gridWidth <> None): dOutput = gridWidth else: dOutput = (Tend - self.t0) / nIntervals else: dOutput = Tend outputStepCounter = long(1) nextOutputPoint = min(self.t0 + dOutput, Tend) while self.t_cur < Tend: # Time-Event detection and step time adjustment if nextTimeEvent is None or nextOutputPoint < nextTimeEvent: time_event = False self.t_cur = nextOutputPoint else: time_event = True self.t_cur = nextTimeEvent try: # #Integrator step # self.y_cur = solver.step(self.t_cur) # self.y_cur = np.array(self.y_cur) # state_event = False # Simulate # take a step to next output point: t_new, y_new = simulation.simulate(self.t_cur) # 5, 10) #5, 10 self.t_cur self.t_cur 2. argument nsteps Simulate 5 seconds # t_new, y_new are both vectors of the time and states at t_cur and all intermediate # points before it! So take last values: self.t_cur = t_new[-1] self.y_cur = y_new[-1] state_event = False except: import sys print "Unexpected error:", sys.exc_info()[0] # except CVodeRootException, info: # self.t_cur = info.t # self.y_cur = info.y # self.y_cur = np.array(self.y_cur) # time_event = False # state_event = True # # # Depending on events have been detected do different tasks if time_event or state_event: event_info = [state_event, time_event] if not self.handle_event(self, event_info): break solver.init(self.t_cur, self.y_cur) nextTimeEvent = self.time_events(self.t_cur, self.y_cur) # If no timeEvent happens: if nextTimeEvent <= self.t_cur: nextTimeEvent = None if self.t_cur == nextOutputPoint: # Write output if not happened before: if not time_event and not state_event: self.handle_result(nextOutputPoint, self.y_cur) outputStepCounter += 1 nextOutputPoint = min(self.t0 + outputStepCounter * dOutput, Tend) self.finalize()
def run_example(with_plots=True): r""" This is the same example from the Sundials package (cvsRoberts_FSA_dns.c) Its purpose is to demonstrate the use of parameters in the differential equation. This simple example problem for CVode, due to Robertson see http://www.dm.uniba.it/~testset/problems/rober.php, is from chemical kinetics, and consists of the system: .. math:: \dot y_1 &= -p_1 y_1 + p_2 y_2 y_3 \\ \dot y_2 &= p_1 y_1 - p_2 y_2 y_3 - p_3 y_2^2 \\ \dot y_3 &= p_3 y_ 2^2 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ def f(t, y, p): yd_0 = -p[0]*y[0]+p[1]*y[1]*y[2] yd_1 = p[0]*y[0]-p[1]*y[1]*y[2]-p[2]*y[1]**2 yd_2 = p[2]*y[1]**2 return N.array([yd_0,yd_1,yd_2]) def jac(t,y, p): J = N.array([[-p[0], p[1]*y[2], p[1]*y[1]], [p[0], -p[1]*y[2]-2*p[2]*y[1], -p[1]*y[1]], [0.0, 2*p[2]*y[1],0.0]]) return J def fsens(t, y, s, p): J = N.array([[-p[0], p[1]*y[2], p[1]*y[1]], [p[0], -p[1]*y[2]-2*p[2]*y[1], -p[1]*y[1]], [0.0, 2*p[2]*y[1],0.0]]) P = N.array([[-y[0],y[1]*y[2],0], [y[0], -y[1]*y[2], -y[1]**2], [0,0,y[1]**2]]) return J.dot(s)+P #The initial conditions y0 = [1.0,0.0,0.0] #Initial conditions for y #Create an Assimulo explicit problem exp_mod = Explicit_Problem(f,y0, name='Robertson Chemical Kinetics Example') exp_mod.rhs_sens = fsens exp_mod.jac = jac #Sets the options to the problem exp_mod.p0 = [0.040, 1.0e4, 3.0e7] #Initial conditions for parameters exp_mod.pbar = [0.040, 1.0e4, 3.0e7] #Create an Assimulo explicit solver (CVode) exp_sim = CVode(exp_mod) #Sets the solver paramters exp_sim.iter = 'Newton' exp_sim.discr = 'BDF' exp_sim.rtol = 1.e-4 exp_sim.atol = N.array([1.0e-8, 1.0e-14, 1.0e-6]) exp_sim.sensmethod = 'SIMULTANEOUS' #Defines the sensitvity method used exp_sim.suppress_sens = False #Dont suppress the sensitivity variables in the error test. exp_sim.report_continuously = True #Simulate t, y = exp_sim.simulate(4,400) #Simulate 4 seconds with 400 communication points #Basic test nose.tools.assert_almost_equal(y[-1][0], 9.05518032e-01, 4) nose.tools.assert_almost_equal(y[-1][1], 2.24046805e-05, 4) nose.tools.assert_almost_equal(y[-1][2], 9.44595637e-02, 4) nose.tools.assert_almost_equal(exp_sim.p_sol[0][-1][0], -1.8761, 2) #Values taken from the example in Sundials nose.tools.assert_almost_equal(exp_sim.p_sol[1][-1][0], 2.9614e-06, 8) nose.tools.assert_almost_equal(exp_sim.p_sol[2][-1][0], -4.9334e-10, 12) #Plot if with_plots: P.plot(t, y) P.title(exp_mod.name) P.xlabel('Time') P.ylabel('State') P.show() return exp_mod, exp_sim
def run_example(with_plots=True): r""" An example for CVode with scaled preconditioned GMRES method as a special linear solver. Note, how the operation Jacobian times vector is provided. ODE: .. math:: \dot y_1 &= y_2 \\ \dot y_2 &= -9.82 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ #Defines the rhs def f(t, y): yd_0 = y[1] yd_1 = -9.82 return N.array([yd_0, yd_1]) #Defines the Jacobian*vector product def jacv(t, y, fy, v): j = N.array([[0, 1.], [0, 0]]) return N.dot(j, v) y0 = [1.0, 0.0] #Initial conditions #Defines an Assimulo explicit problem exp_mod = Explicit_Problem( f, y0, name='Example using the Jacobian Vector product') exp_mod.jacv = jacv #Sets the Jacobian exp_sim = CVode(exp_mod) #Create a CVode solver #Set the parameters exp_sim.iter = 'Newton' #Default 'FixedPoint' exp_sim.discr = 'BDF' #Default 'Adams' exp_sim.atol = 1e-5 #Default 1e-6 exp_sim.rtol = 1e-5 #Default 1e-6 exp_sim.linear_solver = 'SPGMR' #Change linear solver #exp_sim.options["usejac"] = False #Simulate t, y = exp_sim.simulate( 5, 1000) #Simulate 5 seconds with 1000 communication points #Basic tests nose.tools.assert_almost_equal(y[-1][0], -121.75000000, 4) nose.tools.assert_almost_equal(y[-1][1], -49.100000000) #Plot if with_plots: P.plot(t, y) P.xlabel('Time') P.ylabel('State') P.title(exp_mod.name) P.show() return exp_mod, exp_sim
exp_mod = MyProblem( N, X, c_centered, 'ce only model, explicit CVode' ) #exp_mod = MyProblem( N, X, numpy.linspace(c_init-c_init/5.,c_init+c_init/5.,N), 'ce only model, explicit CVode' ) # Set the ODE solver exp_sim = CVode(exp_mod) #Create a CVode solver #Set the parameters exp_sim.iter = 'Newton' #Default 'FixedPoint' exp_sim.discr = 'BDF' #Default 'Adams' exp_sim.atol = 1e-5 #Default 1e-6 exp_sim.rtol = 1e-5 #Default 1e-6 #Simulate exp_mod.set_j_vec( 1.e-4 ) t1, y1 = exp_sim.simulate(100, 100) exp_mod.set_j_vec( 0.0 ) t2, y2 = exp_sim.simulate(200, 100) #exp_mod.set_j_vec( 0.0 ) #t3, y3 = exp_sim.simulate(10000, 200) #Plot #if with_plots: f, ax = plt.subplots(1,2) ax[0].plot(exp_mod.x_m*1e6,y1.T) #Plot the solution ax[1].plot(exp_mod.x_m*1e6,y2.T) #Plot the solution #ax[2].plot(exp_mod.x_m*1e6,y3.T) #Plot the solution #plt.xlabel('Cell Thickness [$\mu$m]') #plt.ylabel('State')
import matplotlib.pyplot as plt def rhs(t,y): k = 1000 L = k*(math.sqrt(y[0]**2+y[1]**2)-1)/math.sqrt(y[0]**2+y[1]**2) result = numpy.array([y[2],y[3],-y[0]*L,-y[1]*L-1]) return result t0 = 0 y0 = numpy.array([0.8,-0.8,0,0]) model = Explicit_Problem(rhs,y0,t0) model.name = 'task1' sim = CVode(model) sim.atol=numpy.array([1,1,1,1])*1e-5 sim.rtol=1e-6 sim.maxord=3 #sim.discr='BDF' #sim.iter='Newton' tfinal = 70 (t,y) = sim.simulate(tfinal) #sim.plot() plt.plot(y[:,0],y[:,1]) plt.axis('equal') plt.show()
def run_example(with_plots=True): r""" Example for demonstrating the use of a user supplied Jacobian ODE: .. math:: \dot y_1 &= y_2 \\ \dot y_2 &= -9.82 on return: - :dfn:`exp_mod` problem instance - :dfn:`exp_sim` solver instance """ #Defines the rhs def f(t, y): yd_0 = y[1] yd_1 = -9.82 return N.array([yd_0, yd_1]) #Defines the Jacobian def jac(t, y): j = N.array([[0, 1.], [0, 0]]) return j #Defines an Assimulo explicit problem y0 = [1.0, 0.0] #Initial conditions exp_mod = Explicit_Problem(f, y0, name='Example using analytic Jacobian') exp_mod.jac = jac #Sets the Jacobian exp_sim = CVode(exp_mod) #Create a CVode solver #Set the parameters exp_sim.iter = 'Newton' #Default 'FixedPoint' exp_sim.discr = 'BDF' #Default 'Adams' exp_sim.atol = 1e-5 #Default 1e-6 exp_sim.rtol = 1e-5 #Default 1e-6 #Simulate t, y = exp_sim.simulate( 5, 1000) #Simulate 5 seconds with 1000 communication points #Plot if with_plots: import pylab as P P.plot(t, y, linestyle="dashed", marker="o") #Plot the solution P.xlabel('Time') P.ylabel('State') P.title(exp_mod.name) P.show() #Basic tests nose.tools.assert_almost_equal(y[-1][0], -121.75000000, 4) nose.tools.assert_almost_equal(y[-1][1], -49.100000000) return exp_mod, exp_sim
# Initial conditions. The vector X0 is passed into the solver v0 = 45 theta = 30 theta = np.radians(theta) X0 = np.array([0, v0 * np.cos(theta), 1, v0 * np.sin(theta)]) # Time at start of simulation t0 = 0.0 # Create a model object with our equations and initial conditions model = Explicit_Problem(no_drag, X0, t0) # Bind event functions to model model.state_events = events model.handle_event = handle_event # Create simulation object sim = CVode(model) # Run simulation t, X = sim.simulate(5, 100) print(X.shape) #print(t[-1], X[-1, :]) # Plot results plt.plot(X[:, 0], X[:, 2], '.') plt.show()
def stiff_ode_solver(matrix, y_initial, forward_rate, rev_rate, third_body=None, iteration='Newton', discr='BDF', atol=1e-10, rtol=1e-6, sim_time=0.001, num_data_points=500): """ Sets up the initial condition for solving the odes Parameters ---------- matrix : ndarray stoichiometric matrix y_initial : list A list of initial concentrations forward_rate : list A list of forward reaction rates for all the reactions in the mechanism rev_rate : list A list of reverse reaction rates for all the reactions in the mechanism sim_time : float total time to simulate in seconds third_body : ndarray third body matrix, default = None iteration : str determines the iteration method that is be used by the solver, default='Newton' discr : determines the discretization method, default='BDF' atol : float absolute tolerance(s) that is to be used by the solver, default=1e-10 rtol : float relative tolerance that is to be used by the solver, default= 1e-7 num_data_points : integer number of even space data points in output arrays, default = 500 Returns ---------- t1 : list A list of time-points at which the system of ODEs is solved [t1, t2, t3,...] y1 : list of lists A list of concentrations of all the species at t1 time-points [[y1(t1), y2(t1),...], [y1(t2), y2(t2),...],...] """ # y0[0] = 0 # y0[0] = 0 # dydt = np.zeros((len(species_list)), dtype=float) # Define the rhs kf = forward_rate kr = rev_rate mat_reac = np.abs(np.asarray(np.where(matrix < 0, matrix, 0))) mat_prod = np.asarray(np.where(matrix > 0, matrix, 0)) # d = kf * np.prod(y0**np.abs(mat_reac), axis = 1) - kr * np.prod(y0**mat_prod, axis = 1) def rhs(t, concentration): # print(t) y = concentration if third_body is not None: third_body_eff = np.dot(third_body, y) third_body_eff = np.where(third_body_eff > 0, third_body_eff, 1) # print(third_body_eff) else: third_body_eff = np.ones(len(forward_rate)) # print(len(third_body_matrix)) rate_concentration = (kf * np.prod(np.power(y, mat_reac), axis=1) - kr * np.prod(np.power(y, mat_prod), axis=1)) dydt = np.dot( third_body_eff, np.multiply(matrix, rate_concentration.reshape(matrix.shape[0], 1))) # dydt = [np.sum(third_body_eff * (matrix[:, i] * rate_concentration)) for i in # range(len(species_list))] del t del y return dydt t0 = 0 # Define an Assimulo problem exp_mod = Explicit_Problem(rhs, y_initial, t0) # Define an explicit solver exp_sim = CVode(exp_mod) # Create a CVode solver # Sets the parameters exp_sim.iter = iteration # Default 'FixedPoint' exp_sim.discr = discr # Default 'Adams' exp_sim.atol = [atol] # Default 1e-6 exp_sim.rtol = rtol # Default 1e-6 exp_sim.maxh = 0.1 exp_sim.minh = 1e-18 exp_sim.num_threads = 1 while True: try: t1, y1 = exp_sim.simulate(sim_time, num_data_points) break except CVodeError: # reduce absolute error by two orders of magnitude # and try to solve again. print("next process started") atol = atol * 1e-2 exp_sim.atol = atol # if atol < 1e-15: # t1 = 0 # y1 = 0 # break # print(exp_sim.atol) return t1, y1