Example #1
0
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()
Example #2
0
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()
Example #3
0
    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
Example #4
0
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()
Example #5
0
def test_NativeSys__PartiallySolvedSystem__roots(idx):
    def f(x, y, p):
        return [-p[0] * y[0], p[0] * y[0] - p[1] * y[1], p[1] * y[1]]

    def roots(x, y):
        return ([y[0] - y[1]], [y[0] - y[2]], [y[1] - y[2]])[idx]

    odesys = SymbolicSys.from_callback(f, 3, 2, roots_cb=roots)
    _p, _q, tend = 7, 3, 0.7
    dep0 = (1, 0, 0)
    ref = [0.11299628093544488, 0.20674119231833346, 0.3541828705348678]

    def check(odesys):
        res = odesys.integrate(tend,
                               dep0, (_p, _q),
                               integrator='cvode',
                               return_on_root=True)
        assert abs(res.xout[-1] - ref[idx]) < 1e-7

    check(odesys)
    native = NativeSys.from_other(odesys)
    check(native)

    psys = PartiallySolvedSystem(
        odesys, lambda t0, xyz, par0, be:
        {odesys.dep[0]: xyz[0] * be.exp(-par0[0] * (odesys.indep - t0))})
    check(psys)
    pnative = NativeSys.from_other(psys)
    check(pnative)
Example #6
0
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()
Example #7
0
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()
Example #8
0
class TimeDoublePendulum:
    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 time_integrate_scipy(self):
        self.odesys.integrate(self.tout, self.y0, integrator='scipy')

    def time_integrate_gsl(self):
        self.odesys.integrate(self.tout, self.y0, integrator='gsl')

    def time_integrate_odeint(self):
        self.odesys.integrate(self.tout, self.y0, integrator='odeint')

    def time_integrate_cvode(self):
        self.odesys.integrate(self.tout, self.y0, integrator='cvode')
Example #9
0
class TimeDoublePendulum:

    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 time_integrate_scipy(self):
        self.odesys.integrate(self.tout, self.y0, integrator='scipy')

    def time_integrate_gsl(self):
        self.odesys.integrate(self.tout, self.y0, integrator='gsl')

    def time_integrate_odeint(self):
        self.odesys.integrate(self.tout, self.y0, integrator='odeint')

    def time_integrate_cvode(self):
        self.odesys.integrate(self.tout, self.y0, integrator='cvode')
Example #10
0
    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)
Example #11
0
File: ode.py Project: adelq/chempy
def get_odesys(rsys, include_params=True, substitutions=None,
               SymbolicSys=None,
               unit_registry=None, output_conc_unit=None,
               output_time_unit=None, **kwargs):
    """ Creates a :class:`pyneqsys.SymbolicSys` from a :class:`ReactionSystem`

    Parameters
    ----------
    rsys : ReactionSystem
        note that if :attr:`param` if not RateExpr (or convertible to one through
        :meth:`_as_RateExpr`) it will be used to construct a :class:`MassAction` instance.
    include_params : bool (default: False)
        whether rate constants should be included into the rate expressions or
        left as free parameters in the :class:`pyneqsys.SymbolicSys` instance.
    substitutions : dict, optional
        variable substitutions used by rate expressions (in respective Reaction.param).
        values are allowed to be tuple like: (new_vars, callback)
    SymbolicSys : class (optional)
        default : :class:`pyneqsys.SymbolicSys`
    unit_registry: dict (optional)
        see :func:`chempy.units.get_derived_units`
    output_conc_unit : unit (Optional)
    output_time_unit : unit (Optional)
    \*\*kwargs :
        Keyword arguemnts pass on to `SymbolicSys`

    Returns
    -------
    pyodesys.symbolic.SymbolicSys
    param_keys
    unique_keys
    p_units

    Examples
    --------
    >>> from chempy import Equilibrium, ReactionSystem
    >>> eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, 10**2)
    >>> substances = 'Fe+3 SCN- FeSCN+2'.split()
    >>> rsys = ReactionSystem(eq.as_reactions(kf=3.0), substances)
    >>> odesys = get_odesys(rsys)[0]
    >>> init_conc = {'Fe+3': 1.0, 'SCN-': .3, 'FeSCN+2': 0}
    >>> tout, Cout, info = odesys.integrate(5, init_conc)
    >>> Cout[-1, :].round(4)
    array([ 0.7042,  0.0042,  0.2958])

    """
    if SymbolicSys is None:
        from pyodesys.symbolic import SymbolicSys

    substance_keys = list(rsys.substances.keys())

    if 'names' not in kwargs:
        kwargs['names'] = list(rsys.substances.values())  # pyodesys>=0.5.3

    r_exprs = [rxn.rate_expr() for rxn in rsys.rxns]

    _original_param_keys = set.union(*(set(ratex.parameter_keys) for ratex in r_exprs))
    _from_subst = set()
    _active_subst = {}
    _passive_subst = {}
    substitutions = substitutions or {}
    for key, v in substitutions.items():
        if key not in _original_param_keys:
            raise ValueError("Substitution: '%s' does not appear in any rate expressions.")
        if isinstance(v, Expr):
            _from_subst.update(v.parameter_keys)
            _active_subst[key] = v
        else:
            _passive_subst[key] = v
    param_keys = list(filter(lambda x: x not in substitutions, _original_param_keys.union(_from_subst)))

    unique_keys = []
    p_defaults = []
    if not include_params:
        for ratex in r_exprs:
            if ratex.unique_keys is not None:
                unique_keys.extend(ratex.unique_keys)
                p_defaults.extend(ratex.args)
    if unit_registry is None:
        def pre_processor(x, y, p):
            return (
                x,
                rsys.as_per_substance_array(y),
                [p[k] for k in param_keys] + [p[k] for k in unique_keys]
            )

        def post_processor(x, y, p):
            return (
                x,
                y,  # dict(zip(substance_keys, y)),
                dict(zip(param_keys+unique_keys, p))
            )
        p_units = [None]*(len(param_keys) + len(unique_keys))
    else:
        # We need to make rsys_params unitless and create
        # a pre- & post-processor for SymbolicSys
        p_units = [get_derived_unit(unit_registry, k) for k in param_keys]
        new_r_exprs = []
        for ratex in r_exprs:
            _pu, _new_rate = ratex._recursive_as_RateExpr().dedimensionalisation(unit_registry)
            p_units.extend(_pu)
            new_r_exprs.append(_new_rate)
        r_exprs = new_r_exprs

        time_unit = get_derived_unit(unit_registry, 'time')
        conc_unit = get_derived_unit(unit_registry, 'concentration')

        def pre_processor(x, y, p):
            return (
                to_unitless(x, time_unit),
                rsys.as_per_substance_array(to_unitless(y, conc_unit)),
                [to_unitless(p[k], p_unit) for k, p_unit in zip(chain(param_keys, unique_keys), p_units)]
            )

        def post_processor(x, y, p):
            time = x*time_unit
            if output_time_unit is not None:
                time = time.rescale(output_time_unit)
            conc = y*conc_unit
            if output_conc_unit is not None:
                conc = conc.rescale(output_conc_unit)
            return time, conc, [elem*p_unit for elem, p_unit in zip(p, p_units)]

    kwargs['pre_processors'] = [pre_processor] + kwargs.get('pre_processors', [])
    kwargs['post_processors'] = kwargs.get('post_processors', []) + [post_processor]

    def dydt(t, y, p, backend=math):
        variables = dict(chain(
            zip(substance_keys, y),
            zip(param_keys, p[:len(param_keys)]),
            zip(unique_keys, p[len(param_keys):])
        ))
        for k, act in _active_subst.items():
            if unit_registry is not None:
                _, act = act.dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return dCdt(rsys, [rat(variables, backend=backend) for rat in r_exprs])

    return SymbolicSys.from_callback(
        dydt, len(substance_keys),
        len(param_keys) + (0 if include_params else len(unique_keys)),
        **kwargs), param_keys, unique_keys, p_units
