Beispiel #1
0
def test_get_odesys_3():
    M = u.molar
    s = u.second
    mol = u.mol
    m = u.metre
    substances = list(map(Substance, 'H2O H+ OH-'.split()))
    dissociation = Reaction({'H2O': 1}, {'H+': 1, 'OH-': 1}, 2.47e-5/s)
    recombination = Reaction({'H+': 1, 'OH-': 1}, {'H2O': 1}, 1.37e11/M/s)
    rsys = ReactionSystem([dissociation, recombination], substances)
    odesys = get_odesys(
        rsys, include_params=True, unit_registry=SI_base_registry,
        output_conc_unit=M)[0]
    c0 = {'H2O': 55.4*M, 'H+': 1e-7*M, 'OH-': 1e-4*mol/m**3}
    x, y, p = odesys.to_arrays(-42*u.second,
                               rsys.as_per_substance_array(c0, unit=M), ())
    fout = odesys.f_cb(x, y, p)

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

    r1 = to_unitless(55.4*2.47e-5*M/s, conc_unit/time_unit)
    r2 = to_unitless(1e-14*1.37e11*M/s, conc_unit/time_unit)
    assert np.all(abs(fout[:, 0] - r2 + r1)) < 1e-10
    assert np.all(abs(fout[:, 1] - r1 + r2)) < 1e-10
    assert np.all(abs(fout[:, 2] - r1 + r2)) < 1e-10
Beispiel #2
0
def test_get_odesys__Eyring():
    R = 8.314472
    T_K = 300
    dH = 80e3
    dS = 10
    rsys1 = ReactionSystem.from_string("""
    NOBr -> NO + Br; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS), substances='NOBr NO Br'.split())
    kref = 20836643994.118652*T_K*np.exp(-(dH - T_K*dS)/(R*T_K))
    NOBr0_M = 0.7
    init_cond = dict(
        NOBr=NOBr0_M*u.M,
        NO=0*u.M,
        Br=0*u.M
    )
    t = 5*u.second
    params = dict(
        temperature=T_K*u.K
    )

    def check(rsys):
        odesys, extra = get_odesys(rsys, unit_registry=SI_base_registry, constants=const)
        res = odesys.integrate(t, init_cond, params, integrator='cvode')
        NOBr_ref = NOBr0_M*np.exp(-kref*to_unitless(res.xout, u.second))
        ref = np.array([NOBr_ref] + [NOBr0_M - NOBr_ref]*2).T
        cmp = to_unitless(res.yout, u.M)
        assert np.allclose(cmp, ref)
    check(rsys1)
    rsys2 = ReactionSystem.from_string("""
    NOBr -> NO + Br; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS), substances='NOBr NO Br'.split())
    check(rsys2)
Beispiel #3
0
def test_get_odesys__with_units():
    a = Substance('A')
    b = Substance('B')
    molar = u.molar
    second = u.second
    r = Reaction({'A': 2}, {'B': 1}, param=1e-3/molar/second)
    rsys = ReactionSystem([r], [a, b])
    odesys = get_odesys(rsys, include_params=True,
                        unit_registry=SI_base_registry)[0]
    c0 = {
        'A': 13*u.mol / u.metre**3,
        'B': .2 * u.molar
    }
    conc_unit = get_derived_unit(SI_base_registry, 'concentration')
    t = np.linspace(0, 10)*u.hour
    xout, yout, info = odesys.integrate(
        t, rsys.as_per_substance_array(c0, unit=conc_unit),
        atol=1e-10, rtol=1e-12)

    t_unitless = to_unitless(xout, u.second)
    Aref = dimerization_irrev(t_unitless, 1e-6, 13.0)
    # Aref = 1/(1/13 + 2*1e-6*t_unitless)
    yref = np.zeros((xout.size, 2))
    yref[:, 0] = Aref
    yref[:, 1] = 200 + (13-Aref)/2
    assert allclose(yout, yref*conc_unit)
Beispiel #4
0
def test_get_odesys__with_units():
    a = Substance('A')
    b = Substance('B')
    molar = u.molar
    second = u.second
    r = Reaction({'A': 2}, {'B': 1}, param=1e-3/molar/second)
    rsys = ReactionSystem([r], [a, b])
    odesys = get_odesys(rsys, include_params=True,
                        unit_registry=SI_base_registry)[0]
    c0 = {
        'A': 13*u.mol / u.metre**3,
        'B': .2 * u.molar
    }
    conc_unit = get_derived_unit(SI_base_registry, 'concentration')
    t = np.linspace(0, 10)*u.hour
    xout, yout, info = odesys.integrate(
        t, rsys.as_per_substance_array(c0, unit=conc_unit),
        atol=1e-10, rtol=1e-12)

    t_unitless = to_unitless(xout, u.second)
    Aref = dimerization_irrev(t_unitless, 1e-6, 13.0)
    # Aref = 1/(1/13 + 2*1e-6*t_unitless)
    yref = np.zeros((xout.size, 2))
    yref[:, 0] = Aref
    yref[:, 1] = 200 + (13-Aref)/2
    assert allclose(yout, yref*conc_unit)
Beispiel #5
0
def test_get_odesys__Eyring():
    R = 8.314472
    T_K = 300
    dH = 80e3
    dS = 10
    rsys1 = ReactionSystem.from_string("""
    NOBr -> NO + Br; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS), substances='NOBr NO Br'.split())
    kref = 20836643994.118652*T_K*np.exp(-(dH - T_K*dS)/(R*T_K))
    NOBr0_M = 0.7
    init_cond = dict(
        NOBr=NOBr0_M*u.M,
        NO=0*u.M,
        Br=0*u.M
    )
    t = 5*u.second
    params = dict(
        temperature=T_K*u.K
    )

    def check(rsys):
        odesys, extra = get_odesys(rsys, unit_registry=SI_base_registry, constants=const)
        res = odesys.integrate(t, init_cond, params, integrator='cvode')
        NOBr_ref = NOBr0_M*np.exp(-kref*to_unitless(res.xout, u.second))
        ref = np.array([NOBr_ref] + [NOBr0_M - NOBr_ref]*2).T
        cmp = to_unitless(res.yout, u.M)
        assert np.allclose(cmp, ref)
    check(rsys1)
    rsys2 = ReactionSystem.from_string("""
    NOBr -> NO + Br; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS), substances='NOBr NO Br'.split())
    check(rsys2)
Beispiel #6
0
def decompose_yields(yields, rxns, atol=1e-10):
    """ Decomposes yields into mass-action reactions

    This function offers a way to express a reaction with non-integer
    stoichiometric coefficients as a linear combination of production reactions
    with integer coefficients.

    Ak = y

    A is (n_species x n_reactions) matrix, k is "rate coefficient", y is yields


    Parameters
    ----------
    yields : OrderedDict
        Specie names as keys and yields as values.
    rxns : iterable :class:`Reaction` instances
        Dict keys must match those of ``yields`` each pair
        of dictionaries gives stoichiometry
        (1st is reactant, 2nd is products).
    atol : float
        Absolute tolerance for residuals.


    Examples
    --------
    >>> from chempy import Reaction
    >>> h2a = Reaction({'H2O': 1}, {'H2': 1, 'O': 1})
    >>> h2b = Reaction({'H2O': 1}, {'H2': 1, 'H2O2': 1}, inact_reac={'H2O': 1})
    >>> decompose_yields({'H2': 3, 'O': 2, 'H2O2': 1}, [h2a, h2b])
    array([2., 1.])

    Raises
    ------
    ValueError
        When atol is exceeded
    numpy.LinAlgError
        When numpy.linalg.lstsq fails to converge

    Returns
    -------
    1-dimensional array of effective rate coefficients.

    """
    from chempy import ReactionSystem
    # Sanity check:
    rxn_keys = set.union(*(rxn.keys() for rxn in rxns))
    for key in yields.keys():
        if key not in rxn_keys:
            raise ValueError("Substance key: %s not in reactions" % key)
    rsys = ReactionSystem(rxns, rxn_keys)
    A = rsys.net_stoichs(yields.keys())
    b = list(yields.values())
    unit = unit_of(b[0])
    x, residuals, rank, s = np.linalg.lstsq(
        np.asarray(A.T, dtype=np.float64), to_unitless(b, unit), rcond=2e-16*max(A.shape))
    if len(residuals) > 0:
        if np.any(residuals > atol):
            raise ValueError("atol not satisfied")
    return x*unit
Beispiel #7
0
def test_get_odesys_3():
    M = u.molar
    s = u.second
    mol = u.mol
    m = u.metre
    substances = list(map(Substance, 'H2O H+ OH-'.split()))
    dissociation = Reaction({'H2O': 1}, {'H+': 1, 'OH-': 1}, 2.47e-5/s)
    recombination = Reaction({'H+': 1, 'OH-': 1}, {'H2O': 1}, 1.37e11/M/s)
    rsys = ReactionSystem([dissociation, recombination], substances)
    odesys = get_odesys(
        rsys, include_params=True, unit_registry=SI_base_registry,
        output_conc_unit=M)[0]
    c0 = {'H2O': 55.4*M, 'H+': 1e-7*M, 'OH-': 1e-4*mol/m**3}
    x, y, p = odesys.to_arrays(-42*u.second,
                               rsys.as_per_substance_array(c0, unit=M), ())
    fout = odesys.f_cb(x, y, p)

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

    r1 = to_unitless(55.4*2.47e-5*M/s, conc_unit/time_unit)
    r2 = to_unitless(1e-14*1.37e11*M/s, conc_unit/time_unit)
    assert np.all(abs(fout[:, 0] - r2 + r1)) < 1e-10
    assert np.all(abs(fout[:, 1] - r1 + r2)) < 1e-10
    assert np.all(abs(fout[:, 2] - r1 + r2)) < 1e-10
