예제 #1
0
def fitStatmechPseudo(Tlist, Cvlist, Nvib, Nrot, molecule=None):
    """
    Fit `Nvib` harmonic oscillator and `Nrot` hindered internal rotor modes to
    the provided dimensionless heat capacities `Cvlist` at temperatures `Tlist`
    in K. This method assumes that there are relatively few heat capacity points
    provided, so the vibrations must be combined into one real vibration and
    two "pseudo-vibrations" and the hindered rotors must be combined into a
    single "pseudo-rotor".
    """


    # Construct the lower and upper bounds for each variable
    bounds = []
    # x[0] corresponds to the first harmonic oscillator (real) frequency
    bounds.append((hoFreqLowerBound, hoFreqUpperBound))
    # x[1] corresponds to the degeneracy of the second harmonic oscillator
    bounds.append((1.0, float(Nvib - 2)))
    # x[2] corresponds to the second harmonic oscillator pseudo-frequency
    bounds.append((hoFreqLowerBound, hoFreqUpperBound))
    # x[3] corresponds to the third harmonic oscillator pseudo-frequency
    bounds.append((hoFreqLowerBound, hoFreqUpperBound))
    # x[4] corresponds to the hindered rotor pseudo-frequency
    bounds.append((hrFreqLowerBound, hrFreqUpperBound))
    # x[5] corresponds to the hindered rotor pseudo-barrier
    bounds.append((hrBarrLowerBound, hrBarrUpperBound))

    # Construct the initial guess
    x0 = numpy.zeros(6, numpy.float64)      # Initial guess
    x0[0] = 300.0
    x0[1] = float(math.floor((Nvib - 1) / 2.0))
    x0[2] = 800.0
    x0[3] = 1600.0
    x0[4] = 100.0
    x0[5] = 300.0

    # Execute the optimization
    fit = PseudoFit(Tlist, Cvlist, Nvib, Nrot)
    fit.initialize(Neq=len(Tlist), Nvars=len(x0), Ncons=0, bounds=bounds, maxIter=maxIter)
    x, igo = fit.solve(x0)

    # Check that the results of the optimization are valid
    if not numpy.isfinite(x).all():
        raise StatmechFitError('Returned solution vector is nonsensical: x = {0}.'.format(x))
    if igo == 8:
        logging.warning('Maximum number of iterations reached when fitting spectral data for {0}.'.format(molecule.toSMILES()))

    # Postprocess optimization results
    Nvib2 = int(round(x[1]))
    Nvib3 = Nvib - Nvib2 - 1
    if Nvib2 < 0 or Nvib2 > Nvib-1 or Nvib3 < 0 or Nvib3 > Nvib-1:
        raise StatmechFitError('Invalid degeneracies {0} and {1} fitted for pseudo-frequencies.'.format(Nvib2, Nvib3))

    vib = [x[0]]
    for i in range(Nvib2): vib.append(x[2])
    for i in range(Nvib3): vib.append(x[3])
    hind = []
    for i in range(Nrot):
        hind.append((x[4], x[5]))

    return vib, hind
예제 #2
0
def fitStatmechToHeatCapacity(Tlist, Cvlist, Nvib, Nrot, molecule=None):
    """
    For a given set of dimensionless heat capacity data `Cvlist` corresponding
    to temperature list `Tlist` in K, fit `Nvib` harmonic oscillator and `Nrot`
    hindered internal rotor modes. External and other previously-known modes
    should have already been removed from `Cvlist` prior to calling this
    function. You must provide at least 7 values for `Cvlist`.

    This function returns a list containing the fitted vibrational frequencies
    in a :class:`HarmonicOscillator` object and the fitted 1D hindered rotors
    in :class:`HinderedRotor` objects.
    """

    # You must specify at least 7 heat capacity points to use in the fitting;
    # you can specify as many as you like above that minimum
    if len(Tlist) < 7:
        raise StatmechFitError('You must specify at least 7 heat capacity points to fitStatmechToHeatCapacity().')
    if len(Tlist) != len(Cvlist):
        raise StatmechFitError('The number of heat capacity points ({0:d}) does not match the number of temperatures provided ({1:d}).'.format(len(Cvlist), len(Tlist)))

    # The number of optimization variables available is constrained to be less
    # than the number of heat capacity points
    # This is also capped to a (somewhat arbitrarily chosen) maximum of 16
    maxVariables = len(Tlist) - 1
    if maxVariables > 16: maxVariables = 16

    # The type of variables fitted depends on the values of Nvib and Nrot and
    # the number of heat capacity points provided
    # For low values of Nvib and Nrot, we can fit the individual
    # parameters directly
    # For high values of Nvib and/or Nrot we are limited by the number of
    # temperatures we are fitting at, and so we can only fit
    # pseudo-oscillators and/or pseudo-rotors
    vib = []; hind = []
    if Nvib <= 0 and Nrot <= 0:
        pass
    elif Nvib + 2 * Nrot <= maxVariables:
        vib, hind = fitStatmechDirect(Tlist, Cvlist, Nvib, Nrot, molecule)
    elif Nvib + 2 <= maxVariables:
        vib, hind = fitStatmechPseudoRotors(Tlist, Cvlist, Nvib, Nrot, molecule)
    else:
        vib, hind = fitStatmechPseudo(Tlist, Cvlist, Nvib, Nrot, molecule)

    modes = []
    if Nvib > 0:
        vib.sort()
        ho = HarmonicOscillator(frequencies=(vib[:],"cm^-1"))
        modes.append(ho)
    for i in range(Nrot):
        freq = hind[i][0]
        barr = hind[i][1]
        inertia = (barr*constants.c*100.0*constants.h) / (8 * math.pi * math.pi * (freq*constants.c*100.0)**2)
        barrier = barr*constants.c*100.0*constants.h*constants.Na
        hr = HinderedRotor(inertia=(inertia*constants.Na*1e23,"amu*angstrom^2"), barrier=(barrier/1000.,"kJ/mol"), symmetry=1, semiclassical=False, quantum=False)
        modes.append(hr)

    # Return the fitted modes
    return modes