Example #12
0
def get_odesys(rsys, include_params=True, substitutions=None, SymbolicSys=None, unit_registry=None,
               output_conc_unit=None, output_time_unit=None, cstr=False, constants=None, **kwargs):
    """ Creates a :class:`pyneqsys.SymbolicSys` from a :class:`ReactionSystem`

    The parameters passed to RateExpr will contain the key ``'time'`` corresponding to the
    independent variable of the IVP.

    Parameters
    ----------
    rsys : ReactionSystem
        Each reaction of ``rsys`` will have their :meth:`Reaction.rate_expr()` invoked.
        Note that if :attr:`Reaction.param` is not a :class:`RateExpr` (or convertible to
        one through :meth:`as_RateExpr`) it will be used to construct a :class:`MassAction`
        instance.
    include_params : bool (default: True)
        Whether rate constants should be included into the rate expressions or
        left as free parameters in the :class:`pyneqsys.SymbolicSys` instance.
    substitutions : dict, optional
        Variable substitutions used by rate expressions (in respective Reaction.param).
        values are allowed to be values of instances of :class:`Expr`.
    SymbolicSys : class (optional)
        Default : :class:`pyneqsys.SymbolicSys`.
    unit_registry: dict (optional)
        See :func:`chempy.units.get_derived_units`.
    output_conc_unit : unit (Optional)
    output_time_unit : unit (Optional)
    cstr : bool
        Generate expressions for continuously stirred tank reactor.
    constants : module
        e.g. ``chempy.units.default_constants``, parameter keys not found in
        substitutions will be looked for as an attribute of ``constants`` when provided.
    \\*\\*kwargs :
        Keyword arguemnts passed on to `SymbolicSys`.

    Returns
    -------
    pyodesys.symbolic.SymbolicSys
    extra : dict, with keys:
        - param_keys : list of str instances
        - unique : OrderedDict mapping str to value (possibly None)
        - p_units : list of units
        - max_euler_step_cb : callable or None
        - linear_dependencies : None or factory of solver callback
        - rate_exprs_cb : callable
        - cstr_fr_fc : None or (feed-ratio-key, subtance-key-to-feed-conc-key-map)

    Examples
    --------
    >>> from chempy import Equilibrium, ReactionSystem
    >>> eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, 10**2)
    >>> substances = 'Fe+3 SCN- FeSCN+2'.split()
    >>> rsys = ReactionSystem(eq.as_reactions(kf=3.0), substances)
    >>> odesys, extra = get_odesys(rsys)
    >>> init_conc = {'Fe+3': 1.0, 'SCN-': .3, 'FeSCN+2': 0}
    >>> tout, Cout, info = odesys.integrate(5, init_conc)
    >>> Cout[-1, :].round(4)
    array([0.7042, 0.0042, 0.2958])

    """
    if SymbolicSys is None:
        from pyodesys.symbolic import SymbolicSys

    r_exprs = [rxn.rate_expr() for rxn in rsys.rxns]
    _ori_pk = set.union(*(ratex.all_parameter_keys() for ratex in r_exprs))
    _ori_uk = set.union(*(ratex.all_unique_keys() for ratex in r_exprs))
    _subst_pk = set()
    _active_subst = OrderedDict()
    _passive_subst = OrderedDict()
    substitutions = substitutions or {}

    unique = OrderedDict()
    unique_units = {}

    cstr_fr_fc = (
        'feedratio',
        OrderedDict([(sk, 'fc_'+sk) for sk in rsys.substances])
    ) if cstr is True else cstr

    if cstr_fr_fc:
        _ori_pk.add(cstr_fr_fc[0])
        for k in cstr_fr_fc[1].values():
            _ori_pk.add(k)

    def _reg_unique_unit(k, arg_dim, idx):
        if unit_registry is None:
            return
        unique_units[k] = reduce(mul, [1]+[unit_registry[dim]**v for dim, v in arg_dim[idx].items()])

    def _get_arg_dim(expr, rxn):
        if unit_registry is None:
            return None
        else:
            return expr.args_dimensionality(reaction=rxn)

    def _reg_unique(expr, rxn=None):
        if not isinstance(expr, Expr):
            raise NotImplementedError("Currently only Expr sub classes are supported.")

        if expr.args is None:
            for idx, k in enumerate(expr.unique_keys):
                if k not in substitutions:
                    unique[k] = None
                    _reg_unique_unit(k, _get_arg_dim(expr, rxn), idx)
        else:
            for idx, arg in enumerate(expr.args):
                if isinstance(arg, Expr):
                    _reg_unique(arg, rxn=rxn)
                elif expr.unique_keys is not None and idx < len(expr.unique_keys):
                    uk = expr.unique_keys[idx]
                    if uk not in substitutions:
                        unique[uk] = arg
                        _reg_unique_unit(uk, _get_arg_dim(expr, rxn), idx)

    for sk, sv in substitutions.items():
        if sk not in _ori_pk and sk not in _ori_uk:
            raise ValueError("Substitution: '%s' does not appear in any rate expressions." % sk)
        if isinstance(sv, Expr):
            _subst_pk.update(sv.parameter_keys)
            _active_subst[sk] = sv
            if not include_params:
                _reg_unique(sv)
        else:
            # if unit_registry is None:
            if unit_registry is not None:
                sv = unitless_in_registry(sv, unit_registry)
            _passive_subst[sk] = sv

    all_pk = []
    for pk in filter(lambda x: x not in substitutions and x != 'time',
                     _ori_pk.union(_subst_pk)):
        if hasattr(constants, pk):
            const = getattr(constants, pk)
            if unit_registry is None:
                const = magnitude(const)
            else:
                const = unitless_in_registry(const, unit_registry)

            _passive_subst[pk] = const
        else:
            all_pk.append(pk)

    if not include_params:
        for rxn, ratex in zip(rsys.rxns, r_exprs):
            _reg_unique(ratex, rxn)

    all_pk_with_unique = list(chain(all_pk, filter(lambda k: k not in all_pk, unique.keys())))
    if include_params:
        param_names_for_odesys = all_pk
    else:
        param_names_for_odesys = all_pk_with_unique

    if unit_registry is None:
        p_units = None
    else:
        # We need to make rsys_params unitless and create
        # a pre- & post-processor for SymbolicSys
        pk_units = [_get_derived_unit(unit_registry, k) for k in all_pk]
        p_units = pk_units if include_params else (pk_units + [unique_units[k] for k in unique])
        new_r_exprs = []
        for ratex in r_exprs:
            _pu, _new_ratex = ratex.dedimensionalisation(unit_registry)
            new_r_exprs.append(_new_ratex)
        r_exprs = new_r_exprs

        time_unit = get_derived_unit(unit_registry, 'time')
        conc_unit = get_derived_unit(unit_registry, 'concentration')

        def post_processor(x, y, p):
            time = x*time_unit
            if output_time_unit is not None:
                time = rescale(time, output_time_unit)
            conc = y*conc_unit
            if output_conc_unit is not None:
                conc = rescale(conc, output_conc_unit)
            return time, conc, np.array([elem*p_unit for elem, p_unit in zip(p.T, p_units)], dtype=object).T

        kwargs['to_arrays_callbacks'] = (
            lambda x: to_unitless(x, time_unit),
            lambda y: to_unitless(y, conc_unit),
            lambda p: np.array([to_unitless(px, p_unit) for px, p_unit in zip(
                p.T if hasattr(p, 'T') else p, p_units)]).T
        )
        kwargs['post_processors'] = kwargs.get('post_processors', []) + [post_processor]

    def dydt(t, y, p, backend=math):
        variables = dict(chain(y.items(), p.items()))
        if 'time' in variables:
            raise ValueError("Key 'time' is reserved.")
        variables['time'] = t
        for k, act in _active_subst.items():
            if unit_registry is not None and act.args:
                _, act = act.dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return rsys.rates(variables, backend=backend, ratexs=r_exprs, cstr_fr_fc=cstr_fr_fc)

    def reaction_rates(t, y, p, backend=math):
        variables = dict(chain(y.items(), p.items()))
        if 'time' in variables:
            raise ValueError("Key 'time' is reserved.")
        variables['time'] = t
        for k, act in _active_subst.items():
            if unit_registry is not None and act.args:
                _, act = act.dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return [ratex(variables, backend=backend, reaction=rxn) for
                rxn, ratex in zip(rsys.rxns, r_exprs)]

    names = [s.name for s in rsys.substances.values()]
    latex_names = [None if s.latex_name is None else ('\\mathrm{' + s.latex_name + '}')
                   for s in rsys.substances.values()]

    compo_vecs, compo_names = rsys.composition_balance_vectors()

    odesys = SymbolicSys.from_callback(
        dydt, dep_by_name=True, par_by_name=True, names=names,
        latex_names=latex_names, param_names=param_names_for_odesys,
        linear_invariants=None if len(compo_vecs) == 0 else compo_vecs,
        linear_invariant_names=None if len(compo_names) == 0 else list(map(str, compo_names)),
        **kwargs)

    symbolic_ratexs = reaction_rates(
        odesys.indep, dict(zip(odesys.names, odesys.dep)),
        dict(zip(odesys.param_names, odesys.params)), backend=odesys.be)
    rate_exprs_cb = odesys._callback_factory(symbolic_ratexs)

    if rsys.check_balance(strict=True):
        # Composition available, we can provide callback for calculating
        # maximum allowed Euler forward step at start of integration.
        def max_euler_step_cb(x, y, p=()):
            _x, _y, _p = odesys.pre_process(*odesys.to_arrays(x, y, p))
            upper_bounds = rsys.upper_conc_bounds(_y)
            fvec = odesys.f_cb(_x[0], _y, _p)
            h = []
            for idx, fcomp in enumerate(fvec):
                if fcomp == 0:
                    h.append(float('inf'))
                elif fcomp > 0:
                    h.append((upper_bounds[idx] - _y[idx])/fcomp)
                else:  # fcomp < 0
                    h.append(-_y[idx]/fcomp)
            min_h = min(h)
            return min(min_h, 1)

        def linear_dependencies(preferred=None):
            if preferred is not None:
                if len(preferred) == 0:
                    raise ValueError("No preferred substance keys provided")
                if len(preferred) >= len(rsys.substances):
                    raise ValueError("Cannot remove all concentrations from linear dependencies")
                for k in preferred:
                    if k not in rsys.substances:
                        raise ValueError("Unknown substance key: %s" % k)

            def analytic_solver(x0, y0, p0, be):
                if preferred is None:
                    _preferred = None
                else:
                    _preferred = list(preferred)
                A = be.Matrix(compo_vecs)
                rA, pivots = A.rref()

                analytic_exprs = OrderedDict()
                for ri, ci1st in enumerate(pivots):
                    for idx in range(ci1st, odesys.ny):
                        key = odesys.names[idx]
                        if rA[ri, idx] == 0:
                            continue
                        if _preferred is None or key in _preferred:
                            terms = [rA[ri, di]*(odesys.dep[di] - y0[odesys.dep[di]])
                                     for di in range(ci1st, odesys.ny) if di != idx]
                            analytic_exprs[odesys[key]] = y0[odesys.dep[idx]] - sum(terms)/rA[ri, idx]
                            if _preferred is not None:
                                _preferred.remove(key)
                            break
                for k in reversed(list(analytic_exprs.keys())):
                    analytic_exprs[k] = analytic_exprs[k].subs(analytic_exprs)
                if _preferred is not None and len(_preferred) > 0:
                    raise ValueError("Failed to obtain analytic expression for: %s" % ', '.join(_preferred))
                return analytic_exprs

            return analytic_solver

    else:
        max_euler_step_cb = None
        linear_dependencies = None

    return odesys, {
        'param_keys': all_pk,
        'unique': unique,
        'p_units': p_units,
        'max_euler_step_cb': max_euler_step_cb,
        'linear_dependencies': linear_dependencies,
        'rate_exprs_cb': rate_exprs_cb,
        'cstr_fr_fc': cstr_fr_fc,
        'unit_registry': unit_registry
    }