Beispiel #8
0
def decompose_yields(yields, rxns, atol=1e-10):
    """ Decomposes yields into mass-action reactions

    This function offers a way to express a reaction with non-integer
    stoichiometric coefficients as a linear combination of production reactions
    with integer coefficients.

    Ak = y

    A is (n_species x n_reactions) matrix, k is "rate coefficient", y is yields


    Parameters
    ----------
    yields: OrderedDict
        specie names as keys and yields as values
    rxns: iterable :class:`Reaction` instances
        dict keys must match those of ``yields`` each pair
        of dictionaries gives stoichiometry
        (1st is reactant, 2nd is products)
    atol: float
        absolute tolerance for residuals


    Examples
    --------
    >>> from chempy import Reaction
    >>> h2a = Reaction({'H2O': 1}, {'H2': 1, 'O': 1})
    >>> h2b = Reaction({'H2O': 1}, {'H2': 1, 'H2O2': 1}, inact_reac={'H2O': 1})
    >>> decompose_yields({'H2': 3, 'O': 2, 'H2O2': 1}, [h2a, h2b])
    array([ 2.,  1.])

    Raises
    ------
    ValueError
        When atol is exceeded
    numpy.LinAlgError
        When numpy.linalg.lstsq fails to converge

    Returns
    -------
    1-dimensional array of effective rate coefficients.

    """
    from chempy import ReactionSystem
    # Sanity check:
    rxn_keys = set.union(*(rxn.keys() for rxn in rxns))
    for key in yields.keys():
        if key not in rxn_keys:
            raise ValueError("Substance key: %s not in reactions" % key)
    rsys = ReactionSystem(rxns, rxn_keys)
    A = rsys.net_stoichs(yields.keys())
    b = list(yields.values())
    unit = unit_of(b[0])
    x, residuals, rank, s = np.linalg.lstsq(A.T, to_unitless(b, unit))
    if len(residuals) > 0:
        if np.any(residuals > atol):
            raise ValueError("atol not satisfied")
    return x*unit
Beispiel #9
0
def test_get_odesys__Eyring_2nd_order_reversible():
    R = 8.314472
    T_K = 273.15 + 20  # 20 degree celsius
    kB = 1.3806504e-23
    h = 6.62606896e-34

    dHf = 74e3
    dSf = R * np.log(h / kB / T_K * 1e16)

    dHb = 79e3
    dSb = dSf - 23

    rsys1 = ReactionSystem.from_string("""
    Fe+3 + SCN- -> FeSCN+2; EyringParam(dH={dHf}*J/mol, dS={dSf}*J/K/mol)
    FeSCN+2 -> Fe+3 + SCN-; EyringParam(dH={dHb}*J/mol, dS={dSb}*J/K/mol)
    """.format(dHf=dHf, dSf=dSf, dHb=dHb, dSb=dSb))
    kf_ref = 20836643994.118652 * T_K * np.exp(-(dHf - T_K * dSf) / (R * T_K))
    kb_ref = 20836643994.118652 * T_K * np.exp(-(dHb - T_K * dSb) / (R * T_K))
    Fe0 = 6e-3
    SCN0 = 2e-3
    init_cond = {'Fe+3': Fe0 * u.M, 'SCN-': SCN0 * u.M, 'FeSCN+2': 0 * u.M}
    t = 3 * u.second

    def check(rsys, params):
        odes, extra = get_odesys(rsys,
                                 include_params=False,
                                 unit_registry=SI_base_registry,
                                 constants=const)
        for odesys in [odes, odes.as_autonomous()]:
            res = odesys.integrate(t, init_cond, params, integrator='cvode')
            t_sec = to_unitless(res.xout, u.second)
            FeSCN_ref = binary_rev(t_sec, kf_ref, kb_ref, 0, Fe0, SCN0)
            cmp = to_unitless(res.yout, u.M)
            ref = np.empty_like(cmp)
            ref[:, odesys.names.index('FeSCN+2')] = FeSCN_ref
            ref[:, odesys.names.index('Fe+3')] = Fe0 - FeSCN_ref
            ref[:, odesys.names.index('SCN-')] = SCN0 - FeSCN_ref
            assert np.allclose(cmp, ref)

    check(rsys1, {'temperature': T_K * u.K})
    rsys2 = ReactionSystem.from_string("""
    Fe+3 + SCN- -> FeSCN+2; MassAction(EyringHS([{dHf}*J/mol, {dSf}*J/K/mol]))
    FeSCN+2 -> Fe+3 + SCN-; MassAction(EyringHS([{dHb}*J/mol, {dSb}*J/K/mol]))
    """.format(dHf=dHf, dSf=dSf, dHb=dHb, dSb=dSb))
    check(rsys2, {'temperature': T_K * u.K})
    rsys3 = ReactionSystem.from_string("""
    Fe+3 + SCN- -> FeSCN+2; MassAction(EyringHS.fk('dHf', 'dSf'))
    FeSCN+2 -> Fe+3 + SCN-; MassAction(EyringHS.fk('dHb', 'dSb'))
    """)
    check(
        rsys3,
        dict(temperature=T_K * u.K,
             dHf=dHf * u.J / u.mol,
             dSf=dSf * u.J / u.mol / u.K,
             dHb=dHb * u.J / u.mol,
             dSb=dSb * u.J / u.mol / u.K))
Beispiel #10
0
def test_get_odesys__Eyring_2nd_order_reversible():
    R = 8.314472
    T_K = 273.15 + 20  # 20 degree celsius
    kB = 1.3806504e-23
    h = 6.62606896e-34

    dHf = 74e3
    dSf = R*np.log(h/kB/T_K*1e16)

    dHb = 79e3
    dSb = dSf - 23

    rsys1 = ReactionSystem.from_string("""
    Fe+3 + SCN- -> FeSCN+2; EyringParam(dH={dHf}*J/mol, dS={dSf}*J/K/mol)
    FeSCN+2 -> Fe+3 + SCN-; EyringParam(dH={dHb}*J/mol, dS={dSb}*J/K/mol)
    """.format(dHf=dHf, dSf=dSf, dHb=dHb, dSb=dSb))
    kf_ref = 20836643994.118652*T_K*np.exp(-(dHf - T_K*dSf)/(R*T_K))
    kb_ref = 20836643994.118652*T_K*np.exp(-(dHb - T_K*dSb)/(R*T_K))
    Fe0 = 6e-3
    SCN0 = 2e-3
    init_cond = {
        'Fe+3': Fe0*u.M,
        'SCN-': SCN0*u.M,
        'FeSCN+2': 0*u.M
    }
    t = 3*u.second

    def check(rsys, params):
        odes, extra = get_odesys(rsys, include_params=False,
                                 unit_registry=SI_base_registry, constants=const)
        for odesys in [odes, odes.as_autonomous()]:
            res = odesys.integrate(t, init_cond, params, integrator='cvode')
            t_sec = to_unitless(res.xout, u.second)
            FeSCN_ref = binary_rev(t_sec, kf_ref, kb_ref, 0, Fe0, SCN0)
            cmp = to_unitless(res.yout, u.M)
            ref = np.empty_like(cmp)
            ref[:, odesys.names.index('FeSCN+2')] = FeSCN_ref
            ref[:, odesys.names.index('Fe+3')] = Fe0 - FeSCN_ref
            ref[:, odesys.names.index('SCN-')] = SCN0 - FeSCN_ref
            assert np.allclose(cmp, ref)

    check(rsys1, {'temperature': T_K*u.K})
    rsys2 = ReactionSystem.from_string("""
    Fe+3 + SCN- -> FeSCN+2; MassAction(EyringHS([{dHf}*J/mol, {dSf}*J/K/mol]))
    FeSCN+2 -> Fe+3 + SCN-; MassAction(EyringHS([{dHb}*J/mol, {dSb}*J/K/mol]))
    """.format(dHf=dHf, dSf=dSf, dHb=dHb, dSb=dSb))
    check(rsys2, {'temperature': T_K*u.K})
    rsys3 = ReactionSystem.from_string("""
    Fe+3 + SCN- -> FeSCN+2; MassAction(EyringHS.fk('dHf', 'dSf'))
    FeSCN+2 -> Fe+3 + SCN-; MassAction(EyringHS.fk('dHb', 'dSb'))
    """)
    check(rsys3, dict(temperature=T_K*u.K,
                      dHf=dHf*u.J/u.mol, dSf=dSf*u.J/u.mol/u.K,
                      dHb=dHb*u.J/u.mol, dSb=dSb*u.J/u.mol/u.K))
Beispiel #11
0
def test_get_odesys__Eyring_2nd_order_linearly_ramped_temperature():
    from scipy.special import expi

    def analytic_unit0(t, k, m, dH, dS):
        R = 8.314472
        kB = 1.3806504e-23
        h = 6.62606896e-34
        A = kB / h * np.exp(dS / R)
        B = dH / R
        return k * np.exp(B * (k * t + 2 * m) / (m * (k * t + m))) / (
            A * (-B**2 * np.exp(B /
                                (k * t + m)) * expi(-B /
                                                    (k * t + m)) - B * k * t -
                 B * m + k**2 * t**2 + 2 * k * m * t + m**2) * np.exp(B / m) +
            (A * B**2 * np.exp(B / m) * expi(-B / m) - A * m *
             (-B + m) + k * np.exp(B / m)) * np.exp(B / (k * t + m)))

    T_K = 290
    dTdt_Ks = 3
    dH = 80e3
    dS = 10
    rsys1 = ReactionSystem.from_string("""
    2 NO2 -> N2O4; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS))

    NO2_M = 1.0
    init_cond = dict(NO2=NO2_M * u.M, N2O4=0 * u.M)
    t = 20 * u.second

    def check(rsys):
        odes, extra = get_odesys(rsys,
                                 unit_registry=SI_base_registry,
                                 constants=const,
                                 substitutions={
                                     'temperature':
                                     RampedTemp(
                                         [T_K * u.K, dTdt_Ks * u.K / u.s])
                                 })
        for odesys in [odes, odes.as_autonomous()]:
            res = odesys.integrate(t, init_cond, integrator='cvode')
            t_sec = to_unitless(res.xout, u.second)
            NO2_ref = analytic_unit0(t_sec, dTdt_Ks, T_K, dH, dS)
            cmp = to_unitless(res.yout, u.M)
            ref = np.empty_like(cmp)
            ref[:, odesys.names.index('NO2')] = NO2_ref
            ref[:, odesys.names.index('N2O4')] = (NO2_M - NO2_ref) / 2
            assert np.allclose(cmp, ref)

    check(rsys1)
    rsys2 = ReactionSystem.from_string("""
    2 NO2 -> N2O4; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS))
    check(rsys2)
