Skip to content

Commit 833b973

Browse files
davidfarinajrrwest
authored andcommitted
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 dcbd55a commit 833b973

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
@@ -603,91 +603,86 @@ cdef class ArrheniusBM(KineticsModel):
603603
assert w0 is not None or recipe is not None, 'either w0 or recipe must be specified'
604604

605605
if Ts is None:
606-
Ts = [300.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 1100.0, 1200.0, 1500.0, 2000.0]
606+
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])
607607
if w0 is None:
608608
#estimate w0
609609
w0s = get_w0s(recipe, rxns)
610610
w0 = sum(w0s) / len(w0s)
611-
612-
if len(rxns) == 1:
613-
T = 1000.0
614-
rxn = rxns[0]
615-
dHrxn = rxn.get_enthalpy_of_reaction(T)
616-
A = rxn.kinetics.A.value_si
617-
n = rxn.kinetics.n.value_si
618-
Ea = rxn.kinetics.Ea.value_si
611+
self.w0 = (w0 * 0.001, 'kJ/mol')
612+
613+
def kfcn(xs, lnA, n, E0):
614+
T = xs[:,0]
615+
dHrxn = xs[:,1]
616+
self.E0 = (E0, 'J/mol')
617+
Ea = np.array([self.get_activation_energy(dHrxn[i]) for i in range(len(dHrxn))])
618+
return lnA + np.log(T ** n * np.exp(-Ea / (constants.R * T)))
619619

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

676-
lnA, n, E0 = params[0].tolist()
677-
A = np.exp(lnA)
673+
A = np.exp(lnA)
678674

679-
self.Tmin = (np.min(Ts), "K")
680-
self.Tmax = (np.max(Ts), "K")
681-
self.comment = 'Fitted to {0} reactions at temperatures: {1}'.format(len(rxns), Ts)
675+
self.Tmin = (np.min(Ts), "K")
676+
self.Tmax = (np.max(Ts), "K")
677+
self.comment = 'Fitted to {0} reactions at temperatures: {1}'.format(len(rxns), Ts)
682678

683679
# fill in parameters
684680
A_units = ['', 's^-1', 'm^3/(mol*s)', 'm^6/(mol^2*s)']
685681
order = len(rxns[0].reactants)
686682
self.A = (A, A_units[order])
687683

688684
self.n = n
689-
self.w0 = (w0, 'J/mol')
690-
self.E0 = (E0, 'J/mol')
685+
self.E0 = (E0 * 0.001, 'kJ/mol')
691686

692687
return self
693688

0 commit comments

Comments
 (0)