Example #13
0
def get_odesys(rsys,
               include_params=True,
               substitutions=None,
               SymbolicSys=None,
               unit_registry=None,
               output_conc_unit=None,
               output_time_unit=None,
               **kwargs):
    """ Creates a :class:`pyneqsys.SymbolicSys` from a :class:`ReactionSystem`

    Parameters
    ----------
    rsys : ReactionSystem
        note that if :attr:`param` if not RateExpr (or convertible to one through
        :meth:`_as_RateExpr`) it will be used to construct a :class:`MassAction` instance.
    include_params : bool (default: False)
        whether rate constants should be included into the rate expressions or
        left as free parameters in the :class:`pyneqsys.SymbolicSys` instance.
    substitutions : dict, optional
        variable substitutions used by rate expressions (in respective Reaction.param).
        values are allowed to be tuple like: (new_vars, callback)
    SymbolicSys : class (optional)
        default : :class:`pyneqsys.SymbolicSys`
    unit_registry: dict (optional)
        see :func:`chempy.units.get_derived_units`
    output_conc_unit : unit (Optional)
    output_time_unit : unit (Optional)
    \*\*kwargs :
        Keyword arguemnts pass on to `SymbolicSys`

    Returns
    -------
    pyodesys.symbolic.SymbolicSys
    param_keys
    unique_keys
    p_units

    Examples
    --------
    >>> from chempy import Equilibrium, ReactionSystem
    >>> eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, 10**2)
    >>> substances = 'Fe+3 SCN- FeSCN+2'.split()
    >>> rsys = ReactionSystem(eq.as_reactions(kf=3.0), substances)
    >>> odesys = get_odesys(rsys)[0]
    >>> init_conc = {'Fe+3': 1.0, 'SCN-': .3, 'FeSCN+2': 0}
    >>> tout, Cout, info = odesys.integrate(5, init_conc)
    >>> Cout[-1, :].round(4)
    array([ 0.7042,  0.0042,  0.2958])

    """
    if SymbolicSys is None:
        from pyodesys.symbolic import SymbolicSys

    substance_keys = list(rsys.substances.keys())

    if 'names' not in kwargs:
        kwargs['names'] = list(rsys.substances.values())  # pyodesys>=0.5.3

    r_exprs = [rxn.rate_expr() for rxn in rsys.rxns]

    _original_param_keys = set.union(*(set(ratex.parameter_keys)
                                       for ratex in r_exprs))
    _from_subst = set()
    _active_subst = {}
    _passive_subst = {}
    substitutions = substitutions or {}
    for key, v in substitutions.items():
        if key not in _original_param_keys:
            raise ValueError(
                "Substitution: '%s' does not appear in any rate expressions.")
        if isinstance(v, Expr):
            _from_subst.update(v.parameter_keys)
            _active_subst[key] = v
        else:
            _passive_subst[key] = v
    param_keys = list(
        filter(lambda x: x not in substitutions,
               _original_param_keys.union(_from_subst)))

    unique_keys = []
    p_defaults = []
    if not include_params:
        for ratex in r_exprs:
            if ratex.unique_keys is not None:
                unique_keys.extend(ratex.unique_keys)
                p_defaults.extend(ratex.args)
    if unit_registry is None:

        def pre_processor(x, y, p):
            return (x, rsys.as_per_substance_array(y),
                    [p[k] for k in param_keys] + [p[k] for k in unique_keys])

        def post_processor(x, y, p):
            return (
                x,
                y,  # dict(zip(substance_keys, y)),
                dict(zip(param_keys + unique_keys, p)))

        p_units = [None] * (len(param_keys) + len(unique_keys))
    else:
        # We need to make rsys_params unitless and create
        # a pre- & post-processor for SymbolicSys
        p_units = [get_derived_unit(unit_registry, k) for k in param_keys]
        new_r_exprs = []
        for ratex in r_exprs:
            _pu, _new_rate = ratex._recursive_as_RateExpr(
            ).dedimensionalisation(unit_registry)
            p_units.extend(_pu)
            new_r_exprs.append(_new_rate)
        r_exprs = new_r_exprs

        time_unit = get_derived_unit(unit_registry, 'time')
        conc_unit = get_derived_unit(unit_registry, 'concentration')

        def pre_processor(x, y, p):
            return (to_unitless(x, time_unit),
                    rsys.as_per_substance_array(to_unitless(y, conc_unit)), [
                        to_unitless(p[k], p_unit) for k, p_unit in zip(
                            chain(param_keys, unique_keys), p_units)
                    ])

        def post_processor(x, y, p):
            time = x * time_unit
            if output_time_unit is not None:
                time = time.rescale(output_time_unit)
            conc = y * conc_unit
            if output_conc_unit is not None:
                conc = conc.rescale(output_conc_unit)
            return time, conc, [
                elem * p_unit for elem, p_unit in zip(p, p_units)
            ]

    kwargs['pre_processors'] = [pre_processor] + kwargs.get(
        'pre_processors', [])
    kwargs['post_processors'] = kwargs.get('post_processors',
                                           []) + [post_processor]

    def dydt(t, y, p, backend=math):
        variables = dict(
            chain(zip(substance_keys, y), zip(param_keys, p[:len(param_keys)]),
                  zip(unique_keys, p[len(param_keys):])))
        for k, act in _active_subst.items():
            if unit_registry is not None:
                _, act = act.dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return dCdt(rsys, [rat(variables, backend=backend) for rat in r_exprs])

    return SymbolicSys.from_callback(
        dydt, len(substance_keys),
        len(param_keys) + (0 if include_params else len(unique_keys)),
        **kwargs), param_keys, unique_keys, p_units