Beispiel #12
0
def test_get_odesys__Eyring_1st_order_linearly_ramped_temperature():
    from scipy.special import expi

    def analytic_unit0(t, T0, dH, dS):
        R = 8.314472
        kB = 1.3806504e-23
        h = 6.62606896e-34
        A = kB / h * np.exp(dS / R)
        B = dH / R
        return np.exp(A * ((-B**2 * np.exp(B / T0) * expi(-B / T0) - T0 *
                            (B - T0)) * np.exp(-B / T0) +
                           (B**2 * np.exp(B / (t + T0)) * expi(-B / (t + T0)) -
                            (t + T0) * (-B + t + T0)) * np.exp(-B /
                                                               (t + T0))) / 2)

    T_K = 290
    dH = 80e3
    dS = 10
    rsys1 = ReactionSystem.from_string("""
    NOBr -> NO + Br; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS))

    NOBr0_M = 0.7
    init_cond = dict(NOBr=NOBr0_M * u.M, NO=0 * u.M, Br=0 * u.M)
    t = 20 * u.second

    def check(rsys):
        odes, extra = get_odesys(rsys,
                                 unit_registry=SI_base_registry,
                                 constants=const,
                                 substitutions={
                                     'temperature':
                                     RampedTemp([T_K * u.K, 1 * u.K / u.s])
                                 })
        for odesys in [odes, odes.as_autonomous()]:
            res = odesys.integrate(t, init_cond, integrator='cvode')
            t_sec = to_unitless(res.xout, u.second)
            NOBr_ref = NOBr0_M * analytic_unit0(t_sec, T_K, dH, dS)
            cmp = to_unitless(res.yout, u.M)
            ref = np.empty_like(cmp)
            ref[:, odesys.names.index('NOBr')] = NOBr_ref
            ref[:, odesys.names.index('Br')] = NOBr0_M - NOBr_ref
            ref[:, odesys.names.index('NO')] = NOBr0_M - NOBr_ref
            assert np.allclose(cmp, ref)

    check(rsys1)
    rsys2 = ReactionSystem.from_string("""
    NOBr -> NO + Br; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS))
    check(rsys2)
Beispiel #13
0
def test_EyringMassAction():
    args = kB_h_times_exp_dS_R, dH_over_R, c0 = 1.2e11 / 273.15, 40e3 / 8, 1
    ama = MassAction(Eyring(args, ("Sfreq", "Hact")))
    rxn1 = Reaction({"A": 2, "B": 1}, {"C": 1}, ama, {"B": 1})
    T_ = "temperature"

    def ref(v):
        return (v.get("Sfreq", 1.2e11 / 273.15) * v[T_] *
                math.exp(-v.get("Hact", 40e3 / 8) / v[T_]) * v["B"] *
                v["A"]**2)

    for params in [(11.0, 13.0, 17.0, 311.2), (12, 8, 5, 270)]:
        var = dict(zip(["A", "B", "C", T_], params))
        ref_val = ref(var)
        assert abs((ama(var, reaction=rxn1) - ref_val) / ref_val) < 1e-14

    with pytest.raises(ValueError):
        MassAction(Eyring([1, 1, 1, 1, 1]))

    # assert ama.as_mass_action({T_: 273.15}).args[0] == 1.2e11*math.exp(-40e3/8/273.15)

    ama2 = MassAction(
        Eyring4([1.2e11 / 273, 40e3 / 8, 1.2, 1e3],
                ("Sfreq", "Hact", "Sref", "Href")))
    rxn2 = Reaction({"C": 1}, {"A": 2, "B": 2}, ama2)
    var2 = {"C": 29, "temperature": 273}

    def ref2(var):
        return (var["C"] * var.get("temperature", 273) *
                var.get("Sfreq", 1.2e11 / 273) / var.get("Sref", 1.2) *
                math.exp((var.get("Href", 1e3) - var.get("Hact", 5e3)) /
                         var.get("temperature", 273)))

    r2 = ref2(var2)
    assert abs((ama2(var2, reaction=rxn2) - r2) / r2) < 1e-14

    rsys = ReactionSystem([rxn1, rxn2])
    var3 = {
        "A": 11,
        "B": 13,
        "C": 17,
        "temperature": 298,
        "Sfreq": 1.2e11 / 298
    }
    rates = rsys.rates(var3)
    rf3 = ref(var3)
    rb3 = ref2(var3)
    ref_rates = {"A": 2 * (rb3 - rf3), "B": 2 * (rb3 - rf3), "C": rf3 - rb3}
    for k, v in ref_rates.items():
        assert abs((rates[k] - v) / v) < 1e-14
Beispiel #14
0
def test_EyringMassAction():
    args = kB_h_times_exp_dS_R, dH_over_R, c0 = 1.2e11 / 273.15, 40e3 / 8, 1
    ama = MassAction(Eyring(args, ('Sfreq', 'Hact')))
    rxn1 = Reaction({'A': 2, 'B': 1}, {'C': 1}, ama, {'B': 1})
    T_ = 'temperature'

    def ref(v):
        return v.get('Sfreq', 1.2e11 / 273.15) * v[T_] * math.exp(
            -v.get('Hact', 40e3 / 8) / v[T_]) * v['B'] * v['A']**2

    for params in [(11., 13., 17., 311.2), (12, 8, 5, 270)]:
        var = dict(zip(['A', 'B', 'C', T_], params))
        ref_val = ref(var)
        assert abs((ama(var, reaction=rxn1) - ref_val) / ref_val) < 1e-14

    with pytest.raises(ValueError):
        MassAction(Eyring([1, 1, 1, 1, 1]))

    # assert ama.as_mass_action({T_: 273.15}).args[0] == 1.2e11*math.exp(-40e3/8/273.15)

    ama2 = MassAction(
        Eyring4([1.2e11 / 273, 40e3 / 8, 1.2, 1e3],
                ('Sfreq', 'Hact', 'Sref', 'Href')))
    rxn2 = Reaction({'C': 1}, {'A': 2, 'B': 2}, ama2)
    var2 = {'C': 29, 'temperature': 273}

    def ref2(var):
        return var['C'] * var.get('temperature', 273) * var.get(
            'Sfreq', 1.2e11 / 273) / var.get('Sref', 1.2) * math.exp(
                (var.get('Href', 1e3) - var.get('Hact', 5e3)) /
                var.get('temperature', 273))

    r2 = ref2(var2)
    assert abs((ama2(var2, reaction=rxn2) - r2) / r2) < 1e-14

    rsys = ReactionSystem([rxn1, rxn2])
    var3 = {
        'A': 11,
        'B': 13,
        'C': 17,
        'temperature': 298,
        'Sfreq': 1.2e11 / 298
    }
    rates = rsys.rates(var3)
    rf3 = ref(var3)
    rb3 = ref2(var3)
    ref_rates = {'A': 2 * (rb3 - rf3), 'B': 2 * (rb3 - rf3), 'C': rf3 - rb3}
    for k, v in ref_rates.items():
        assert abs((rates[k] - v) / v) < 1e-14
Beispiel #15
0
def test_get_odesys__Eyring_1st_order_linearly_ramped_temperature():
    from scipy.special import expi

    def analytic_unit0(t, T0, dH, dS):
        R = 8.314472
        kB = 1.3806504e-23
        h = 6.62606896e-34
        A = kB/h*np.exp(dS/R)
        B = dH/R
        return np.exp(A*(
            (-B**2*np.exp(B/T0)*expi(-B/T0) - T0*(B - T0))*np.exp(-B/T0) +
            (B**2*np.exp(B/(t + T0))*expi(-B/(t + T0)) - (t + T0)*(-B + t + T0))*np.exp(-B/(t + T0))
        )/2)

    T_K = 290
    dH = 80e3
    dS = 10
    rsys1 = ReactionSystem.from_string("""
    NOBr -> NO + Br; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS))

    NOBr0_M = 0.7
    init_cond = dict(
        NOBr=NOBr0_M*u.M,
        NO=0*u.M,
        Br=0*u.M
    )
    t = 20*u.second

    def check(rsys):
        odes, extra = get_odesys(rsys, unit_registry=SI_base_registry, constants=const, substitutions={
            'temperature': RampedTemp([T_K*u.K, 1*u.K/u.s])})
        for odesys in [odes, odes.as_autonomous()]:
            res = odesys.integrate(t, init_cond, integrator='cvode')
            t_sec = to_unitless(res.xout, u.second)
            NOBr_ref = NOBr0_M*analytic_unit0(t_sec, T_K, dH, dS)
            cmp = to_unitless(res.yout, u.M)
            ref = np.empty_like(cmp)
            ref[:, odesys.names.index('NOBr')] = NOBr_ref
            ref[:, odesys.names.index('Br')] = NOBr0_M - NOBr_ref
            ref[:, odesys.names.index('NO')] = NOBr0_M - NOBr_ref
            assert np.allclose(cmp, ref)

    check(rsys1)
    rsys2 = ReactionSystem.from_string("""
    NOBr -> NO + Br; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS))
    check(rsys2)
