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)
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
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)
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)
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)
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)
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
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)
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])
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)
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)
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)
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)
def test_ode_with_global_parameters(): ratex = MassAction(Arrhenius([1e10, 40e3/8.3145])) rxn = Reaction({'A': 1}, {'B': 1}, ratex) rsys = ReactionSystem([rxn], 'A B') odesys, extra = get_odesys(rsys, include_params=False) param_keys, unique_keys, p_units = map(extra.get, 'param_keys unique p_units'.split()) conc = {'A': 3, 'B': 5} x, y, p = odesys.to_arrays(-37, conc, {'temperature': 298.15}) fout = odesys.f_cb(x, y, p) ref = 3*1e10*np.exp(-40e3/8.3145/298.15) assert np.all(abs((fout[:, 0] + ref)/ref) < 1e-14) assert np.all(abs((fout[:, 1] - ref)/ref) < 1e-14)
def get_rsys(): r1 = Reaction( {"A"}, {"B"}, MassAction([4.0 / 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])
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
def test_get_ode__Radiolytic__substitutions(): rad = Radiolytic([2.4e-7]) rxn = Reaction({'A': 4, 'B': 1}, {'C': 3, 'D': 2}, rad) rsys = ReactionSystem([rxn], 'A B C D') substance_rho = Density([1, -1e-3, 273.15]) odesys = get_odesys(rsys, include_params=True, substitutions={'density': substance_rho})[0] conc = {'A': 3, 'B': 5, 'C': 11, 'D': 13} state = {'doserate': 0.4, 'temperature': 298.15} x, y, p = odesys.to_arrays(-37, conc, state) fout = odesys.f_cb(x, y, p) r = 2.4e-7*0.4*substance_rho({'temperature': 298.15}) ref = [-4*r, -r, 3*r, 2*r] assert np.all(abs((fout - ref)/ref) < 1e-14)
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_get_ode__Radiolytic__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') odesys = get_odesys(rsys, include_params=True, 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, { 'doserate': 0.4*u.gray/u.second, 'density': 0.998*u.kg/u.decimetre**3 }) fout = odesys.f_cb(x, y, p) # f_cb does not carry any units r = 2.4e-7*0.4*0.998*1e3 # mol/m3 ref = [-4*r, -r, 3*r, 2*r] assert np.all(abs((fout - ref)/ref) < 1e-14)
def test_get_ode__Radiolytic__substitutions(): rad = Radiolytic([2.4e-7]) rxn = Reaction({"A": 4, "B": 1}, {"C": 3, "D": 2}, rad) rsys = ReactionSystem([rxn], "A B C D") substance_rho = Density([1, -1e-3, 273.15]) odesys = get_odesys( rsys, include_params=True, substitutions={"density": substance_rho} )[0] conc = {"A": 3, "B": 5, "C": 11, "D": 13} state = {"doserate": 0.4, "temperature": 298.15} x, y, p = odesys.to_arrays(-37, conc, state) fout = odesys.f_cb(x, y, p) r = 2.4e-7 * 0.4 * substance_rho({"temperature": 298.15}) ref = [-4 * r, -r, 3 * r, 2 * r] assert np.all(abs((fout - ref) / ref) < 1e-14)
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-": 0.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)
def test_chained_parameter_variation(): ratex = MassAction(Arrhenius([1e10, 63e3 / 8.3145])) rxn = Reaction({'A': 1}, {'B': 1}, ratex) rsys = ReactionSystem([rxn], 'A B') odesys, extra = get_odesys(rsys, include_params=False) param_keys, unique_keys, p_units = map(extra.get, 'param_keys unique p_units'.split()) conc = {'A': 3.17, 'B': 5.03} Ts = (294, 304, 317) times = [3.1, 2.1, 5.3] kw = dict(integrator='cvode', atol=1e-12, rtol=1e-13, first_step=1e-14) tout, cout, info = chained_parameter_variation(odesys, times, conc, {'temperature': Ts}, {}, integrate_kwargs=kw) assert len(info['nfev']) == 3 assert info['nfev'][0] > 2 assert info['nfev'][1] > 2 assert info['nfev'][2] > 2 assert np.all(np.diff(tout) > 0) tout1 = tout[tout <= times[0]] tout23 = tout[tout > times[0]] tout2 = tout23[tout23 <= times[0] + times[1]] tout3 = tout23[tout23 > times[0] + times[1]] def _ref(y0, x, T, x0): k = 1e10 * np.exp(-63e3 / 8.3145 / T) return y0 * np.exp(-k * (x - x0)) Aref1 = _ref(conc['A'], tout1, Ts[0], tout1[0]) Bref1 = conc['B'] + conc['A'] - Aref1 Aref2 = _ref(Aref1[-1], tout2, Ts[1], tout1[-1]) Bref2 = Bref1[-1] + Aref1[-1] - Aref2 Aref3 = _ref(Aref2[-1], tout3, Ts[2], tout2[-1]) Bref3 = Bref2[-1] + Aref2[-1] - Aref3 cref = np.concatenate([ np.vstack((a, b)).T for a, b in [(Aref1, Bref1), (Aref2, Bref2), (Aref3, Bref3)] ]) forgive = 27 * 1.1 assert np.allclose(cref, cout, atol=kw['atol'] * forgive, rtol=kw['rtol'] * forgive)
def test_get_odesys__time_dep_temperature(): import sympy as sp def refA(t, A0, A, Ea_over_R, T0, dTdt): T = (T0 + dTdt * t) d_Ei = sp.Ei(-Ea_over_R / T0).n(100).round(90) - sp.Ei( -Ea_over_R / T).n(100).round(90) d_Texp = T0 * sp.exp(-Ea_over_R / T0) - T * sp.exp(-Ea_over_R / T) return A0 * sp.exp(A / dTdt * (Ea_over_R * d_Ei + d_Texp)).n(30) params = A0, A, Ea_over_R, T0, dTdt = [13, 1e10, 56e3 / 8, 273, 2] B0 = 11 rate = MassAction(Arrhenius([A, Ea_over_R])) rxn = Reaction({'A': 1}, {'B': 3}, rate) rsys = ReactionSystem([rxn], 'A B') rt = RampedTemp([T0, dTdt], ('init_temp', 'ramp_rate')) odesys, extra = get_odesys(rsys, False, substitutions={'temperature': rt}) all_pk, unique, p_units = map(extra.get, 'param_keys unique p_units'.split()) conc = {'A': A0, 'B': B0} tout = [2, 5, 10] for ramp_rate in [2, 3, 4]: unique['ramp_rate'] = ramp_rate xout, yout, info = odesys.integrate(10, conc, unique, atol=1e-10, rtol=1e-12) params[-1] = ramp_rate Aref = np.array([float(refA(t, *params)) for t in xout]) # Aref = 1/(1/13 + 2*1e-6*t_unitless) yref = np.zeros((xout.size, 2)) yref[:, 0] = Aref yref[:, 1] = B0 + 3 * (A0 - Aref) assert allclose(yout, yref) unique['ramp_rate'] = 2 x, y, p = odesys.to_arrays(tout, conc, unique) fout = odesys.f_cb(x, y, p) def r(t): return A * np.exp(-Ea_over_R / (T0 + dTdt * t)) * A0 # refA(t, *params) ref = np.array([[-r(2), -r(5), -r(10)], [3 * r(2), 3 * r(5), 3 * r(10)]]).T assert np.allclose(fout, ref)
def test_get_odesys__rate_exprs_cb(): k = .2 a = Substance('A') b = Substance('B') r = Reaction({'A': 1}, {'B': 1}, param=k) rsys = ReactionSystem([r], [a, b]) assert sorted(rsys.substances.keys()) == ['A', 'B'] odesys, extra = get_odesys(rsys) c0 = {'A': 1.0, 'B': 3.0} t = np.linspace(0.0, 10.0) res = odesys.integrate(t, c0) yref = np.zeros((t.size, 2)) yref[:, 0] = np.exp(-k*t) yref[:, 1] = 4 - np.exp(-k*t) assert np.allclose(res.yout, yref) rate = extra['rate_exprs_cb'](res.xout, res.yout, res.params) assert np.allclose(rate[:, 0], k*yref[:, 0])
def test_get_ode__Radiolytic__units__multi(): 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") odesys = get_odesys(rsys, include_params=True, unit_registry=SI_base_registry)[0] conc = {"A": 3 * u.molar, "B": 5 * u.molar, "C": 11 * u.molar, "D": 13 * u.molar} doserates = [dr * u.gray / u.second for dr in [0.1, 0.2, 0.3, 0.4]] results = odesys.integrate( 37 * u.second, conc, {"doserate": doserates, "density": 0.998 * u.kg / u.decimetre ** 3}, ) assert len(results) == 4 for i, r in enumerate(results): dr = r.params[odesys.param_names.index("doserate")] assert dr.ndim == 0 or len(dr) == 1 assert dr == doserates[i]
def test_get_ode__Radiolytic__units__multi(): 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') odesys = get_odesys(rsys, include_params=True, unit_registry=SI_base_registry)[0] conc = {'A': 3*u.molar, 'B': 5*u.molar, 'C': 11*u.molar, 'D': 13*u.molar} doserates = [dr*u.gray/u.second for dr in [.1, .2, .3, .4]] results = odesys.integrate(37*u.second, conc, { 'doserate': doserates, 'density': 0.998*u.kg/u.decimetre**3 }) assert len(results) == 4 for i, r in enumerate(results): dr = r.params[odesys.param_names.index('doserate')] assert dr.ndim == 0 or len(dr) == 1 assert dr == doserates[i]
def test_get_odesys__time_dep_rate(): class RampedRate(Expr): argument_names = ("rate_constant", "ramping_rate") def __call__(self, variables, reaction, backend=math): rate_constant, ramping_rate = self.all_args(variables, backend=backend) return rate_constant * ramping_rate * variables["time"] rate = MassAction(RampedRate([7, 2])) rxn = Reaction({"A": 1}, {"B": 3}, rate) rsys = ReactionSystem([rxn], "A B") odesys = get_odesys(rsys)[0] conc = {"A": 3, "B": 11} x, y, p = odesys.to_arrays([5, 13, 17], conc, ()) fout = odesys.f_cb(x, y, p) r = 2 * 7 * 3 ref = np.array([[-r * 5, -r * 13, -r * 17], [r * 5 * 3, r * 13 * 3, r * 17 * 3]]).T assert np.allclose(fout, ref)
def test_get_odesys_1(): k = .2 a = Substance('A') b = Substance('B') r = Reaction({'A': 1}, {'B': 1}, param=k) rsys = ReactionSystem([r], [a, b]) assert sorted(rsys.substances.keys()) == ['A', 'B'] odesys = get_odesys(rsys, include_params=True)[0] c0 = { 'A': 1.0, 'B': 3.0, } t = np.linspace(0.0, 10.0) xout, yout, info = odesys.integrate(t, c0) yref = np.zeros((t.size, 2)) yref[:, 0] = np.exp(-k*t) yref[:, 1] = 4 - np.exp(-k*t) assert np.allclose(yout, yref)
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_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)