Skip to content

Commit fb42d78

Browse files
committed
revised arrbm fitting to reactions procedure
Use the `get_activation_energy` method in the objective function. For intial guess of params, use average of A and n, and use BEP for guess of Ea.
1 parent 396ce81 commit fb42d78

1 file changed

Lines changed: 66 additions & 71 deletions

File tree

rmgpy/kinetics/arrhenius.pyx

Lines changed: 66 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -597,91 +597,86 @@ cdef class ArrheniusBM(KineticsModel):
597597
assert w0 is not None or recipe is not None, 'either w0 or recipe must be specified'
598598

599599
if Ts is None:
600-
Ts = [300.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 1100.0, 1200.0, 1500.0, 2000.0]
600+
Ts = np.array([300.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 1100.0, 1200.0, 1500.0, 2000.0])
601601
if w0 is None:
602602
#estimate w0
603603
w0s = get_w0s(recipe, rxns)
604604
w0 = sum(w0s) / len(w0s)
605-
606-
if len(rxns) == 1:
607-
T = 1000.0
608-
rxn = rxns[0]
609-
dHrxn = rxn.get_enthalpy_of_reaction(T)
610-
A = rxn.kinetics.A.value_si
611-
n = rxn.kinetics.n.value_si
612-
Ea = rxn.kinetics.Ea.value_si
605+
self.w0 = (w0 * 0.001, 'kJ/mol')
606+
607+
def kfcn(xs, lnA, n, E0):
608+
T = xs[:,0]
609+
dHrxn = xs[:,1]
610+
self.E0 = (E0, 'J/mol')
611+
Ea = np.array([self.get_activation_energy(dHrxn[i]) for i in range(len(dHrxn))])
612+
return lnA + np.log(T ** n * np.exp(-Ea / (constants.R * T)))
613613