Beispiel #16
0
def test_get_odesys__Eyring_2nd_order():
    R = 8.314472
    T_K = 300
    dH = 80e3
    dS = 10
    rsys1b = ReactionSystem.from_string(
        """
    NO + Br -> NOBr; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(
            dH=dH, dS=dS
        )
    )
    c0 = 1  # mol/dm3 === 1000 mol/m3
    kbref = 20836643994.118652 * T_K * np.exp(-(dH - T_K * dS) / (R * T_K)) / c0
    NO0_M = 1.5
    Br0_M = 0.7
    init_cond = dict(NOBr=0 * u.M, NO=NO0_M * u.M, Br=Br0_M * u.M)
    t = 5 * u.second
    params = dict(temperature=T_K * u.K)

    def analytic_b(t):
        U, V = NO0_M, Br0_M
        d = U - V
        return (U * (1 - np.exp(-kbref * t * d))) / (U / V - np.exp(-kbref * t * d))

    def check(rsys):
        odesys, extra = get_odesys(
            rsys, unit_registry=SI_base_registry, constants=const
        )
        res = odesys.integrate(t, init_cond, params, integrator="cvode")
        t_sec = to_unitless(res.xout, u.second)
        NOBr_ref = analytic_b(t_sec)
        cmp = to_unitless(res.yout, u.M)
        ref = np.empty_like(cmp)
        ref[:, odesys.names.index("NOBr")] = NOBr_ref
        ref[:, odesys.names.index("Br")] = Br0_M - NOBr_ref
        ref[:, odesys.names.index("NO")] = NO0_M - NOBr_ref
        assert np.allclose(cmp, ref)

    check(rsys1b)
    rsys2b = ReactionSystem.from_string(
        """
    NO + Br -> NOBr; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(
            dH=dH, dS=dS
        )
    )
    check(rsys2b)
Beispiel #17
0
def test_create_odesys__ShiftedTPoly():
    rxn = Reaction({'A': 1, 'B': 1}, {'C': 3, 'D': 2}, 'k_bi', {'A': 3})
    rsys = ReactionSystem([rxn], 'A B C D')

    _k0, _k1, T0C = 10, 2, 273.15
    rate = MassAction(ShiftedTPoly([T0C*u.K, _k0/u.molar/u.s, _k1/u.molar/u.s/u.K]))
    T_C = 25
    T = (T0C+T_C)*u.kelvin
    p1 = rate.rate_coeff({'temperature': T})
    assert allclose(p1, (_k0 + _k1*T_C)/u.molar/u.s)

    odesys, odesys_extra = create_odesys(rsys)
    ics = {'A': 3*u.molar, 'B': 5*u.molar, 'C': 11*u.molar, 'D': 13*u.molar}
    pars = dict(k_bi=p1)
    validation = odesys_extra['validate'](dict(ics, **pars))
    assert set(map(str, validation['not_seen'])) == {'C', 'D'}

    dedim_ctx = _mk_dedim(SI_base_registry)
    (t, c, _p), dedim_extra = dedim_ctx['dedim_tcp'](-37*u.s, [ics[k] for k in odesys.names], pars)
    fout = odesys.f_cb(t, c, [_p[pk] for pk in odesys.param_names])
    r = 3*5*(_k0 + _k1*25)*1000  # mol/m3/s
    ref = [-4*r, -r, 3*r, 2*r]
    assert np.all(abs((fout - ref)/ref) < 1e-14)

    odesys.integrate(t, c, _p)
Beispiel #18
0
def test_get_native__first_step(solve):
    integrator = 'cvode'
    forgive = 20

    def k(num):
        return "MassAction(unique_keys=('k%d',))" % num

    lines = [  # fictitious isomerization
        "CNO -> ONC; %s" % k(1),
        "ONC -> NCO; %s" % k(2),
        "NCO -> CON; %s" % k(3)
    ]
    rsys = ReactionSystem.from_string('\n'.join(lines), 'CNO ONC NCO CON')
    odesys, extra = get_odesys(rsys, include_params=False)
    if len(solve) > 0:
        from pyodesys.symbolic import PartiallySolvedSystem
        odesys = PartiallySolvedSystem(odesys, extra['linear_dependencies'](solve))
    c0 = defaultdict(float, {'CNO': .7})
    rate_coeffs = (1e78, 2, 3.)
    args = (5, c0, dict(zip('k1 k2 k3'.split(), rate_coeffs)))
    kwargs = dict(integrator=integrator, atol=1e-9, rtol=1e-9, nsteps=1000)
    native = get_native(rsys, odesys, integrator)

    h0 = extra['max_euler_step_cb'](0, *args[1:])
    xout1, yout1, info1 = odesys.integrate(*args, first_step=h0, **kwargs)
    xout2, yout2, info2 = native.integrate(*args, **kwargs)
    ref1 = decay_get_Cref(rate_coeffs, [c0[key] for key in native.names], xout1)
    ref2 = decay_get_Cref(rate_coeffs, [c0[key] for key in native.names], xout2)
    allclose_kw = dict(atol=kwargs['atol']*forgive, rtol=kwargs['rtol']*forgive)

    assert np.allclose(yout1[:, :3], ref1, **allclose_kw)

    assert info2['success']
    assert info2['nfev'] > 10 and info2['nfev'] > 1 and info2['time_cpu'] < 10 and info2['time_wall'] < 10
    assert np.allclose(yout2[:, :3], ref2, **allclose_kw)
Beispiel #19
0
def test_get_ode__Radiolytic__substitutions__units():
    rad = Radiolytic([2.4e-7 * u.mol / u.joule])
    rxn = Reaction({'A': 4, 'B': 1}, {'C': 3, 'D': 2}, rad)
    rsys = ReactionSystem([rxn], 'A B C D')
    g_dm3 = u.gram / u.decimetre**3
    kg_dm3 = u.kg / u.decimetre**3
    substance_rho = Density(
        [1 * kg_dm3, -1 * g_dm3 / u.kelvin, 273.15 * u.kelvin])
    odesys = get_odesys(rsys,
                        include_params=True,
                        unit_registry=SI_base_registry,
                        substitutions={'density': substance_rho})[0]
    conc = {
        'A': 3 * u.molar,
        'B': 5 * u.molar,
        'C': 11 * u.molar,
        'D': 13 * u.molar
    }
    x, y, p = odesys.to_arrays(-37 * u.second, conc, {
        'doserate': 0.4 * u.gray / u.second,
        'temperature': 298.15 * u.kelvin
    })
    fout = odesys.f_cb(x, y, p)
    r = 2.4e-7 * 0.4 * 0.975 * 1e3  # mol/m3/s
    ref = [-4 * r, -r, 3 * r, 2 * r]
    assert np.all(abs((fout - ref) / ref) < 1e-14)
Beispiel #20
0
def test_get_odesys__late_binding():
    def _gibbs(args, T, R, backend, **kwargs):
        H, S = args
        return backend.exp(-(H - T*S)/(R*T))

    def _eyring(args, T, R, k_B, h, backend, **kwargs):
        H, S = args
        return k_B/h*T*backend.exp(-(H - T*S)/(R*T))

    gibbs_pk = ('temperature', 'molar_gas_constant')
    eyring_pk = gibbs_pk + ('Boltzmann_constant', 'Planck_constant')

    GibbsEC = MassActionEq.from_callback(_gibbs, argument_names=('H', 'S'), parameter_keys=gibbs_pk)
    EyringMA = MassAction.from_callback(_eyring, argument_names=('H', 'S'), parameter_keys=eyring_pk)

    uk_equil = ('He_assoc', 'Se_assoc')
    beta = GibbsEC(unique_keys=uk_equil)  # equilibrium parameters

    uk_kinet = ('Ha_assoc', 'Sa_assoc')
    bimol_barrier = EyringMA(unique_keys=uk_kinet)  # activation parameters

    eq = Equilibrium({'Fe+3', 'SCN-'}, {'FeSCN+2'}, beta)
    rsys = ReactionSystem(eq.as_reactions(kf=bimol_barrier))
    odesys, extra = get_odesys(rsys, include_params=False)
    pk, unique, p_units = map(extra.get, 'param_keys unique p_units'.split())
    assert sorted(unique) == sorted(uk_equil + uk_kinet)
    assert sorted(pk) == sorted(eyring_pk)
Beispiel #21
0
def test_get_odesys__linear_dependencies__PartiallySolvedSystem(preferred):
    import sympy
    from pyodesys.symbolic import PartiallySolvedSystem
    rsys = ReactionSystem.from_string('\n'.join(
        ['H2O -> H+ + OH-; 1e-4', 'OH- + H+ -> H2O; 1e10']))
    odesys, extra = get_odesys(rsys)
    c0 = {'H2O': 0, 'H+': 2e-7, 'OH-': 3e-7}
    h0max = extra['max_euler_step_cb'](0, c0)
    analytic_factory = extra['linear_dependencies']()
    y0 = {k: sympy.Symbol(k + '0') for k in rsys.substances}
    analytic_factory(None, {odesys[k]: v for k, v in y0.items()}, None, sympy)
    psys = PartiallySolvedSystem(odesys, analytic_factory)
    xout, yout, info = psys.integrate(1,
                                      c0,
                                      atol=1e-12,
                                      rtol=1e-10,
                                      first_step=h0max * 1e-12,
                                      integrator='cvode')
    c_reac = c0['H+'], c0['OH-']
    H2O_ref = binary_rev(xout, 1e10, 1e-4, c0['H2O'], max(c_reac), min(c_reac))
    assert np.allclose(yout[:, psys.names.index('H2O')], H2O_ref)
    assert np.allclose(yout[:, psys.names.index('H+')],
                       c0['H+'] + c0['H2O'] - H2O_ref)
    assert np.allclose(yout[:, psys.names.index('OH-')],
                       c0['OH-'] + c0['H2O'] - H2O_ref)