예제 #3
0
def fit_statmech_direct(Tlist, Cvlist, n_vib, n_rot, molecule=None):
    """
    Fit `n_vib` harmonic oscillator and `n_rot` hindered internal rotor modes to
    the provided dimensionless heat capacities `Cvlist` at temperatures `Tlist`
    in K. This method assumes that there are enough heat capacity points
    provided that the vibrational frequencies and hindered rotation frequency-
    barrier pairs can be fit directly.
    """

    # Construct the lower and upper bounds for each variable
    bounds = []
    # Bounds for harmonic oscillator frequencies
    for i in range(n_vib):
        bounds.append((ho_freq_lower_bound, ho_freq_upper_bound))
    # Bounds for hindered rotor frequencies and barrier heights
    for i in range(n_rot):
        bounds.append((hr_freq_lower_bound, hr_freq_upper_bound))
        bounds.append((hr_barr_lower_bound, hr_barr_upper_bound))

    # Construct the initial guess
    # Initial guesses within each mode type must be distinct or else the
    # optimization will fail
    x0 = np.zeros(n_vib + 2 * n_rot, np.float64)
    # Initial guess for harmonic oscillator frequencies
    if n_vib > 0:
        x0[0] = 200.0
        x0[1:n_vib] = np.linspace(800.0, 1600.0, n_vib - 1)
    # Initial guess for hindered rotor frequencies and barrier heights
    if n_rot > 0:
        x0[n_vib] = 100.0
        x0[n_vib + 1] = 100.0
        for i in range(1, n_rot):
            x0[n_vib + 2 * i] = x0[n_vib + 2 * i - 2] + 20.0
            x0[n_vib + 2 * i + 1] = x0[n_vib + 2 * i - 1] + 100.0

    # Execute the optimization
    fit = DirectFit(Tlist, Cvlist, n_vib, n_rot)
    fit.initialize(Neq=len(Tlist), Nvars=len(x0), Ncons=0, bounds=bounds, maxIter=max_iter)
    x, igo = fit.solve(x0)

    # Check that the results of the optimization are valid
    if not np.isfinite(x).all():
        raise StatmechFitError('Returned solution vector is nonsensical: x = {0}.'.format(x))
    if igo == 8:
        logging.warning('Maximum number of iterations reached when fitting spectral data for '
                        '{0}.'.format(molecule.to_smiles()))
    elif igo > 8:
        logging.warning('A solver error occured when fitting spectral data for {0}.'.format(molecule.to_smiles()))
    logging.debug('Fitting remaining heat capacity to {0} vibrations and {1} rotations'.format(n_vib, n_rot))
    logging.debug('The residuals for heat capacity values is {}'.format(fit.evaluate(x)[0]))

    # Postprocess optimization results
    vib = list(x[0:n_vib])
    hind = []
    for i in range(n_rot):
        hind.append((x[n_vib + 2 * i], x[n_vib + 2 * i + 1]))

    return vib, hind