614-
def kfcn(E0):
615-
Vp = 2 * w0 * (2 * w0 + 2 * E0) / (2 * w0 - 2 * E0)
616-
out = Ea - (w0 + dHrxn / 2.0) * (Vp - 2 * w0 + dHrxn) * (Vp - 2 * w0 + dHrxn) / (Vp * Vp - (2 * w0) * (2 * w0) + dHrxn * dHrxn)
617-
return out
618-
619-
if abs(dHrxn) > 4 * w0 / 10.0:
620-
E0 = w0 / 10.0
621-
else:
622-
E0 = fsolve(kfcn, w0 / 10.0)[0]
623-
624-
self.Tmin = rxn.kinetics.Tmin
625-
self.Tmax = rxn.kinetics.Tmax
626-
self.comment = 'Fitted to {0} reaction at temperature: {1} K'.format(len(rxns), T)
627-
else:
628-
# define optimization function
629-
def kfcn(xs, lnA, n, E0):
630-
T = xs[:,0]
631-
dHrxn = xs[:,1]
632-
Vp = 2 * w0 * (2 * w0 + 2 * E0) / (2 * w0 - 2 * E0)
633-
Ea = (w0 + dHrxn / 2.0) * (Vp - 2 * w0 + dHrxn) * (Vp - 2 * w0 + dHrxn) / (Vp * Vp - (2 * w0) * (2 * w0) + dHrxn * dHrxn)
634-
Ea = np.where(dHrxn< -4.0*E0, 0.0, Ea)
635-
Ea = np.where(dHrxn > 4.0*E0, dHrxn, Ea)
636-
return lnA + np.log(T ** n * np.exp(-Ea / (8.314 * T)))
637-
638-
# get (T,dHrxn(T)) -> (Ln(k) mappings
639-
xdata = []
640-
ydata = []
641-
sigmas = []
642-
for rxn in rxns:
643-
# approximately correct the overall uncertainties to std deviations
644-
s = rank_accuracy_map[rxn.rank].value_si/2.0
645-
for T in Ts:
646-
xdata.append([T, rxn.get_enthalpy_of_reaction(T)])
647-
ydata.append(np.log(rxn.get_rate_coefficient(T)))
648-
649-
sigmas.append(s / (8.314 * T))
650-
651-
xdata = np.array(xdata)
652-
ydata = np.array(ydata)
653-
654-
# fit parameters
655-
boo = True
656-
xtol = 1e-8
657-
ftol = 1e-8
658-
while boo:
659-
boo = False
660-
try:
661-
params = curve_fit(kfcn, xdata, ydata, sigma=sigmas, p0=[1.0, 1.0, w0 / 10.0], xtol=xtol, ftol=ftol)
662-
except RuntimeError:
663-
if xtol < 1.0:
664-
boo = True
665-
xtol *= 10.0
666-
ftol *= 10.0
667-
else:
668-
raise ValueError("Could not fit BM arrhenius to reactions with xtol<1.0")
614+
# get (T,dHrxn(T)) -> (Ln(k) mappings
615+
xdata = []
616+
ydata = []
617+
sigmas = []
618+
E0 = 0.0
619+
lnA = 0.0
620+
n = 0.0
621+
for rxn in rxns:
622+
# approximately correct the overall uncertainties to std deviations
623+
s = rank_accuracy_map[rxn.rank].value_si/2.0
624+
# Use BEP with alpha = 0.25 for inital guess of E0
625+
E0 += rxn.kinetics._Ea.value_si - 0.25 * rxn.get_enthalpy_of_reaction(298)
626+
lnA += np.log(rxn.kinetics.A.value_si)
627+
n += rxn.kinetics.n.value_si
628+
for T in Ts:
629+
xdata.append([T, rxn.get_enthalpy_of_reaction(298)])
630+
ydata.append(np.log(rxn.get_rate_coefficient(T)))
631+
sigmas.append(s / (constants.R * T))
632+
# Use the average of the E0s as intial guess
633+
E0 /= len(rxns)
634+
lnA /= len(rxns)
635+
n /= len(rxns)
636+
E0 = min(E0, w0)
637+
if E0 < 0:
638+
E0 = w0 / 100.0
639+
640+
xdata = np.array(xdata)
641+
ydata = np.array(ydata)
642+
643+
# fit parameters
644+
boo = True
645+
xtol = 1e-8
646+
ftol = 1e-8
647+
attempts = 0
648+
while boo:
649+
boo = False
650+
try:
651+
params = curve_fit(kfcn, xdata, ydata, sigma=sigmas, p0=[lnA, n, E0], xtol=xtol, ftol=ftol)
652+
lnA, n, E0 = params[0].tolist()
653+
if abs(E0/self.w0.value_si) > 1 and attempts < 5:
654+
boo = True
655+
if attempts > 0:
656+
self.w0.value_si *= 1.25
657+
attempts += 1
658+
E0 = self.w0.value_si / 10.0
659+
except RuntimeError:
660+
if xtol < 1.0:
661+
boo = True
662+
xtol *= 10.0
663+
ftol *= 10.0
664+
else:
665+
raise ValueError("Could not fit BM arrhenius to reactions with xtol<1.0")
669666

670-
lnA, n, E0 = params[0].tolist()
671-
A = np.exp(lnA)
667+
A = np.exp(lnA)
672668

673-
self.Tmin = (np.min(Ts), "K")
674-
self.Tmax = (np.max(Ts), "K")
675-
self.comment = 'Fitted to {0} reactions at temperatures: {1}'.format(len(rxns), Ts)
669+
self.Tmin = (np.min(Ts), "K")
670+
self.Tmax = (np.max(Ts), "K")
671+
self.comment = 'Fitted to {0} reactions at temperatures: {1}'.format(len(rxns), Ts)
676672

677673
# fill in parameters
678674
A_units = ['', 's^-1', 'm^3/(mol*s)', 'm^6/(mol^2*s)']
679675
order = len(rxns[0].reactants)
680676
self.A = (A, A_units[order])
681677

682678
self.n = n
683-
self.w0 = (w0, 'J/mol')
684-
self.E0 = (E0, 'J/mol')
679+
self.E0 = (E0 * 0.001, 'kJ/mol')
685680

686681
return self
687682

0 commit comments

Comments
 (0)