def _create_arc_integrator(self, trans_duration=3, res=100, pulse_res=20): """ Create integrator and simulator objects for later use. trans_duration is number of periods to simulate perturbed trajectory and reference, res is the resolution (per period) of the output trajectories """ # Use parameterized period so the integration length can be # controlled without re-initializing self.arcint = cs.CVodesIntegrator(self.modlT) self.arcint.setOption('abstol', self.intoptions['int_abstol']) self.arcint.setOption('reltol', self.intoptions['int_reltol']) self.arcint.setOption('tf', 1.) self.arcint.init() # # Simulate the perturbed trajectory for trans_duration. tf = 2*np.pi*trans_duration traj_res = int(res*trans_duration) self.arc_traj_ts = np.linspace(0, tf, num=traj_res, endpoint=True) self.arcsim = cs.Simulator(self.arcint, self.arc_traj_ts/tf) self.arcsim.init() self.pulsesim = cs.Simulator(self.arcint, np.linspace(0, 1., num=pulse_res, endpoint=True)) self.pulsesim.init()
def solve_ode(self): """ Solve the ODE using casadi's CVODES wrapper to ensure that the collocated dynamics match the error-controlled dynamics of the ODE """ self.ts.sort() # Assert ts is increasing f_integrator = cs.SXFunction( 'ode', cs.daeIn(t=self.dxdt.inputExpr(0), x=self.dxdt.inputExpr(1), p=self.dxdt.inputExpr(2)), cs.daeOut(ode=self.dxdt.outputExpr(0))) integrator = cs.Integrator('int', 'cvodes', f_integrator) simulator = cs.Simulator('sim', integrator, self.ts) simulator.setInput(self.sol[0], 'x0') simulator.setInput(self.var.p_op, 'p') simulator.evaluate() x_sim = self.sol_sim = np.array(simulator.getOutput()).T err = ((self.sol - x_sim).mean(0) / (self.sol.mean(0))).mean() if err > 1E-3: warn('Collocation does not match ODE Solution: \ {:.2%} Error'.format(err))
def limit_cycle(self): """ integrate the solution for one period, remembering each of time points along the way """ self.ts = np.linspace(0, self.T, self.intoptions['lc_res']) intlc = cs.Integrator('cvodes',self.model) intlc.setOption("abstol" , self.intoptions['lc_abstol']) intlc.setOption("reltol" , self.intoptions['lc_reltol']) intlc.setOption("max_num_steps", self.intoptions['lc_maxnumsteps']) intlc.setOption("tf" , self.T) intsim = cs.Simulator(intlc, self.ts) intsim.init() # Input Arguments intsim.setInput(self.y0, cs.INTEGRATOR_X0) intsim.setInput(self.param, cs.INTEGRATOR_P) intsim.evaluate() self.sol = intsim.output().toArray().T # create interpolation object self.lc = self.interp_sol(self.ts, self.sol.T)
def int_odes(self, tf, y0=None, numsteps=10000, return_endpt=False, ts=0): """ This function integrates the ODEs until well past the transients. This uses Casadi's simulator class, C++ wrapped in swig. Inputs: tf - the final time of integration. numsteps - the number of steps in the integration is the second argument """ if y0 is None: y0 = self.y0 self.integrator = cs.Integrator('cvodes', self.model) #Set up the tolerances etc. self.integrator.setOption("abstol", self.intoptions['int_abstol']) self.integrator.setOption("reltol", self.intoptions['int_reltol']) self.integrator.setOption("max_num_steps", self.intoptions['int_maxstepcount']) self.integrator.setOption("tf", tf) #Let's integrate self.integrator.init() self.ts = np.linspace(ts, tf, numsteps, endpoint=True) self.simulator = cs.Simulator(self.integrator, self.ts) self.simulator.init() self.simulator.setInput(y0, cs.INTEGRATOR_X0) self.simulator.setInput(self.param, cs.INTEGRATOR_P) self.simulator.evaluate() sol = self.simulator.output().toArray().T if return_endpt == True: return sol[-1] else: return sol
def _single_pulse_comparison_state(self, state, phase, amount, trans_duration): """ Compares a single perturbation of state to a reference trajectory """ base = self.base_class # Use parameterized period so the integration length can be # controlled without re-initializing self.arcint = cs.CVodesIntegrator(base.modlT) self.arcint.setOption('abstol', self.int_opt['int_abstol']) self.arcint.setOption('reltol', self.int_opt['int_reltol']) self.arcint.setOption('tf', 1.) self.arcint.init() # Find y0 at start of pulse tstart = phase * base.y0[-1] / (2 * np.pi) y0 = base.lc(tstart) y0[state] += amount # Simulate the perturbed trajectory for trans_duration. tf = base.y0[-1] * trans_duration ts = np.linspace(0, tf, num=int(100 * trans_duration), endpoint=True) self.arcsim = cs.Simulator(self.arcint, ts / tf) self.arcsim.init() self.arcsim.setInput(y0, cs.INTEGRATOR_X0) self.arcsim.setInput(list(base.paramset) + [tf], cs.INTEGRATOR_P) self.arcsim.evaluate() trajectory = self.arcsim.output().toArray() yend = trajectory[-1] tend = ts[-1] % base.y0[-1] + tstart def resy(t): return np.linalg.norm(yend - base.lc(t % base.y0[-1])) # Minimize resy(t) tvals = np.linspace(0, base.y0[-1], num=25) tguess = tvals[np.array([resy(t) for t in tvals]).argmin()] tmin = sc.optimize.fmin(resy, tguess, disp=0)[0] % base.y0[-1] assert resy(tmin) / base.NEQ < 1E-3, "transient not converged" if tmin > base.y0[-1] / 2: tmin += -base.y0[-1] tdiff = tmin - tend # rescale tdiff from -T/2 to T/2 tdiff = tdiff % base.y0[-1] if tdiff > base.y0[-1] / 2: tdiff += -base.y0[-1] reference = base.lc((ts + tend + tdiff) % base.y0[-1]) # from scipy.integrate import cumtrapz # amp_change = cumtrapz((trajectory - reference).T, x=ts)[:,-1] return trajectory, reference, tdiff
def _create_arc_integrator(self, trans_duration=3): """ Create integrator and simulator objects for later use """ # Use parameterized period so the integration length can be # controlled without re-initializing self.arcint = cs.CVodesIntegrator(self.modlT) self.arcint.setOption('abstol', self.intoptions['int_abstol']) self.arcint.setOption('reltol', self.intoptions['int_reltol']) self.arcint.setOption('tf', 1.) self.arcint.init() # # Simulate the perturbed trajectory for trans_duration. tf = self.y0[-1] * trans_duration self.arc_traj_ts = np.linspace(0, tf, num=int(100 * trans_duration), endpoint=True) self.arcsim = cs.Simulator(self.arcint, self.arc_traj_ts / tf) self.arcsim.init()
def fwd_simulation(self, dE_start, condition, detail=True, reltol=1e-6, abstol=1e-8): Tem = condition.Temperature time = condition.TimeGrid opts = {} opts['abstol'] = abstol opts['reltol'] = reltol opts['disable_internal_warnings'] = True opts['max_num_steps'] = 1e5 P_dae = np.hstack([dE_start, Tem]) # Partial Pressure Pinlet = np.zeros(self.ngas) for idx, spe in enumerate(self.specieslist): if spe.phase == 'gaseous': Pinlet[idx] = condition.PartialPressure[str(spe)] x0 = Pinlet.tolist() + [0] * (self.nsurf - 1) + [1] # print(x0) Fint = cas.Integrator('Fint', 'cvodes', self._dae_, opts) Fsim = cas.Simulator('Fsim', Fint, time) Fsim.setInput(x0, 'x0') Fsim.setInput(P_dae, 'p') Fsim.evaluate() # Evalu out = Fsim.getOutput().full() tor_list = {} for i, spe in enumerate(self.specieslist): if spe.phase == 'gaseous': nt = int(condition.Ntime / 2) slope, intercept, r_value, p_value, std_err = stats.linregress( condition.TimeGrid[nt:], out[i, nt:]) tor_list[spe.name] = slope return out, tor_list
def _simulate(self, ts, y0=None, paramset=None): """ Simulate the class, outputing the solution at the times specified by ts. Optional inputs of y0 and paramsets to use ones other than those currently in the class """ if y0 is None: y0 = self.y0[:-1] if paramset is None: paramset = self.paramset int = cs.CVodesIntegrator(self.model) int.setOption("abstol" , self.intoptions['lc_abstol']) int.setOption("reltol" , self.intoptions['lc_reltol']) int.setOption("max_num_steps", self.intoptions['lc_maxnumsteps']) int.setOption("tf" , self.y0[-1]) sim = cs.Simulator(int, ts) sim.init() # Input Arguments sim.setInput(y0, cs.INTEGRATOR_X0) sim.setInput(paramset, cs.INTEGRATOR_P) sim.evaluate() return sim.output().toArray()
def fwd_simulation(self, dE_start, condition, detail=True, reltol=1e-6, abstol=1e-8): # reevaluate the reaction condition condition._calSim() condition._calGrid() time = condition.TimeGrid T0 = condition.T0 beta = condition.Beta opts = {} opts['abstol'] = abstol opts['reltol'] = reltol opts['disable_internal_warnings'] = True opts['max_num_steps'] = 1e5 x0 = self.init_condition(condition) P_dae = np.hstack([dE_start, T0, beta]) # print(x0) # print(P_dae) # print(time) # opts['tf'] = 2 # Fint = cas.Integrator('Fint', 'cvodes', self._dae_, opts) # F_sim = Fint(x0=x0, p=P_dae) Fint = cas.Integrator('Fint', 'cvodes', self._dae_, opts) Fsim = cas.Simulator('Fsim', Fint, time) Fsim.setInput(x0, 'x0') Fsim.setInput(P_dae, 'p') Fsim.evaluate() # Evaluate out = Fsim.getOutput().full() out[:self.ngas, :] *= self.pump_ratio return out
def eval_likeli(self, dE, conditionlist, evidence_info={}): reltol = evidence_info.get('reltol', 1e-12) abstol = evidence_info.get('abstol', 1e-12) err = evidence_info.get('peak_err', 10) opts = {} opts['abstol'] = abstol opts['reltol'] = reltol opts['disable_internal_warnings'] = True opts['max_num_steps'] = 1e5 # Initialize simulator evidence = 0 for condition in conditionlist: time = condition.TimeGrid T0 = condition.T0 beta = condition.Beta x0 = self.init_condition(condition) P_dae = np.hstack([dE, T0, beta]) Fint = cas.Integrator('Fint', 'cvodes', self._dae_, opts) Fsim = cas.Simulator('Fsim', Fint, time) Fsim.setInput(x0, 'x0') Fsim.setInput(P_dae, 'p') Fsim.evaluate() out = Fsim.getOutput().full() # Find the peak for spe, peak_exp in condition.PeakPosition.items(): idx = get_index_species(spe, self.specieslist) des = out[idx, :] idx_peak = np.argmax(des) peak_sim = condition.TemGrid[idx_peak] dev = peak_sim - peak_exp evidence += (dev * dev) / err**2 return -evidence
def solve_ode(self): """ Solve the ODE using casadi's CVODES wrapper to ensure that the collocated dynamics match the error-controlled dynamics of the ODE """ f_integrator = cs.SXFunction( 'ode', cs.daeIn(t=self.model.inputExpr(0), x=self.model.inputExpr(1), p=self.model.inputExpr(2)), cs.daeOut(ode=self.model.outputExpr(0))) integrator = cs.Integrator('int', 'cvodes', f_integrator) simulator = cs.Simulator('sim', integrator, self._tgrid) simulator.setInput(self._output['x_opt'][0], 'x0') simulator.setInput(self._output['p_opt'], 'p') simulator.evaluate() x_sim = self._output['x_sim'] = np.array(simulator.getOutput()).T err = ((self._output['x_opt'] - x_sim).mean(0) / (self._output['x_opt'].mean(0))).mean() if err > 1E-3: warn('Collocation does not match ODE Solution: \ {:.2f}% Error'.format(100 * err))
def Integrate(self, t, sim=True, res=500, pars=None): """ Integrate the forced oscillator from t = 0 to t. sim=True, return vector of values. """ # Get parameters over the limit cycle if pars is None: pars = np.array([self._get_paramset(tj) for tj in self.tgrid_u]).flatten() apars = pars.reshape((self.nk, self.NP)) y = self.y0 integrator = cs.CVodesIntegrator(self.model) integrator.setOption('abstol', self.int_opt['int_abstol']) integrator.setOption('reltol', self.int_opt['int_reltol']) integrator.setOption('tf', self.h) integrator.init() t_i = 0 if not sim: while t_i < t - self.h + 1E-5: element_index = int((t_i / self.h) % self.nk) integrator.setInput(y, cs.INTEGRATOR_X0) integrator.setInput(apars[element_index], cs.INTEGRATOR_P) integrator.evaluate() y = integrator.output() t_i += self.h if t - t_i > 1E-5: element_index = int((t_i / self.h) % self.nk) integrator.setOption('tf', t - t_i) integrator.init() integrator.setInput(y, cs.INTEGRATOR_X0) integrator.setInput(apars[element_index], cs.INTEGRATOR_P) integrator.evaluate() y = integrator.output() return t, y else: # Allow for specific ts requests in res try: ts = np.linspace(0, t, res) except TypeError: ts = res sol = np.zeros((0, self.NEQ)) while t_i < t - self.h + 1E-5: element_index = int((t_i / self.h) % self.nk) ts_i = ts[np.all([ts < t_i + self.h, ts >= t_i], 0)] - t_i sim_i = cs.Simulator(integrator, np.hstack([0, ts_i, self.h])) sim_i.init() sim_i.setInput(y, cs.INTEGRATOR_X0) sim_i.setInput(apars[element_index], cs.INTEGRATOR_P) sim_i.evaluate() out = sim_i.output().toArray() sol = np.vstack([sol, out[1:-1]]) y = out[-1] t_i += self.h if t - t_i > 1E-5: element_index = int((t_i / self.h) % self.nk) ts_i = ts[np.all([ts < t_i + self.h, ts >= t_i], 0)] - t_i sim_i = cs.Simulator(integrator, np.hstack([0, ts_i, t - t_i])) sim_i.init() sim_i.setInput(y, cs.INTEGRATOR_X0) sim_i.setInput(apars[element_index], cs.INTEGRATOR_P) sim_i.evaluate() out = sim_i.output().toArray() sol = np.vstack([sol, out[1:-1]]) y = out[-1] # Close enough. elif len(sol) == len(ts) - 1: sol = np.vstack([sol, y]) return ts, sol
def _single_pulse_comparison(self, param, phase, amount, pulse_duration, trans_duration): """ Compares a single perturbation to a reference trajectory """ base = self.base_class try: par_ind = self.base_class.pdict[param] except KeyError: par_ind = int(param) # Use parameterized period so the integration length can be # controlled without re-initializing self.arcint = cs.CVodesIntegrator(base.modlT) self.arcint.setOption('abstol', self.int_opt['int_abstol']) self.arcint.setOption('reltol', self.int_opt['int_reltol']) self.arcint.setOption('tf', 1.) self.arcint.init() # Find y0 at start of pulse tstart = phase * base.y0[-1] / (2 * np.pi) tpulse = pulse_duration * base.y0[-1] / (2 * np.pi) y0 = base.lc(tstart) # Integrate trajectory through pulse # Find parameter set for pulse param_init = np.array(self.paramset) param_init[par_ind] *= (1 + amount) self.arcint.setInput(y0, cs.INTEGRATOR_X0) self.arcint.setInput(param_init.tolist() + [tpulse], cs.INTEGRATOR_P) self.arcint.evaluate() yf = np.array(self.arcint.output()) # Simulate the perturbed trajectory for trans_duration. tf = base.y0[-1] * trans_duration ts = np.linspace(0, tf, num=int(100 * trans_duration), endpoint=True) self.arcsim = cs.Simulator(self.arcint, ts / tf) self.arcsim.init() self.arcsim.setInput(yf, cs.INTEGRATOR_X0) self.arcsim.setInput(list(base.paramset) + [tf], cs.INTEGRATOR_P) self.arcsim.evaluate() trajectory = self.arcsim.output().toArray() yend = trajectory[-1] tend = ts[-1] % base.y0[-1] def resy(t): return np.linalg.norm(yend - base.lc(t % base.y0[-1])) # Minimize resy(t) tvals = np.linspace(0, base.y0[-1], num=25) tguess = tvals[np.array([resy(t) for t in tvals]).argmin()] tmin = sc.optimize.fmin(resy, tguess, disp=0)[0] % base.y0[-1] assert resy(tmin) / base.NEQ < 1E-3, "transient not converged" tdiff = tmin - tend reference = base.lc((ts + tdiff) % base.y0[-1]) from scipy.integrate import cumtrapz amp_change = cumtrapz((trajectory - reference).T, x=ts)[:, -1] return amp_change
def calcdSdp(self, res=50): """ Function to calculate the sensitivity state profile shapes to parameter perturbations, from wilkins. Might want to move this to a new class. """ ts = np.linspace(0, self.y0[-1], res, endpoint=True) integrator = cs.CVodesIntegrator(self.model) integrator.setOption("abstol", self.intoptions['sensabstol']) integrator.setOption("reltol", self.intoptions['sensreltol']) integrator.setOption("max_num_steps", self.intoptions['sensmaxnumsteps']) integrator.setOption("sensitivity_method", self.intoptions['sensmethod']); integrator.setOption("t0", 0) integrator.setOption("tf", self.y0[-1]) integrator.setOption("numeric_jacobian", True) integrator.setOption("number_of_fwd_dir", self.NP) integrator.setOption("fsens_err_con", 1) integrator.setOption("fsens_abstol", self.intoptions['sensabstol']) integrator.setOption("fsens_reltol", self.intoptions['sensreltol']) integrator.init() integrator.setInput(self.y0[:-1],cs.INTEGRATOR_X0) integrator.setInput(self.paramset,cs.INTEGRATOR_P) p0_seed = np.eye(self.NP) for i in range(0,self.NP): integrator.setFwdSeed(p0_seed[i],cs.INTEGRATOR_P,i) for i in range(0,self.NP): integrator.setFwdSeed(self.S0[:,i],cs.INTEGRATOR_X0,i) sim = cs.Simulator(integrator, ts) sim.setOption("number_of_fwd_dir", self.NP) # sim.setOption("fsens_err_con", 1) # sim.setOption("fsens_abstol", self.intoptions['sensabstol']) # sim.setOption("fsens_reltol", self.intoptions['sensreltol']) sim.init() sim.setInput(self.y0[:-1],cs.INTEGRATOR_X0) sim.setInput(self.paramset,cs.INTEGRATOR_P) p0_seed = np.eye(self.NP) for i in range(0,self.NP): sim.setFwdSeed(p0_seed[i],cs.INTEGRATOR_P,i) for i in range(0,self.NP): sim.setFwdSeed(self.S0[:,i],cs.INTEGRATOR_X0,i) sim.evaluate(nfdir=self.NP) # Raw sensitivity matrix S, calculated with initial conditions # S_0 = Z[0]. This matrix is not periodic, and will grow # unbounded with time S = np.array([sim.fwdSens(cs.INTEGRATOR_X0, i).toArray() for i in xrange(self.NP)]) S = S.swapaxes(0,1).swapaxes(1,2) # S[t,y,p] y = sim.output().toArray() # Periodic Z matrix, defined as the state sensitivity with # constant period (Wilkins 2009, page 2710) Z = np.zeros(S.shape) for i in xrange(res): Z[i] = S[i] + (ts[i]/self.y0[-1])*np.outer(self.dydt(y[i]), self.dTdp) self.Z = Z # Periodic W matrix, a projection of the Z matrix. Captures the # change in amplitude of the state trajectory without taking # into account changes in phase. W = np.zeros(S.shape) for i, (y_i, Z_i) in enumerate(zip(y,Z)): W[i] = (np.eye(self.NEQ) - np.outer(y_i, y_i)/np.linalg.norm(y_i)**2).dot(Z_i) self.W = W