예제 #4
0
def fitStatmechDirect(Tlist, Cvlist, Nvib, Nrot, molecule=None):
    """
    Fit `Nvib` harmonic oscillator and `Nrot` hindered internal rotor modes to
    the provided dimensionless heat capacities `Cvlist` at temperatures `Tlist`
    in K. This method assumes that there are enough heat capacity points
    provided that the vibrational frequencies and hindered rotation frequency-
    barrier pairs can be fit directly.
    """

    # Construct the lower and upper bounds for each variable
    bounds = []
    # Bounds for harmonic oscillator frequencies
    for i in range(Nvib):
        bounds.append((hoFreqLowerBound, hoFreqUpperBound))
    # Bounds for hindered rotor frequencies and barrier heights
    for i in range(Nrot):
        bounds.append((hrFreqLowerBound, hrFreqUpperBound))
        bounds.append((hrBarrLowerBound, hrBarrUpperBound))

    # Construct the initial guess
    # Initial guesses within each mode type must be distinct or else the
    # optimization will fail
    x0 = numpy.zeros(Nvib + 2*Nrot, numpy.float64)
    # Initial guess for harmonic oscillator frequencies
    if Nvib > 0:
        x0[0] = 200.0
        x0[1:Nvib] = numpy.linspace(800.0, 1600.0, Nvib-1)
    # Initial guess for hindered rotor frequencies and barrier heights
    if Nrot > 0:
        x0[Nvib] = 100.0
        x0[Nvib+1] = 100.0
        for i in range(1, Nrot):
            x0[Nvib+2*i] = x0[Nvib+2*i-2] + 20.0
            x0[Nvib+2*i+1] = x0[Nvib+2*i-1] + 100.0

    # Execute the optimization
    fit = DirectFit(Tlist, Cvlist, Nvib, Nrot)
    fit.initialize(Neq=len(Tlist), Nvars=len(x0), Ncons=0, bounds=bounds, maxIter=maxIter)
    x, igo = fit.solve(x0)

    # Check that the results of the optimization are valid
    if not numpy.isfinite(x).all():
        raise StatmechFitError('Returned solution vector is nonsensical: x = {0}.'.format(x))
    if igo == 8:
        logging.warning('Maximum number of iterations reached when fitting spectral data for {0}.'.format(molecule.toSMILES()))

    # Postprocess optimization results
    vib = list(x[0:Nvib])
    hind = []
    for i in range(Nrot):
        hind.append((x[Nvib+2*i], x[Nvib+2*i+1]))

    return vib, hind
예제 #5
0
def fit_statmech_pseudo(Tlist, Cvlist, n_vib, n_rot, molecule=None):
    """
    Fit `n_vib` harmonic oscillator and `n_rot` hindered internal rotor modes to
    the provided dimensionless heat capacities `Cvlist` at temperatures `Tlist`
    in K. This method assumes that there are relatively few heat capacity points
    provided, so the vibrations must be combined into one real vibration and
    two "pseudo-vibrations" and the hindered rotors must be combined into a
    single "pseudo-rotor".
    """

    # Construct the lower and upper bounds for each variable
    bounds = []
    # x[0] corresponds to the first harmonic oscillator (real) frequency
    bounds.append((ho_freq_lower_bound, ho_freq_upper_bound))
    # x[1] corresponds to the degeneracy of the second harmonic oscillator
    bounds.append((1.0, float(n_vib - 2)))
    # x[2] corresponds to the second harmonic oscillator pseudo-frequency
    bounds.append((ho_freq_lower_bound, ho_freq_upper_bound))
    # x[3] corresponds to the third harmonic oscillator pseudo-frequency
    bounds.append((ho_freq_lower_bound, ho_freq_upper_bound))
    # x[4] corresponds to the hindered rotor pseudo-frequency
    bounds.append((hr_freq_lower_bound, hr_freq_upper_bound))
    # x[5] corresponds to the hindered rotor pseudo-barrier
    bounds.append((hr_barr_lower_bound, hr_barr_upper_bound))

    # Construct the initial guess
    x0 = np.zeros(6, np.float64)  # Initial guess
    x0[0] = 300.0
    x0[1] = float(math.floor((n_vib - 1) / 2.0))
    x0[2] = 800.0
    x0[3] = 1600.0
    x0[4] = 100.0
    x0[5] = 300.0

    # Execute the optimization
    fit = PseudoFit(Tlist, Cvlist, n_vib, n_rot)
    fit.initialize(Neq=len(Tlist),
                   Nvars=len(x0),
                   Ncons=0,
                   bounds=bounds,
                   maxIter=max_iter)
    x, igo = fit.solve(x0)

    # Check that the results of the optimization are valid
    if not np.isfinite(x).all():
        raise StatmechFitError(
            'Returned solution vector is nonsensical: x = {0}.'.format(x))
    if igo == 8:
        logging.warning(
            'Maximum number of iterations reached when fitting spectral data for '
            '{0}.'.format(molecule.to_smiles()))
    if igo > 8:
        logging.warning(
            'A solver error occured when fitting spectral data for {0}.'.
            format(molecule.to_smiles()))
    logging.debug(
        'Fitting remaining heat capacity to {0} vibrations and {1} rotations'.
        format(n_vib, n_rot))
    logging.debug('The residuals for heat capacity values is {}'.format(
        fit.evaluate(x)[0]))

    # Postprocess optimization results
    n_vib2 = int(round(x[1]))
    n_vib_3 = n_vib - n_vib2 - 1
    if n_vib2 < 0 or n_vib2 > n_vib - 1 or n_vib_3 < 0 or n_vib_3 > n_vib - 1:
        raise StatmechFitError('Invalid degeneracies {0} and {1} fitted for '
                               'pseudo-frequencies.'.format(n_vib2, n_vib_3))

    vib = [x[0]]
    for i in range(n_vib2):
        vib.append(x[2])
    for i in range(n_vib_3):
        vib.append(x[3])
    hind = []
    for i in range(n_rot):
        hind.append((x[4], x[5]))

    return vib, hind