Beispiel #22
0
def test_get_odesys__linear_dependencies__preferred(substances):
    rsys = ReactionSystem.from_string(
        "\n".join(["H2O -> H+ + OH-; 1e-4", "OH- + H+ -> H2O; 1e10"]), substances
    )
    assert isinstance(rsys.substances, OrderedDict)
    odesys, extra = get_odesys(rsys)

    af_H2O_H = extra["linear_dependencies"](["H+", "H2O"])
    import sympy

    y0 = {k: sympy.Symbol(k + "0") for k in rsys.substances}
    af_H2O_H(
        None, {odesys[k]: v for k, v in y0.items()}, None, sympy
    )  # ensure idempotent
    exprs_H2O_H = af_H2O_H(None, {odesys[k]: v for k, v in y0.items()}, None, sympy)
    ref_H2O_H = {
        "H2O": y0["H2O"] + y0["OH-"] - odesys["OH-"],  # oxygen
        "H+": 2 * y0["H2O"]
        + y0["H+"]
        + y0["OH-"]
        - odesys["OH-"]
        - 2 * (y0["H2O"] + y0["OH-"] - odesys["OH-"]),  # hydrogen
    }
    for k, v in ref_H2O_H.items():
        assert (exprs_H2O_H[odesys[k]] - v) == 0
Beispiel #23
0
def test_create_odesys():
    rsys = ReactionSystem.from_string("""
    A -> B; 'k1'
    B + C -> P; 'k2'
    """, substance_factory=Substance)

    odesys, odesys_extra = create_odesys(rsys, unit_registry=SI_base_registry)

    tend = 10
    c0 = {'A': 1e-6, 'B': 0, 'C': 1, 'P': 0}
    p = dict(k1=3, k2=4)

    tend_units = tend*u.s
    p_units = {'k1': p['k1']/u.s, 'k2': p['k2']/u.M/u.s}
    c0_units = {k: v*u.molar for k, v in c0.items()}

    validation = odesys_extra['validate'](dict(c0_units, **p_units))
    P, = validation['not_seen']
    assert P.name == 'P'

    ref_rates = {'A': -p_units['k1']*c0_units['A'], 'P': p_units['k2']*c0_units['B']*c0_units['C']}
    ref_rates['B'] = -ref_rates['A'] - ref_rates['P']
    ref_rates['C'] = -ref_rates['P']
    assert validation['rates'] == ref_rates

    result1, result1_extra = odesys_extra['unit_aware_solve'](
        tend_units, c0_units, p_units, integrator='cvode')
    assert result1.info['success']

    result2 = odesys.integrate(tend, c0, p, integrator='cvode')
    assert np.allclose(result2.yout[-1, :], to_unitless(result1.yout[-1, :], u.molar))
Beispiel #24
0
def test_get_odesys__Expr_as_param__unique_as_param():
    def _eyring_pe_coupled(args, T, S, backend=math, **kwargs):
        freq, = args
        return freq*T/S

    EyringPreExpCoupled = Expr.from_callback(_eyring_pe_coupled, argument_names=('freq',),
                                             parameter_keys=('temperature', 'S_u'))

    def _k(args, T, backend=math, **kwargs):
        A, H, S = args
        return A*backend.exp(-(H - T*S)/(8.314511*T))

    EyringMA = MassAction.from_callback(_k, parameter_keys=('temperature',),
                                        argument_names=('Aa', 'Ha', 'Sa'))
    kb_h = 2.08e10
    rxn = Reaction({'A'}, {'B'}, EyringMA(unique_keys=('A_u', 'H_u', 'S_u')))
    rsys = ReactionSystem([rxn], ['A', 'B'])
    odesys2, extra2 = get_odesys(rsys, include_params=False,
                                 substitutions={'A_u': EyringPreExpCoupled(kb_h)})
    y0 = defaultdict(float, {'A': 7.0})
    rt = 293.15
    xout2, yout2, info2 = odesys2.integrate(5, y0, {'H_u': 107e3, 'S_u': 150, 'temperature': rt},
                                            integrator='cvode', atol=1e-12, rtol=1e-10, nsteps=1000)
    kref2 = kb_h*rt*np.exp(-(107e3 - rt*150)/(8.314511*rt))/150
    ref2 = y0['A']*np.exp(-kref2*xout2)
    assert np.allclose(yout2[:, 0], ref2)
    assert np.allclose(yout2[:, 1], y0['A'] - ref2)
Beispiel #25
0
def test_create_odesys():
    rsys = ReactionSystem.from_string("""
    A -> B; 'k1'
    B + C -> P; 'k2'
    """, substance_factory=Substance)

    odesys, odesys_extra = create_odesys(rsys, unit_registry=SI_base_registry)

    tend = 10
    c0 = {'A': 1e-6, 'B': 0, 'C': 1, 'P': 0}
    p = dict(k1=3, k2=4)

    tend_units = tend*u.s
    p_units = {'k1': p['k1']/u.s, 'k2': p['k2']/u.M/u.s}
    c0_units = {k: v*u.molar for k, v in c0.items()}

    validation = odesys_extra['validate'](dict(c0_units, **p_units))
    P, = validation['not_seen']
    assert P.name == 'P'

    ref_rates = {'A': -p_units['k1']*c0_units['A'], 'P': p_units['k2']*c0_units['B']*c0_units['C']}
    ref_rates['B'] = -ref_rates['A'] - ref_rates['P']
    ref_rates['C'] = -ref_rates['P']
    assert validation['rates'] == ref_rates

    result1, result1_extra = odesys_extra['unit_aware_solve'](
        tend_units, c0_units, p_units, integrator='cvode')
    assert result1.info['success']

    result2 = odesys.integrate(tend, c0, p, integrator='cvode')
    assert np.allclose(result2.yout[-1, :], to_unitless(result1.yout[-1, :], u.molar))
Beispiel #26
0
def test_get_native__conc_roots():
    M, s = u.M, u.s
    rsys = ReactionSystem.from_string("2 O3 -> 3 O2; 'k2'")
    u_reg = SI_base_registry.copy()
    odesys, extra = get_odesys(rsys, include_params=False, unit_registry=u_reg)
    c0 = {"O3": 4.2e-3 * M, "O2": 0 * M}
    cr = ["O2"]
    native = get_native(rsys, odesys, "cvode", conc_roots=cr)
    tend = 1e5 * u.s
    params = {"k2": logspace_from_lin(1e-3 / M / s, 1e3 / M / s, 14)}
    tgt_O2 = 1e-3 * M
    results = native.integrate(
        tend,
        c0,
        params,
        integrator="native",
        return_on_root=True,
        special_settings=[unitless_in_registry(tgt_O2, u_reg)],
    )
    assert len(results) == params["k2"].size
    # dydt = -p*y**2
    # 1/y0 - 1/y = -2*pt
    # t = 1/2/p*(1/y - 1/y0)
    tgt_O3 = c0["O3"] - 2 / 3 * tgt_O2
    for r in results:
        ref = rescale(
            1 / 2 / r.named_param("k2") * (1 / tgt_O3 - 1 / c0["O3"]), u.s)
        assert allclose(r.xout[-1], ref, rtol=1e-6)
Beispiel #27
0
def test_get_odesys__Eyring_2nd_order_linearly_ramped_temperature():
    from scipy.special import expi

    def analytic_unit0(t, k, m, dH, dS):
        R = 8.314472
        kB = 1.3806504e-23
        h = 6.62606896e-34
        A = kB/h*np.exp(dS/R)
        B = dH/R
        return k*np.exp(B*(k*t + 2*m)/(m*(k*t + m)))/(
            A*(-B**2*np.exp(B/(k*t + m))*expi(-B/(k*t + m)) - B*k*t - B*m + k**2*t**2 + 2*k*m*t + m**2)*np.exp(B/m) +
            (A*B**2*np.exp(B/m)*expi(-B/m) - A*m*(-B + m) + k*np.exp(B/m))*np.exp(B/(k*t + m))
        )

    T_K = 290
    dTdt_Ks = 3
    dH = 80e3
    dS = 10
    rsys1 = ReactionSystem.from_string("""
    2 NO2 -> N2O4; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS))

    NO2_M = 1.0
    init_cond = dict(
        NO2=NO2_M*u.M,
        N2O4=0*u.M
    )
    t = 20*u.second

    def check(rsys):
        odes, extra = get_odesys(rsys, unit_registry=SI_base_registry, constants=const, substitutions={
            'temperature': RampedTemp([T_K*u.K, dTdt_Ks*u.K/u.s])})
        for odesys in [odes, odes.as_autonomous()]:
            res = odesys.integrate(t, init_cond, integrator='cvode')
            t_sec = to_unitless(res.xout, u.second)
            NO2_ref = analytic_unit0(t_sec, dTdt_Ks, T_K, dH, dS)
            cmp = to_unitless(res.yout, u.M)
            ref = np.empty_like(cmp)
            ref[:, odesys.names.index('NO2')] = NO2_ref
            ref[:, odesys.names.index('N2O4')] = (NO2_M - NO2_ref)/2
            assert np.allclose(cmp, ref)

    check(rsys1)
    rsys2 = ReactionSystem.from_string("""
    2 NO2 -> N2O4; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS))
    check(rsys2)
