def update_dynamics(self): eqs = dict(self.rhs()) keys = set(eqs.keys()) symvars = [getattr(self, v) for v in self.vars] if keys <= set(self.nodes): # by node dep = flatten(zip(*symvars)) expr = flatten(sym.Matrix([eqs[node] for node in self])) elif keys <= set(self.vars): # by variable dep = it.chain.from_iterable(symvars) expr = flatten(sym.Matrix([eqs[v] for v in self.vars])) else: raise ValueError( "rhs must map either nodes to rhs or variables to rhs") dep_expr = [(d, e + Zero()) for d, e in zip(dep, expr)] if any(expr == sym.nan for _, expr in dep_expr): raise ValueError( "At least one rhs expression is NaN. Missing parameters?") self._sys = SymbolicSys(dep_expr, self.t) if self.use_native: self._native_sys = native_sys[self.integrator].from_other( self._sys) self._stale_dynamics = False
def main(y0='1,0', mu=1.0, tend=10., nt=50, savefig='None', plot=False, savetxt='None', integrator='scipy', dpi=100, kwargs='', verbose=False): assert nt > 1 y = sp.symarray('y', 2) p = sp.Symbol('p', real=True) f = [y[1], -y[0] + p * y[1] * (1 - y[0]**2)] odesys = SymbolicSys(zip(y, f), params=[p], names=True) tout = np.linspace(0, tend, nt) y0 = list(map(float, y0.split(','))) kwargs = dict(eval(kwargs) if kwargs else {}) xout, yout, info = odesys.integrate(tout, y0, [mu], integrator=integrator, **kwargs) if verbose: print(info) if savetxt != 'None': np.savetxt(stack_1d_on_left(xout, yout), savetxt) if plot: import matplotlib.pyplot as plt odesys.plot_result() plt.legend() if savefig != 'None': plt.savefig(savefig, dpi=dpi) else: plt.show()
def numerical_solver(left, right, y0, params=[], params_value=[], tstart=0., tend=10., nt=100, plot=False, savetxt='None', savefig='None', integrator='scipy', dpi=100, kwargs='', verbose=False): ''' Пояснение: y0 - начальные значения для переменных в виде строки 'X0,X1, ..., Xn' tstart, tend, nt - начало, конец и количество шагов по времени соответственно ''' ''' Zip группирует в соответствии левую и правую часть системы x, y = 'x', 'y' f = ['x**2 - y', 'y-y**3+x'] list(zip([x,y], f)) -> [('x', 'x**2 - y'), ('y', 'y-y**3+x')] ''' odesys = SymbolicSys(zip(left, right), params=params, names=True) ''' Создаем точки по t''' tout = np.linspace(tstart, tend, nt) ''' Преобразуем начальные условия ''' y0 = list(map(float, y0.split(','))) '''kwargs пока не нужен''' kwargs = dict(eval(kwargs) if kwargs else {}) ''' Интегрируем ''' xout, yout, info = odesys.integrate(tout, y0, params_value, integrator=integrator, **kwargs) return xout, yout, info if verbose: print(info) if savetxt != 'None': # stack_1d_on_left(xout, yout) -> [[t_0, x1_0, x2_0, x3_0], ... , [t_n, x1_n, x2_n, x3_n]] np.savetxt(savetxt, stack_1d_on_left(xout, yout)) if plot: odesys.plot_result() plt.legend() plt.figure(figsize=(20, 10)) if savefig != 'None': plt.savefig(savefig, dpi=dpi) else: plt.show()
def solve(self, initial_conditions, parameters): """ :param initial_conditions: :param parameters: :return: """ if not isinstance(initial_conditions, dict): raise TypeError('Input must be dict. Got "{}"'.format( type(initial_conditions))) if not isinstance(parameters, dict): raise TypeError('Input must be dict. Got "{}"'.format( type(parameters))) node_names = [i for i in self.ode.keys()] ode_list = [i for i in self.ode.values()] print(ode_list) NotImplementedError('Now make our new ODEs compatible with jitcode') SymbolicSys(ode_list)
def main(m=1, g=9.81, l=1, q1=.1, q2=.2, u1=0, u2=0, tend=10., nt=200, savefig='None', plot=False, savetxt='None', integrator='scipy', dpi=100, kwargs="", verbose=False): assert nt > 1 kwargs = dict(eval(kwargs) if kwargs else {}) odesys = SymbolicSys(get_equations(m, g, l)) tout = np.linspace(0, tend, nt) y0 = [q1, q2, u1, u2] xout, yout, info = odesys.integrate(tout, y0, integrator=integrator, **kwargs) if verbose: print(info) if savetxt != 'None': np.savetxt(stack_1d_on_left(xout, yout), savetxt) if plot: import matplotlib.pyplot as plt odesys.plot_result(xout, yout) if savefig != 'None': plt.savefig(savefig, dpi=dpi) else: plt.show()
def _create_odesys(rsys, substance_symbols=None, parameter_symbols=None, pretty_replace=lambda x: x, backend=None, SymbolicSys=None, time_symbol=None, unit_registry=None): """ This will be a simpler version of get_odesys without the unit handling code. The motivation is to reduce complexity (the code of get_odesys is long with multiple closures). This will also rely on SymPy explicitly and the user will be expected to deal with SymPy expressions. Only when this function has the same capabilities as get_odesys will it become and public API (along with a deprecation of get_odesys). Parameters ---------- rsys : ReactionSystem instance substance_symbols : OrderedDict If ``None``: ``rsys.substances`` will be used. parameter_symbols : OrderedDict backend : str or module Symbolic backend (e.g. sympy). The package ``sym`` is used as a wrapper. Returns ------- SymbolicSys (subclass of ``pyodesys.ODESys``) dict : - ``'symbols'``: dict mapping str to symbol. - ``'validate'``: callable acppeting a dictionary mapping str to quantities """ if backend is None: from sym import Backend backend = Backend(backend) if SymbolicSys is None: from pyodesys.symbolic import SymbolicSys if substance_symbols is None: substance_symbols = OrderedDict([(key, backend.Symbol(key)) for key in rsys.substances]) if isinstance(substance_symbols, OrderedDict): if list(substance_symbols) != list(rsys.substances): raise ValueError("substance_symbols needs to have same (oredered) keys as rsys.substances") if parameter_symbols is None: keys = [] for rxn in rsys.rxns: uk, = rxn.param.unique_keys keys.append(uk) for pk in rxn.param.parameter_keys: if pk not in keys: keys.append(pk) parameter_symbols = OrderedDict([(key, backend.Symbol(key)) for key in keys]) if not isinstance(parameter_symbols, OrderedDict): raise ValueError("parameter_symbols needs to be an OrderedDict") symbols = OrderedDict(chain(substance_symbols.items(), parameter_symbols.items())) symbols['time'] = time_symbol or backend.Symbol('t') if any(symbols['time'] == v for k, v in symbols.items() if k != 'time'): raise ValueError("time_symbol already in use (name clash?)") rates = rsys.rates(symbols) compo_vecs, compo_names = rsys.composition_balance_vectors() odesys = SymbolicSys( zip([substance_symbols[key] for key in rsys.substances], [rates[key] for key in rsys.substances]), symbols['time'], parameter_symbols.values(), names=list(rsys.substances.keys()), latex_names=[s.latex_name for s in rsys.substances.values()], param_names=parameter_symbols.keys(), latex_param_names=[pretty_replace(n) for n in parameter_symbols.keys()], linear_invariants=compo_vecs, linear_invariant_names=list(map(str, compo_names)), backend=backend, dep_by_name=True, par_by_name=True ) validate = partial(_validate, rsys=rsys, symbols=symbols, odesys=odesys, backend=backend) return odesys, { 'symbols': symbols, 'validate': validate, 'unit_aware_solve': _mk_unit_aware_solve(odesys, unit_registry, validate=validate) if unit_registry else None }
def _test_render_native_code_cse(NativeSys): # regression test taken from chempy from pyodesys.symbolic import SymbolicSys from sympy import symbols, log, exp import numpy as np symbs = symbols( 'N U A L NL t T He_dis Se_dis Cp_dis Tref_dis ' 'He_u Se_u Cp_u Tref_u Ha_agg Sa_agg Ha_as Sa_as Ha_f Sa_f ' 'R h k_B') di = {s.name: s for s in symbs} class NS: pass ns = NS() ns.__dict__.update(di) def _gibbs(H, S, Cp, Tref): H2 = H + Cp * (ns.T - Tref) S2 = S + Cp * log(ns.T / Tref) return exp(-(H2 - ns.T * S2) / (ns.R * ns.T)) def _eyring(H, S): return ns.k_B / ns.h * ns.T * exp(-(H - ns.T * S) / (ns.R * ns.T)) k_agg = _eyring(di['Ha_agg'], di['Sa_agg']) k_as = _eyring(di['Ha_as'], di['Sa_as']) k_f = _eyring(di['Ha_f'], di['Sa_f']) k_dis = k_as * _gibbs( *[di[k] for k in ('He_dis', 'Se_dis', 'Cp_dis', 'Tref_dis')]) k_u = k_f * _gibbs(*[di[k] for k in ('He_u', 'Se_u', 'Cp_u', 'Tref_u')]) r_agg = k_agg * ns.U r_as = k_as * ns.N * ns.L r_f = k_f * ns.U r_dis = k_dis * ns.NL r_u = k_u * ns.N exprs = [ -r_as + r_f + r_dis - r_u, -r_agg - r_f + r_u, r_agg, r_dis - r_as, r_as - r_dis ] def _solve(odesys, **kwargs): default_c0 = defaultdict(float, {'N': 1e-9, 'L': 1e-8}) params = dict( R=8.314472, # or N_A & k_B k_B=1.3806504e-23, h=6.62606896e-34, # k_B/h == 2.083664399411865e10 K**-1 * s**-1 He_dis=-45e3, Se_dis=-400, Cp_dis=1.78e3, Tref_dis=298.15, He_u=60e3, Se_u=130.5683, Cp_u=20.5e3, Tref_u=298.15, Ha_agg=106e3, Sa_agg=70, Ha_as=4e3, Sa_as=-10, Ha_f=90e3, Sa_f=50, T=50 + 273.15) return odesys.integrate(3600 * 24, [default_c0[s.name] for s in symbs[:5]], [params[s.name] for s in symbs[6:]], **kwargs) symbolic = SymbolicSys(zip(symbs[:5], exprs), symbs[5], params=symbs[6:]) kw = dict(integrator='cvode', nsteps=35000, atol=1e-11, rtol=1e-11) ref = _solve(symbolic, **kw) assert ref.info['success'] native = NativeSys.from_other( symbolic) # <-- regression test, optional: save_temp=True sol = _solve(native, **kw) assert sol.info['success'] assert np.allclose(sol.yout[-1, :], ref.yout[-1, :])
Derivative(alpha, t): eq['dot(alpha)'], alpha: eq['alpha']}).\ doit() eq['dot(theta)'][i] = eq['dot(theta)'][i].\ subs({nu[1]: eq['nu1'], nu[2]: eq['nu2'], Derivative(alpha, t): eq['dot(alpha)'], alpha: eq['alpha']}).\ doit() psi0, psi1, psi2, theta0, theta1, theta2, x, y, alpha = symbols('psi_0, psi_1, psi_2, theta_0, theta_1, theta_2, x, y, alpha') for i in range(3): eq['dot(psi)'][i] = subs_symbols(eq['dot(psi)'][i]) eq['dot(theta)'][i] = subs_symbols(eq['dot(theta)'][i]) left = [psi0, psi1, psi2, theta0, theta1, theta2] right = [eq['dot(psi)'][0], eq['dot(psi)'][1], eq['dot(psi)'][2], eq['dot(theta)'][0], eq['dot(theta)'][1], eq['dot(theta)'][2]] print(right) print('Количество совпадает:', len(left) == len(right)) odesys = SymbolicSys(list(zip(left, right)), t, [p, r, d]) xout, yout, info = odesys.integrate(t_end, initial_conditions, params, integrator='gsl', method='rk8pd', atol=1e-11, rtol=1e-12) return xout, yout, info, left, right
def setup(self): self.odesys = SymbolicSys(_get_equations(1, 9.81, 1)) self.tout = np.linspace(0, 10., 200) self.y0 = [.1, .2, 0, 0]
def _create_odesys( rsys, substance_symbols=None, parameter_symbols=None, pretty_replace=lambda x: x, backend=None, SymbolicSys=None, time_symbol=None, unit_registry=None, rates_kw=None, parameter_expressions=None, symbolic_kw=None, ): """This will be a simpler version of get_odesys without the unit handling code. The motivation is to reduce complexity (the code of get_odesys is long with multiple closures). This will also rely on SymPy explicitly and the user will be expected to deal with SymPy expressions. Only when this function has the same capabilities as get_odesys will it become and public API (along with a deprecation of get_odesys). Parameters ---------- rsys : ReactionSystem instance substance_symbols : OrderedDict If ``None``: ``rsys.substances`` will be used. parameter_symbols : OrderedDict backend : str or module Symbolic backend (e.g. sympy). The package ``sym`` is used as a wrapper. SymbolicSys: class See ``pyodesys`` for API. time_symbol : Symbol unit_registry : object e.g. ``chempy.units.SI_base_registry`` rates_kw : dict Keyword arguments passed to the ``rates`` method of rsys. parameter_expressions : dict Optional overrides. symbolic_kw : dict Keyword arguments passed on to SymbolicSys. Returns ------- SymbolicSys (subclass of ``pyodesys.ODESys``) dict : - ``'symbols'``: dict mapping str to symbol. - ``'validate'``: callable acppeting a dictionary mapping str to quantities """ if backend is None: from sym import Backend backend = Backend(backend) if SymbolicSys is None: from pyodesys.symbolic import SymbolicSys if substance_symbols is None: substance_symbols = OrderedDict([(key, backend.Symbol(key)) for key in rsys.substances]) if isinstance(substance_symbols, OrderedDict): if list(substance_symbols) != list(rsys.substances): raise ValueError( "substance_symbols needs to have same (oredered) keys as rsys.substances" ) if parameter_symbols is None: keys = [] for rxnpar in map(attrgetter("param"), rsys.rxns): if isinstance(rxnpar, str): if rxnpar in (parameter_expressions or {}): for pk in parameter_expressions[rxnpar].all_parameter_keys( ): keys.append(pk) else: keys.append(rxnpar) elif isinstance(rxnpar, Expr): keys.extend(rxnpar.all_unique_keys()) for pk in rxnpar.all_parameter_keys(): if pk not in keys: keys.append(pk) else: raise NotImplementedError("Unknown") if rates_kw and "cstr_fr_fc" in rates_kw: flowrate_volume, feed_conc = rates_kw["cstr_fr_fc"] keys.append(flowrate_volume) keys.extend(feed_conc.values()) assert all(sk in rsys.substances for sk in feed_conc) if len(keys) != len(set(keys)): raise ValueError("Duplicates in keys") parameter_symbols = OrderedDict([(key, backend.Symbol(key)) for key in keys]) if not isinstance(parameter_symbols, OrderedDict): raise ValueError("parameter_symbols needs to be an OrderedDict") symbols = OrderedDict( chain(substance_symbols.items(), parameter_symbols.items())) symbols["time"] = time_symbol or backend.Symbol("t") if any(symbols["time"] == v for k, v in symbols.items() if k != "time"): raise ValueError("time_symbol already in use (name clash?)") varbls = dict(symbols, **parameter_symbols) varbls.update(parameter_expressions or {}) rates = rsys.rates(varbls, **(rates_kw or {})) compo_vecs, compo_names = rsys.composition_balance_vectors() odesys = SymbolicSys( zip( [substance_symbols[key] for key in rsys.substances], [rates[key] for key in rsys.substances], ), symbols["time"], parameter_symbols.values(), names=list(rsys.substances.keys()), latex_names=[s.latex_name for s in rsys.substances.values()], param_names=parameter_symbols.keys(), latex_param_names=[ pretty_replace(n) for n in parameter_symbols.keys() ], linear_invariants=compo_vecs, linear_invariant_names=list(map(str, compo_names)), backend=backend, dep_by_name=True, par_by_name=True, **(symbolic_kw or {})) validate = partial(_validate, rsys=rsys, symbols=symbols, odesys=odesys, backend=backend) return odesys, { "symbols": symbols, "validate": validate, "unit_aware_solve": _mk_unit_aware_solve(odesys, unit_registry, validate=validate) if unit_registry else None, }