@@ -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