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