Beispiel #28
0
def test_get_odesys__max_euler_step_cb():
    rsys = ReactionSystem.from_string('\n'.join(['H2O -> H+ + OH-; 1e-4', 'OH- + H+ -> H2O; 1e10']))
    odesys, extra = get_odesys(rsys)
    r1 = 1.01e-4
    r2 = 6e-4
    dH2Odt = r2 - r1
    euler_ref = 2e-7/dH2Odt
    assert abs(extra['max_euler_step_cb'](0, {'H2O': 1.01, 'H+': 2e-7, 'OH-': 3e-7}) - euler_ref)/euler_ref < 1e-8
Beispiel #29
0
def test_get_odesys__max_euler_step_cb():
    rsys = ReactionSystem.from_string('\n'.join(['H2O -> H+ + OH-; 1e-4', 'OH- + H+ -> H2O; 1e10']))
    odesys, extra = get_odesys(rsys)
    r1 = 1.01e-4
    r2 = 6e-4
    dH2Odt = r2 - r1
    euler_ref = 2e-7/dH2Odt
    assert abs(extra['max_euler_step_cb'](0, {'H2O': 1.01, 'H+': 2e-7, 'OH-': 3e-7}) - euler_ref)/euler_ref < 1e-8
Beispiel #30
0
def test_get_native__Radiolytic__named_parameter__units(dep_scaling):
    rsys = ReactionSystem.from_string("""
    -> H; Radiolytic(2*per100eV)
    H + H -> H2; 'k2'
    """,
                                      checks=('substance_keys', 'duplicate',
                                              'duplicate_names'))
    gval = 2 * u.per100eV

    from pyodesys.symbolic import ScaledSys
    kwargs = {} if dep_scaling == 1 else dict(SymbolicSys=ScaledSys,
                                              dep_scaling=dep_scaling)
    odesys, extra = get_odesys(rsys,
                               include_params=False,
                               unit_registry=SI_base_registry,
                               **kwargs)
    c0 = {'H': 42e-6 * u.molar, 'H2': 17e3 * u.nanomolar}
    native = get_native(rsys, odesys, 'cvode')
    tend = 7 * 60 * u.minute
    params = {
        'doserate': 314 * u.Gy / u.hour,
        'k2': 53 / u.molar / u.minute,
        'density': 998 * u.g / u.dm3
    }
    result = native.integrate(tend,
                              c0,
                              params,
                              atol=1e-15,
                              rtol=1e-15,
                              integrator='cvode',
                              nsteps=8000)
    assert result.info['success']

    def analytic_H(t, p, k, H0):
        # dH/dt = p - k2*H**2
        x0 = np.sqrt(2) * np.sqrt(p)
        x1 = x0
        x2 = np.sqrt(k)
        x3 = t * x1 * x2
        x4 = H0 * x2
        x5 = np.sqrt(x0 + 2 * x4)
        x6 = np.sqrt(-1 / (2 * H0 * x2 - x0))
        x7 = x5 * x6 * np.exp(x3)
        x8 = np.exp(-x3) / (x5 * x6)
        return x1 * (x7 - x8) / (2 * x2 * (x7 + x8))

    t_ul = to_unitless(result.xout, u.s)
    p_ul = to_unitless(params['doserate'] * params['density'] * gval,
                       u.micromolar / u.s)
    ref_H_uM = analytic_H(t_ul, p_ul,
                          to_unitless(params['k2'], 1 / u.micromolar / u.s),
                          to_unitless(c0['H'], u.micromolar))
    ref_H2_uM = to_unitless(c0['H2'], u.micromolar) + to_unitless(
        c0['H'], u.micromolar) / 2 + t_ul * p_ul / 2 - ref_H_uM / 2
    assert np.allclose(to_unitless(result.named_dep('H'), u.micromolar),
                       ref_H_uM)
    assert np.allclose(to_unitless(result.named_dep('H2'), u.micromolar),
                       ref_H2_uM)
Beispiel #31
0
def test_get_native__named_parameter__units(dep_scaling):
    rsys = ReactionSystem.from_string(
        """
    -> H; 'p'
    H + H -> H2; 'k2'
    """,
        checks=("substance_keys", "duplicate", "duplicate_names"),
    )
    from pyodesys.symbolic import ScaledSys

    kwargs = ({} if dep_scaling == 1 else dict(SymbolicSys=ScaledSys,
                                               dep_scaling=dep_scaling))
    odesys, extra = get_odesys(rsys,
                               include_params=False,
                               unit_registry=SI_base_registry,
                               **kwargs)
    c0 = {"H": 42e-6 * u.molar, "H2": 17 * u.micromolar}
    native = get_native(rsys, odesys, "cvode")
    tend = 7 * 60 * u.minute
    g_rho_Ddot = g, rho, Ddot = 2 * u.per100eV, 998 * u.g / u.dm3, 314 * u.Gy / u.hour
    params = {"p": reduce(mul, g_rho_Ddot), "k2": 53 / u.molar / u.minute}
    result = native.integrate(tend,
                              c0,
                              params,
                              atol=1e-15,
                              rtol=1e-15,
                              integrator="cvode",
                              nsteps=16000)
    assert result.info["success"]

    def analytic_H(t, p, k, H0):
        # dH/dt = p - k2*H**2
        x0 = np.sqrt(2) * np.sqrt(p)
        x1 = x0
        x2 = np.sqrt(k)
        x3 = t * x1 * x2
        x4 = H0 * x2
        x5 = np.sqrt(x0 + 2 * x4)
        x6 = np.sqrt(-1 / (2 * H0 * x2 - x0))
        x7 = x5 * x6 * np.exp(x3)
        x8 = np.exp(-x3) / (x5 * x6)
        return x1 * (x7 - x8) / (2 * x2 * (x7 + x8))

    t_ul = to_unitless(result.xout, u.s)
    p_ul = to_unitless(params["p"], u.micromolar / u.s)
    ref_H_uM = analytic_H(
        t_ul,
        p_ul,
        to_unitless(params["k2"], 1 / u.micromolar / u.s),
        to_unitless(c0["H"], u.micromolar),
    )
    ref_H2_uM = (to_unitless(c0["H2"], u.micromolar) +
                 to_unitless(c0["H"], u.micromolar) / 2 + t_ul * p_ul / 2 -
                 ref_H_uM / 2)
    assert np.allclose(to_unitless(result.named_dep("H"), u.micromolar),
                       ref_H_uM)
    assert np.allclose(to_unitless(result.named_dep("H2"), u.micromolar),
                       ref_H2_uM)
Beispiel #32
0
def test_chained_parameter_variation_from_ReactionSystem():
    g_E_mol_J = 2.1e-7
    rsys = ReactionSystem.from_string(
        """
        (H2O) -> e-(aq) + H+ + OH; Radiolytic(%.2e*mol/J)
        2 OH -> H2O2; 3.6e9/M/s
        H+ + OH- -> H2O; 1.4e11/M/s
        H2O -> H+ + OH-; 1.4e-3/s
        N2O + e-(aq) -> N2 + O-; 9.6e9/M/s
        O- + H+ -> OH; 1e11/M/s
        """ % g_E_mol_J  # neglecting a large body of reactions (just a test-case after all)
    )
    ureg = SI_base_registry
    field_u = get_derived_unit(ureg, 'doserate') * get_derived_unit(ureg, 'density')
    rd = ReactionDiffusion.from_ReactionSystem(rsys, fields=[[0*field_u]], unit_registry=ureg,
                                               param_names=['doserate'])
    dens_kg_dm3 = 0.998
    odesys = rd._as_odesys(
        variables_from_params=dict(
            density=lambda self, params: dens_kg_dm3*1e3*u.kg/u.m**3
        )
    )
    npoints = 5
    durations = [59*u.second, 42*u.minute, 2*u.hour]
    doserates = [135*u.Gy/u.s, 11*u.Gy/u.s, 180*u.Gy/u.minute]
    M = u.molar
    ic = defaultdict(lambda: 0*M, {'H2O': 55.4*M, 'H+': 1e-7*M, 'OH-': 1e-7*M, 'N2O': 20e-3*M})

    result = odesys.chained_parameter_variation(durations, ic, {'doserate': doserates}, npoints=npoints)
    ref_xout_s = [0]
    for dur in map(lambda dur: to_unitless(dur, u.s), durations):
        ref_xout_s += list(np.linspace(ref_xout_s[-1], ref_xout_s[-1] + dur, npoints+1)[1:])
    assert allclose(result.xout, ref_xout_s*u.s)

    N2_M = to_unitless(result.named_dep('N2'), u.M)
    H2O2_M = to_unitless(result.named_dep('H2O2'), u.M)

    e_accum_molar = 0
    for i, (dur, dr) in enumerate(zip(durations, doserates)):
        dur_s = to_unitless(dur, u.s)
        dr_Gy_s = to_unitless(dr, u.Gy/u.s)
        local_ts = np.linspace(0, dur_s, npoints+1)
        # local_ic = {k: result.named_dep(k)[i*npoints] for k in odesys.names}
        for j, (lt, ld) in enumerate(zip(local_ts[1:], np.diff(local_ts))):
            e_accum_molar += ld*g_E_mol_J*dr_Gy_s*dens_kg_dm3
            assert abs(N2_M[i*npoints + j + 1] - e_accum_molar)/e_accum_molar < 1e-3
            assert abs(H2O2_M[i*npoints + j + 1] - e_accum_molar)/e_accum_molar < 1e-3

    res2 = odesys.integrate(durations[0], ic, {'doserate': doserates[0]}, integrator='cvode')
    dr2 = res2.params[res2.odesys.param_names.index('doserate')]
    assert np.asarray(res2.params).shape[-1] == len(odesys.param_names)
    assert allclose(dr2, doserates[0])
    assert allclose(res2.xout[-1], durations[0])
    assert allclose(res2.named_dep('N2')[-1], durations[0]*doserates[0]*g_E_mol_J*u.mol/u.J*dens_kg_dm3*u.kg/u.dm3)
    to_unitless(res2.xout, u.s)
    to_unitless(res2.yout, u.molar)
    to_unitless(dr2, u.Gy/u.s)