Example #14
0
 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]
Example #15
0
File: ode.py Project: bjodah/chempy
def get_odesys(rsys, include_params=True, substitutions=None, SymbolicSys=None, unit_registry=None,
               output_conc_unit=None, output_time_unit=None, cstr=False, constants=None, **kwargs):
    """ Creates a :class:`pyneqsys.SymbolicSys` from a :class:`ReactionSystem`

    The parameters passed to RateExpr will contain the key ``'time'`` corresponding to the
    independent variable of the IVP.

    Parameters
    ----------
    rsys : ReactionSystem
        Each reaction of ``rsys`` will have their :meth:`Reaction.rate_expr()` invoked.
        Note that if :attr:`Reaction.param` is not a :class:`RateExpr` (or convertible to
        one through :meth:`as_RateExpr`) it will be used to construct a :class:`MassAction`
        instance.
    include_params : bool (default: True)
        Whether rate constants should be included into the rate expressions or
        left as free parameters in the :class:`pyneqsys.SymbolicSys` instance.
    substitutions : dict, optional
        Variable substitutions used by rate expressions (in respective Reaction.param).
        values are allowed to be values of instances of :class:`Expr`.
    SymbolicSys : class (optional)
        Default : :class:`pyneqsys.SymbolicSys`.
    unit_registry: dict (optional)
        See :func:`chempy.units.get_derived_units`.
    output_conc_unit : unit (Optional)
    output_time_unit : unit (Optional)
    cstr : bool
        Generate expressions for continuously stirred tank reactor.
    constants : module
        e.g. ``chempy.units.default_constants``, parameter keys not found in
        substitutions will be looked for as an attribute of ``constants`` when provided.
    \\*\\*kwargs :
        Keyword arguemnts passed on to `SymbolicSys`.

    Returns
    -------
    pyodesys.symbolic.SymbolicSys
    extra : dict, with keys:
        - param_keys : list of str instances
        - unique : OrderedDict mapping str to value (possibly None)
        - p_units : list of units
        - max_euler_step_cb : callable or None
        - linear_dependencies : None or factory of solver callback
        - rate_exprs_cb : callable
        - cstr_fr_fc : None or (feed-ratio-key, subtance-key-to-feed-conc-key-map)

    Examples
    --------
    >>> from chempy import Equilibrium, ReactionSystem
    >>> eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, 10**2)
    >>> substances = 'Fe+3 SCN- FeSCN+2'.split()
    >>> rsys = ReactionSystem(eq.as_reactions(kf=3.0), substances)
    >>> odesys, extra = get_odesys(rsys)
    >>> init_conc = {'Fe+3': 1.0, 'SCN-': .3, 'FeSCN+2': 0}
    >>> tout, Cout, info = odesys.integrate(5, init_conc)
    >>> Cout[-1, :].round(4)
    array([0.7042, 0.0042, 0.2958])

    """
    if SymbolicSys is None:
        from pyodesys.symbolic import SymbolicSys

    r_exprs = [rxn.rate_expr() for rxn in rsys.rxns]
    _ori_pk = set.union(*(ratex.all_parameter_keys() for ratex in r_exprs))
    _ori_uk = set.union(*(ratex.all_unique_keys() for ratex in r_exprs))
    _subst_pk = set()
    _active_subst = OrderedDict()
    _passive_subst = OrderedDict()
    substitutions = substitutions or {}

    unique = OrderedDict()
    unique_units = {}

    cstr_fr_fc = (
        'feedratio',
        OrderedDict([(sk, 'fc_'+sk) for sk in rsys.substances])
    ) if cstr is True else cstr

    if cstr_fr_fc:
        _ori_pk.add(cstr_fr_fc[0])
        for k in cstr_fr_fc[1].values():
            _ori_pk.add(k)

    def _reg_unique_unit(k, arg_dim, idx):
        if unit_registry is None:
            return
        unique_units[k] = reduce(mul, [1]+[unit_registry[dim]**v for dim, v in arg_dim[idx].items()])

    def _get_arg_dim(expr, rxn):
        if unit_registry is None:
            return None
        else:
            return expr.args_dimensionality(reaction=rxn)

    def _reg_unique(expr, rxn=None):
        if not isinstance(expr, Expr):
            raise NotImplementedError("Currently only Expr sub classes are supported.")

        if expr.args is None:
            for idx, k in enumerate(expr.unique_keys):
                if k not in substitutions:
                    unique[k] = None
                    _reg_unique_unit(k, _get_arg_dim(expr, rxn), idx)
        else:
            for idx, arg in enumerate(expr.args):
                if isinstance(arg, Expr):
                    _reg_unique(arg, rxn=rxn)
                elif expr.unique_keys is not None and idx < len(expr.unique_keys):
                    uk = expr.unique_keys[idx]
                    if uk not in substitutions:
                        unique[uk] = arg
                        _reg_unique_unit(uk, _get_arg_dim(expr, rxn), idx)

    for sk, sv in substitutions.items():
        if sk not in _ori_pk and sk not in _ori_uk:
            raise ValueError("Substitution: '%s' does not appear in any rate expressions." % sk)
        if isinstance(sv, Expr):
            _subst_pk.update(sv.parameter_keys)
            _active_subst[sk] = sv
            if not include_params:
                _reg_unique(sv)
        else:
            # if unit_registry is None:
            if unit_registry is not None:
                sv = unitless_in_registry(sv, unit_registry)
            _passive_subst[sk] = sv

    all_pk = []
    for pk in filter(lambda x: x not in substitutions and x != 'time',
                     _ori_pk.union(_subst_pk)):
        if hasattr(constants, pk):
            const = getattr(constants, pk)
            if unit_registry is None:
                const = magnitude(const)
            else:
                const = unitless_in_registry(const, unit_registry)

            _passive_subst[pk] = const
        else:
            all_pk.append(pk)

    if not include_params:
        for rxn, ratex in zip(rsys.rxns, r_exprs):
            _reg_unique(ratex, rxn)

    all_pk_with_unique = list(chain(all_pk, filter(lambda k: k not in all_pk, unique.keys())))
    if include_params:
        param_names_for_odesys = all_pk
    else:
        param_names_for_odesys = all_pk_with_unique

    if unit_registry is None:
        p_units = None
    else:
        # We need to make rsys_params unitless and create
        # a pre- & post-processor for SymbolicSys
        pk_units = [_get_derived_unit(unit_registry, k) for k in all_pk]
        p_units = pk_units if include_params else (pk_units + [unique_units[k] for k in unique])
        new_r_exprs = []
        for ratex in r_exprs:
            _pu, _new_ratex = ratex.dedimensionalisation(unit_registry)
            new_r_exprs.append(_new_ratex)
        r_exprs = new_r_exprs

        time_unit = get_derived_unit(unit_registry, 'time')
        conc_unit = get_derived_unit(unit_registry, 'concentration')

        def post_processor(x, y, p):
            time = x*time_unit
            if output_time_unit is not None:
                time = rescale(time, output_time_unit)
            conc = y*conc_unit
            if output_conc_unit is not None:
                conc = rescale(conc, output_conc_unit)
            return time, conc, np.array([elem*p_unit for elem, p_unit in zip(p.T, p_units)], dtype=object).T

        kwargs['to_arrays_callbacks'] = (
            lambda x: to_unitless(x, time_unit),
            lambda y: to_unitless(y, conc_unit),
            lambda p: np.array([to_unitless(px, p_unit) for px, p_unit in zip(
                p.T if hasattr(p, 'T') else p, p_units)]).T
        )
        kwargs['post_processors'] = kwargs.get('post_processors', []) + [post_processor]

    def dydt(t, y, p, backend=math):
        variables = dict(chain(y.items(), p.items()))
        if 'time' in variables:
            raise ValueError("Key 'time' is reserved.")
        variables['time'] = t
        for k, act in _active_subst.items():
            if unit_registry is not None and act.args:
                _, act = act.dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return rsys.rates(variables, backend=backend, ratexs=r_exprs, cstr_fr_fc=cstr_fr_fc)

    def reaction_rates(t, y, p, backend=math):
        variables = dict(chain(y.items(), p.items()))
        if 'time' in variables:
            raise ValueError("Key 'time' is reserved.")
        variables['time'] = t
        for k, act in _active_subst.items():
            if unit_registry is not None and act.args:
                _, act = act.dedimensionalisation(unit_registry)
            variables[k] = act(variables, backend=backend)
        variables.update(_passive_subst)
        return [ratex(variables, backend=backend, reaction=rxn) for
                rxn, ratex in zip(rsys.rxns, r_exprs)]

    names = [s.name for s in rsys.substances.values()]
    latex_names = [None if s.latex_name is None else ('\\mathrm{' + s.latex_name + '}')
                   for s in rsys.substances.values()]

    compo_vecs, compo_names = rsys.composition_balance_vectors()

    odesys = SymbolicSys.from_callback(
        dydt, dep_by_name=True, par_by_name=True, names=names,
        latex_names=latex_names, param_names=param_names_for_odesys,
        linear_invariants=None if len(compo_vecs) == 0 else compo_vecs,
        linear_invariant_names=None if len(compo_names) == 0 else list(map(str, compo_names)),
        **kwargs)

    symbolic_ratexs = reaction_rates(
        odesys.indep, dict(zip(odesys.names, odesys.dep)),
        dict(zip(odesys.param_names, odesys.params)), backend=odesys.be)
    rate_exprs_cb = odesys._callback_factory(symbolic_ratexs)

    if rsys.check_balance(strict=True):
        # Composition available, we can provide callback for calculating
        # maximum allowed Euler forward step at start of integration.
        def max_euler_step_cb(x, y, p=()):
            _x, _y, _p = odesys.pre_process(*odesys.to_arrays(x, y, p))
            upper_bounds = rsys.upper_conc_bounds(_y)
            fvec = odesys.f_cb(_x[0], _y, _p)
            h = []
            for idx, fcomp in enumerate(fvec):
                if fcomp == 0:
                    h.append(float('inf'))
                elif fcomp > 0:
                    h.append((upper_bounds[idx] - _y[idx])/fcomp)
                else:  # fcomp < 0
                    h.append(-_y[idx]/fcomp)
            min_h = min(h)
            return min(min_h, 1)

        def linear_dependencies(preferred=None):
            if preferred is not None:
                if len(preferred) == 0:
                    raise ValueError("No preferred substance keys provided")
                if len(preferred) >= len(rsys.substances):
                    raise ValueError("Cannot remove all concentrations from linear dependencies")
                for k in preferred:
                    if k not in rsys.substances:
                        raise ValueError("Unknown substance key: %s" % k)

            def analytic_solver(x0, y0, p0, be):
                if preferred is None:
                    _preferred = None
                else:
                    _preferred = list(preferred)
                A = be.Matrix(compo_vecs)
                rA, pivots = A.rref()

                analytic_exprs = OrderedDict()
                for ri, ci1st in enumerate(pivots):
                    for idx in range(ci1st, odesys.ny):
                        key = odesys.names[idx]
                        if rA[ri, idx] == 0:
                            continue
                        if _preferred is None or key in _preferred:
                            terms = [rA[ri, di]*(odesys.dep[di] - y0[odesys.dep[di]])
                                     for di in range(ci1st, odesys.ny) if di != idx]
                            analytic_exprs[odesys[key]] = y0[odesys.dep[idx]] - sum(terms)/rA[ri, idx]
                            if _preferred is not None:
                                _preferred.remove(key)
                            break
                for k in reversed(list(analytic_exprs.keys())):
                    analytic_exprs[k] = analytic_exprs[k].subs(analytic_exprs)
                if _preferred is not None and len(_preferred) > 0:
                    raise ValueError("Failed to obtain analytic expression for: %s" % ', '.join(_preferred))
                return analytic_exprs

            return analytic_solver

    else:
        max_euler_step_cb = None
        linear_dependencies = None

    return odesys, {
        'param_keys': all_pk,
        'unique': unique,
        'p_units': p_units,
        'max_euler_step_cb': max_euler_step_cb,
        'linear_dependencies': linear_dependencies,
        'rate_exprs_cb': rate_exprs_cb,
        'cstr_fr_fc': cstr_fr_fc,
        'unit_registry': unit_registry
    }