Beispiel #33
0
def test_get_odesys__Eyring_2nd_order():
    R = 8.314472
    T_K = 300
    dH = 80e3
    dS = 10
    rsys1b = ReactionSystem.from_string("""
    NO + Br -> NOBr; EyringParam(dH={dH}*J/mol, dS={dS}*J/K/mol)
    """.format(dH=dH, dS=dS))
    c0 = 1  # mol/dm3 === 1000 mol/m3
    kbref = 20836643994.118652*T_K*np.exp(-(dH - T_K*dS)/(R*T_K))/c0
    NO0_M = 1.5
    Br0_M = 0.7
    init_cond = dict(
        NOBr=0*u.M,
        NO=NO0_M*u.M,
        Br=Br0_M*u.M
    )
    t = 5*u.second
    params = dict(
        temperature=T_K*u.K
    )

    def analytic_b(t):
        U, V = NO0_M, Br0_M
        d = U - V
        return (U*(1 - np.exp(-kbref*t*d)))/(U/V - np.exp(-kbref*t*d))

    def check(rsys):
        odesys, extra = get_odesys(rsys, unit_registry=SI_base_registry, constants=const)
        res = odesys.integrate(t, init_cond, params, integrator='cvode')
        t_sec = to_unitless(res.xout, u.second)
        NOBr_ref = analytic_b(t_sec)
        cmp = to_unitless(res.yout, u.M)
        ref = np.empty_like(cmp)
        ref[:, odesys.names.index('NOBr')] = NOBr_ref
        ref[:, odesys.names.index('Br')] = Br0_M - NOBr_ref
        ref[:, odesys.names.index('NO')] = NO0_M - NOBr_ref
        assert np.allclose(cmp, ref)

    check(rsys1b)
    rsys2b = ReactionSystem.from_string("""
    NO + Br -> NOBr; MassAction(EyringHS([{dH}*J/mol, {dS}*J/K/mol]))
    """.format(dH=dH, dS=dS))
    check(rsys2b)
Beispiel #34
0
def test_get_odesys_2():
    g = Radiolytic([3.14])
    a = Substance('A')
    b = Substance('B')
    r = Reaction({'A': 1}, {'B': 1}, param=g)
    rsys = ReactionSystem([r], [a, b])
    odesys = get_odesys(rsys, include_params=True)[0]
    c0 = {
        'A': 1.0,
        'B': 3.0,
    }
    t = np.linspace(0.0, .1)
    xout, yout, info = odesys.integrate(t, rsys.as_per_substance_array(c0),
                                        {'doserate': 2.72, 'density': .998})
    yref = np.zeros((t.size, 2))
    k = 3.14*2.72*.998
    yref[:, 0] = 1 - k*t
    yref[:, 1] = 3 + k*t
    assert np.allclose(yout, yref)
def test_from_ReactionSystem__g_values():
    from chempy import ReactionSystem as RS
    rs = RS.from_string('-> H + OH; Radiolytic(2.1e-7)', checks=())
    rd = ReactionDiffusion.from_ReactionSystem(rs, variables={'density': 998, 'doserate': 0.15})
    gv = rd.g_values
    assert len(gv) == 1
    assert np.allclose(gv[0], rs.as_per_substance_array({'H': 2.1e-7, 'OH': 2.1e-7}))
    assert len(rd.fields) == 1
    assert len(rd.fields[0]) == 1
    assert np.allclose(rd.fields[0][0], 998*0.15)
Beispiel #36
0
def test_get_odesys__Equilibrium_as_reactions():
    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, integrator='cvode', atol=1e-11, rtol=1e-12)
    cmplx_ref = binary_rev(tout, 3, 3.0/100, init_conc['FeSCN+2'], init_conc['Fe+3'], init_conc['SCN-'])
    assert np.allclose(Cout[:, 2], cmplx_ref)
Beispiel #37
0
def test_get_odesys_2():
    g = Radiolytic([3.14])
    a = Substance('A')
    b = Substance('B')
    r = Reaction({'A': 1}, {'B': 1}, param=g)
    rsys = ReactionSystem([r], [a, b])
    odesys = get_odesys(rsys, include_params=True)[0]
    c0 = {
        'A': 1.0,
        'B': 3.0,
    }
    t = np.linspace(0.0, .1)
    xout, yout, info = odesys.integrate(t, rsys.as_per_substance_array(c0),
                                        {'doserate': 2.72, 'density': .998})
    yref = np.zeros((t.size, 2))
    k = 3.14*2.72*.998
    yref[:, 0] = 1 - k*t
    yref[:, 1] = 3 + k*t
    assert np.allclose(yout, yref)
def get_rsys():
    r1 = Reaction({'A'}, {'B'},
                  MassAction([4. / 100], unique_keys=['k1']),
                  name='R1: A cons.')
    r2 = Reaction({'B', 'C'}, {'A', 'C'},
                  MassAction([1e4], unique_keys=['k2']),
                  name='R2: A reform.')
    r3 = Reaction({'B': 2}, {'B', 'C'},
                  MassAction([3e7], unique_keys=['k3']),
                  name='R3: C form.')
    return ReactionSystem([r1, r2, r3])
Beispiel #39
0
def test_get_ode__Radiolytic():
    rad = Radiolytic([2.4e-7])
    rxn = Reaction({"A": 4, "B": 1}, {"C": 3, "D": 2}, rad)
    rsys = ReactionSystem([rxn], "A B C D")
    odesys = get_odesys(rsys, include_params=True)[0]
    c = {"A": 3, "B": 5, "C": 11, "D": 13}
    x, y, p = odesys.to_arrays(-37, c, {"doserate": 0.4, "density": 0.998})
    fout = odesys.f_cb(x, y, p)
    r = 2.4e-7 * 0.4 * 0.998
    ref = [-4 * r, -r, 3 * r, 2 * r]
    assert np.all(abs((fout - ref) / ref) < 1e-14)
Beispiel #40
0
def test_get_odesys_2():
    g = Radiolytic([3.14])
    a = Substance("A")
    b = Substance("B")
    r = Reaction({"A": 1}, {"B": 1}, param=g)
    rsys = ReactionSystem([r], [a, b])
    odesys = get_odesys(rsys, include_params=True)[0]
    c0 = {
        "A": 1.0,
        "B": 3.0,
    }
    t = np.linspace(0.0, 0.1)
    xout, yout, info = odesys.integrate(
        t, rsys.as_per_substance_array(c0), {"doserate": 2.72, "density": 0.998}
    )
    yref = np.zeros((t.size, 2))
    k = 3.14 * 2.72 * 0.998
    yref[:, 0] = 1 - k * t
    yref[:, 1] = 3 + k * t
    assert np.allclose(yout, yref)
Beispiel #41
0
def test_get_ode__Radiolytic():
    rad = Radiolytic([2.4e-7])
    rxn = Reaction({'A': 4, 'B': 1}, {'C': 3, 'D': 2}, rad)
    rsys = ReactionSystem([rxn], 'A B C D')
    odesys = get_odesys(rsys, include_params=True)[0]
    c = {'A': 3, 'B': 5, 'C': 11, 'D': 13}
    x, y, p = odesys.to_arrays(-37, c, {'doserate': 0.4, 'density': 0.998})
    fout = odesys.f_cb(x, y, p)
    r = 2.4e-7*0.4*0.998
    ref = [-4*r, -r, 3*r, 2*r]
    assert np.all(abs((fout - ref)/ref) < 1e-14)
Beispiel #42
0
def test_get_ode__TPoly():
    rate = MassAction(ShiftedTPoly([273.15*u.K, 10/u.molar/u.s, 2/u.molar/u.s/u.K]))
    rxn = Reaction({'A': 1, 'B': 1}, {'C': 3, 'D': 2}, rate, {'A': 3})
    rsys = ReactionSystem([rxn], 'A B C D')
    odesys = get_odesys(rsys, unit_registry=SI_base_registry)[0]
    conc = {'A': 3*u.molar, 'B': 5*u.molar, 'C': 11*u.molar, 'D': 13*u.molar}
    x, y, p = odesys.to_arrays(-37*u.second, conc, {'temperature': 298.15*u.kelvin})
    fout = odesys.f_cb(x, y, p)
    r = 3*5*(10+2*25)*1000  # mol/m3/s
    ref = [-4*r, -r, 3*r, 2*r]
    assert np.all(abs((fout - ref)/ref) < 1e-14)
Beispiel #43
0
def test_get_ode__ArrheniusParam():
    rxn = Reaction({'A': 1}, {'B': 1}, None)
    rxn.param = ArrheniusParam(1e10, 40e3)
    rsys = ReactionSystem([rxn], 'A B')
    odesys = get_odesys(rsys, include_params=True)[0]
    conc = {'A': 3, 'B': 5}
    x, y, p = odesys.to_arrays(-37, conc, {'temperature': 200})
    fout = odesys.f_cb(x, y, p)
    ref = 3*1e10*np.exp(-40e3/8.314472/200)
    assert np.all(abs((fout[:, 0] + ref)/ref) < 1e-14)
    assert np.all(abs((fout[:, 1] - ref)/ref) < 1e-14)
Beispiel #44
0
def test_EyringMassAction():
    args = kB_h_times_exp_dS_R, dH_over_R, c0 = 1.2e11/273.15, 40e3/8, 1
    ama = MassAction(Eyring(args, ('Sfreq', 'Hact')))
    rxn1 = Reaction({'A': 2, 'B': 1}, {'C': 1}, ama, {'B': 1})
    T_ = 'temperature'

    def ref(v):
        return v.get('Sfreq', 1.2e11/273.15)*v[T_]*math.exp(-v.get('Hact', 40e3/8)/v[T_])*v['B']*v['A']**2

    for params in [(11., 13., 17., 311.2),
                   (12, 8, 5, 270)]:
        var = dict(zip(['A', 'B', 'C', T_], params))
        ref_val = ref(var)
        assert abs((ama(var, reaction=rxn1) - ref_val)/ref_val) < 1e-14

    with pytest.raises(ValueError):
        MassAction(Eyring([1, 1, 1, 1, 1]))

    # assert ama.as_mass_action({T_: 273.15}).args[0] == 1.2e11*math.exp(-40e3/8/273.15)

    ama2 = MassAction(Eyring4([1.2e11/273, 40e3/8, 1.2, 1e3], ('Sfreq', 'Hact', 'Sref', 'Href')))
    rxn2 = Reaction({'C': 1}, {'A': 2, 'B': 2}, ama2)
    var2 = {'C': 29, 'temperature': 273}

    def ref2(var):
        return var['C']*var.get('temperature', 273)*var.get('Sfreq', 1.2e11/273)/var.get('Sref', 1.2)*math.exp(
            (var.get('Href', 1e3) - var.get('Hact', 5e3))/var.get('temperature', 273))

    r2 = ref2(var2)
    assert abs((ama2(var2, reaction=rxn2) - r2)/r2) < 1e-14

    rsys = ReactionSystem([rxn1, rxn2])
    var3 = {'A': 11, 'B': 13, 'C': 17, 'temperature': 298, 'Sfreq': 1.2e11/298}
    rates = rsys.rates(var3)
    rf3 = ref(var3)
    rb3 = ref2(var3)
    ref_rates = {'A': 2*(rb3 - rf3), 'B': 2*(rb3 - rf3), 'C': rf3 - rb3}
    for k, v in ref_rates.items():
        assert abs((rates[k] - v)/v) < 1e-14
def test_from_ReactionSystem__g_values__units():
    from chempy import ReactionSystem as RS
    from chempy.units import SI_base_registry, default_units as u
    rs = RS.from_string('-> H + OH; Radiolytic(2.1*per100eV)', checks=())
    variables = {'density': .998 * u.kg/u.dm3, 'doserate': 0.15*u.Gy/u.s}
    rd = ReactionDiffusion.from_ReactionSystem(rs, variables=variables, unit_registry=SI_base_registry)
    gv = rd.g_values
    per100eV_as_mol_per_joule = 1.0364268556366418e-07
    ref = 2.1 * per100eV_as_mol_per_joule
    assert len(gv) == 1
    assert np.allclose(gv[0], rs.as_per_substance_array({'H': ref, 'OH': ref}))
    assert len(rd.fields) == 1
    assert len(rd.fields[0]) == 1
    assert np.allclose(rd.fields[0][0], 998*0.15)
Beispiel #46
0
def test_get_native__a_substance_no_composition(solve):
    rsys = ReactionSystem.from_string('\n'.join(['H2O -> H2O+ + e-(aq); 1e-8', 'e-(aq) + H2O+ -> H2O; 1e10']))
    odesys, extra = get_odesys(rsys)
    c0 = {'H2O': 0, 'H2O+': 2e-9, 'e-(aq)': 3e-9}
    if len(solve) > 0:
        from pyodesys.symbolic import PartiallySolvedSystem
        odesys = PartiallySolvedSystem(odesys, extra['linear_dependencies'](solve))
    odesys = get_native(rsys, odesys, 'gsl')
    xout, yout, info = odesys.integrate(1, c0, atol=1e-15, rtol=1e-15, integrator='gsl')
    c_reac = c0['H2O+'], c0['e-(aq)']
    H2O_ref = binary_rev(xout, 1e10, 1e-4, c0['H2O'], max(c_reac), min(c_reac))
    assert np.allclose(yout[:, odesys.names.index('H2O')], H2O_ref)
    assert np.allclose(yout[:, odesys.names.index('H2O+')], c0['H2O+'] + c0['H2O'] - H2O_ref)
    assert np.allclose(yout[:, odesys.names.index('e-(aq)')], c0['e-(aq)'] + c0['H2O'] - H2O_ref)
Beispiel #47
0
def test_create_odesys__Radiolytic():
    rsys1 = ReactionSystem.from_string("""
    -> e-(aq); Radiolytic.fk('g_emaq')
    """, checks=())
    ic1 = {'e-(aq)': 0.0}
    t1 = 5
    p1 = dict(
        g_emaq=42.0,
        doserate=17.0,
        density=5.0
    )
    odesys1, odesys_extra = create_odesys(rsys1)
    result1 = odesys1.integrate(t1, ic1, p1)
    yref1 = result1.xout*p1['g_emaq']*p1['doserate']*p1['density']
    assert np.allclose(yref1, result1.yout.squeeze())
Beispiel #48
0
def test_get_native__Radiolytic__named_parameter__units(scaling_density):
    scaling, density = scaling_density
    rsys = ReactionSystem.from_string("""
    -> H; Radiolytic(2*per100eV)
    H + H -> H2; 'k2'
    """, checks=('substance_keys', 'duplicate', 'duplicate_names'))
    gval = 2*u.per100eV

    from pyodesys.symbolic import ScaledSys
    kwargs = {} if scaling == 1 else dict(SymbolicSys=ScaledSys, dep_scaling=scaling)
    dens = {'density': 998*u.g/u.dm3}
    odesys, extra = get_odesys(rsys, include_params=False, substitutions=dens if density else {},
                               unit_registry=SI_base_registry, **kwargs)
    c0 = {'H': 42e-6*u.molar, 'H2': 17e3*u.nanomolar}
    native = get_native(rsys, odesys, 'cvode')
    tend = 7*60*u.minute
    params = {'doserate': 314*u.Gy/u.hour, 'k2': 53/u.molar/u.minute}
    if not density:
        params.update(dens)
    result = native.integrate(tend, c0, params, atol=1e-15, rtol=1e-15, integrator='cvode', nsteps=8000)
    assert result.info['success']

    def analytic_H(t, p, k, H0):
        # dH/dt = p - k2*H**2
        x0 = np.sqrt(2)*np.sqrt(p)
        x1 = x0
        x2 = np.sqrt(k)
        x3 = t*x1*x2
        x4 = H0*x2
        x5 = np.sqrt(x0 + 2*x4)
        x6 = np.sqrt(-1/(2*H0*x2 - x0))
        x7 = x5*x6*np.exp(x3)
        x8 = np.exp(-x3)/(x5*x6)
        return x1*(x7 - x8)/(2*x2*(x7 + x8))

    t_ul = to_unitless(result.xout, u.s)
    p_ul = to_unitless(params['doserate']*dens['density']*gval, u.micromolar/u.s)
    ref_H_uM = analytic_H(
        t_ul,
        p_ul,
        to_unitless(params['k2'], 1/u.micromolar/u.s),
        to_unitless(c0['H'], u.micromolar)
    )
    ref_H2_uM = to_unitless(c0['H2'], u.micromolar) + to_unitless(c0['H'], u.micromolar)/2 + t_ul*p_ul/2 - ref_H_uM/2
    assert np.allclose(to_unitless(result.named_dep('H'), u.micromolar), ref_H_uM)
    assert np.allclose(to_unitless(result.named_dep('H2'), u.micromolar), ref_H2_uM)
Beispiel #49
0
def test_get_odesys__linear_dependencies__preferred(substances):
    rsys = ReactionSystem.from_string('\n'.join(['H2O -> H+ + OH-; 1e-4', 'OH- + H+ -> H2O; 1e10']), substances)
    assert isinstance(rsys.substances, OrderedDict)
    odesys, extra = get_odesys(rsys)

    af_H2O_H = extra['linear_dependencies'](['H+', 'H2O'])
    import sympy
    y0 = {k: sympy.Symbol(k+'0') for k in rsys.substances}
    af_H2O_H(None, {odesys[k]: v for k, v in y0.items()}, None, sympy)  # ensure idempotent
    exprs_H2O_H = af_H2O_H(None, {odesys[k]: v for k, v in y0.items()}, None, sympy)
    ref_H2O_H = {
        'H2O': y0['H2O'] + y0['OH-'] - odesys['OH-'],  # oxygen
        'H+': 2*y0['H2O'] + y0['H+'] + y0['OH-'] - odesys['OH-'] - 2*(
            y0['H2O'] + y0['OH-'] - odesys['OH-'])  # hydrogen
    }
    for k, v in ref_H2O_H.items():
        assert (exprs_H2O_H[odesys[k]] - v) == 0
Beispiel #50
0
def test_get_odesys_rsys_with_units__named_params():
    rsys = ReactionSystem.from_string("""
    A -> B; 'k1'
    B + C -> P; 'k2'
    """, substance_factory=Substance)
    odesys, extra = get_odesys(rsys, include_params=False, unit_registry=SI_base_registry)
    tend = 10
    tend_units = tend*u.s
    c0 = {'A': 1e-6, 'B': 0, 'C': 1, 'P': 0}
    p = {'k1': 3, 'k2': 4}
    p_units = {'k1': 3/u.s, 'k2': 4/u.M/u.s}
    c0_units = {k: v*u.molar for k, v in c0.items()}
    result1 = odesys.integrate(tend_units, c0_units, p_units, integrator='odeint')
    assert result1.info['success']

    with pytest.raises(Exception):
        odesys.integrate(tend, c0, p, integrator='odeint')