Example #16
0
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, :])
Example #17
0
                 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
Example #18
0
 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]
Example #19
0
from pyodesys.symbolic import SymbolicSys


def f(t, y, p):
    return [y[1], -y[0] + p[0] * y[1] * (1 - y[0]**2)]


odesys = SymbolicSys.from_callback(f, 2, 1)
xout, yout, info = odesys.integrate(10, [1, 0], [1],
                                    integrator='odeint',
                                    nsteps=1000)
_ = odesys.plot_result()
import matplotlib.pyplot as plt
plt.show()  # doctest: +SKIP
Example #20
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,
    }
Example #21
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):
    """ 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
    }
Example #22
0
def _test_chained_multi_native(NativeSys,
                               integrator='cvode',
                               rtol_close=0.02,
                               atol=1e-10,
                               rtol=1e-14,
                               steps_fact=1,
                               **kwargs):
    logc, logt, reduced = kwargs.pop('logc'), kwargs.pop('logt'), kwargs.pop(
        'reduced')
    zero_time, zero_conc, nonnegative = kwargs.pop('zero_time'), kwargs.pop(
        'zero_conc'), kwargs.pop('nonnegative')
    logexp = (sp.log, sp.exp)

    ny, nk = 3, 3
    k = (.04, 1e4, 3e7)
    init_conc = (1, zero_conc, zero_conc)
    tend = 1e11
    _yref_1e11 = (0.2083340149701255e-7, 0.8333360770334713e-13,
                  0.9999999791665050)

    lin_s = SymbolicSys.from_callback(get_ode_exprs(logc=False, logt=False)[0],
                                      ny,
                                      nk,
                                      lower_bounds=[0] *
                                      ny if nonnegative else None)
    logexp = (sp.log, sp.exp)

    if reduced:
        if logc or logt:
            PartSolvSys = PartiallySolvedSystem  # we'll add NativeSys further down below
        else:

            class PartSolvSys(PartiallySolvedSystem, NativeSys):
                pass

        other1, other2 = [_ for _ in range(3) if _ != (reduced - 1)]

        def reduced_analytic(x0, y0, p0):
            return {
                lin_s.dep[reduced - 1]:
                y0[0] + y0[1] + y0[2] - lin_s.dep[other1] - lin_s.dep[other2]
            }

        our_sys = PartSolvSys(lin_s, reduced_analytic)
    else:
        our_sys = lin_s

    if logc or logt:

        class TransformedNativeSys(TransformedSys, NativeSys):
            pass

        SS = symmetricsys(logexp if logc else None,
                          logexp if logt else None,
                          SuperClass=TransformedNativeSys)
        our_sys = SS.from_other(our_sys)

    ori_sys = NativeSys.from_other(lin_s)

    for sys_iter, kw in [([our_sys, ori_sys], {
            'nsteps': [100 * steps_fact, 1613 * 1.01 * steps_fact],
            'return_on_error': [True, False]
    }), ([ori_sys], {
            'nsteps': [1705 * 1.01 * steps_fact]
    })]:
        results = integrate_chained(sys_iter,
                                    kw, [(zero_time, tend)] * 3,
                                    [init_conc] * 3, [k] * 3,
                                    integrator=integrator,
                                    atol=atol,
                                    rtol=rtol,
                                    **kwargs)

        for res in results:
            x, y, nfo = res
            assert np.allclose(_yref_1e11,
                               y[-1, :],
                               atol=1e-16,
                               rtol=rtol_close)
            assert nfo['success'] == True  # noqa
            assert nfo['nfev'] > 100
            assert nfo['njev'] > 10
            assert nfo['nsys'] in (1, 2)
Example #23
0
class Dynamical(Parametrized, metaclass=DynamicalMeta, vars=None):
    graph = GraphAttrDict()
    _node = NodeDict()
    _adj = AdjlistOuterDict()
    _pred = AdjlistOuterDict()

    def __init__(self, *args, integrator=None, use_native=False, **kwargs):
        super().__init__(*args, **kwargs)
        self.use_native = use_native
        self.integrator = integrator

        self._sys = None
        self._stale_dynamics = True
        self._native_sys = None

    def expire_dynamics(self):
        self._stale_dynamics = True

    @abc.abstractmethod
    def rhs(self):
        pass

    @property
    def t(self):
        return sym.Symbol('t')

    @property
    def vars(self):
        return self._vars

    @property
    @uses_dynamics
    def sys(self):
        return self._sys

    @property
    @uses_dynamics
    def native_sys(self):
        return self._native_sys

    @property
    def stale_dynamics(self):
        return self._stale_dynamics

    @uses_dynamics
    def f(self, t, y):
        sys = self.native_sys if self.use_native else self.sys
        return sys.f_cb(t, y)

    @uses_dynamics
    def jac(self, t, y):
        sys = self.native_sys if self.use_native else self.sys
        return sys.j_cb(t, y)

    @uses_dynamics
    def jtimes(self, t, y, v):
        sys = self.native_sys if self.use_native else self.sys
        return sys.jtimes_cb(t, y, v)

    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

    @uses_dynamics
    def integrate(self, *args, **kwargs):
        if self.use_native:
            return self._native_sys.integrate(*args, **kwargs)
        else:
            kw = dict(integrator=self.integrator)
            kw.update(kwargs)
            return self._sys.integrate(*args, **kw)