Пример #1
0
    def getStatmechData(self, molecule, thermoModel):
        """
        Use the previously-loaded frequency database to generate a set of
        characteristic group frequencies corresponding to the speficied
        `molecule`. The provided thermo data in `thermoModel` is used to fit
        some frequencies and all hindered rotors to heat capacity data.
        """
        conformer = Conformer()

        # Compute spin multiplicity
        # For closed-shell molecule the spin multiplicity is 1
        # For monoradicals the spin multiplicity is 2
        # For higher-order radicals the highest allowed spin multiplicity is assumed
        conformer.spinMultiplicity = molecule.getRadicalCount() + 1

        # No need to determine rotational and vibrational modes for single atoms
        if len(molecule.atoms) < 2:
            return (conformer, None, None)

        linear = molecule.isLinear()
        numRotors = molecule.countInternalRotors()
        numVibrations = 3 * len(molecule.atoms) - (5 if linear else
                                                   6) - numRotors

        # Get characteristic frequency groups and the associated frequencies
        groupCount = self.getFrequencyGroups(molecule)
        frequencies = []
        for entry, count in groupCount.iteritems():
            if count != 0 and entry.data is not None:
                frequencies.extend(entry.data.generateFrequencies(count))

        # Check that we have the right number of degrees of freedom specified
        if len(frequencies) > numVibrations:
            # We have too many vibrational modes
            difference = len(frequencies) - numVibrations
            # First try to remove hindered rotor modes until the proper number of modes remain
            if numRotors > difference:
                numRotors -= difference
                numVibrations = len(frequencies)
                logging.warning(
                    'For {0}, more characteristic frequencies were generated than vibrational modes allowed. Removed {1:d} internal rotors to compensate.'
                    .format(molecule, difference))
            # If that won't work, turn off functional groups until the problem is underspecified again
            else:
                groupsRemoved = 0
                freqsRemoved = 0
                freqCount = len(frequencies)
                while freqCount > numVibrations:
                    minDegeneracy, minEntry = min([(entry.data.symmetry, entry)
                                                   for entry in groupCount
                                                   if groupCount[entry] > 0])
                    if groupCount[minEntry] > 1:
                        groupCount[minEntry] -= 1
                    else:
                        del groupCount[minEntry]
                    groupsRemoved += 1
                    freqsRemoved += minDegeneracy
                    freqCount -= minDegeneracy
                # Log warning
                logging.warning(
                    'For {0}, more characteristic frequencies were generated than vibrational modes allowed. Removed {1:d} groups ({2:d} frequencies) to compensate.'
                    .format(molecule, groupsRemoved, freqsRemoved))
                # Regenerate characteristic frequencies
                frequencies = []
                for entry, count in groupCount.iteritems():
                    if count != 0:
                        frequencies.extend(
                            entry.data.generateFrequencies(count))

        # Subtract out contributions to heat capacity from the group frequencies
        Tlist = numpy.arange(300.0, 1501.0, 100.0, numpy.float64)
        Cv = numpy.array(
            [thermoModel.getHeatCapacity(T) / constants.R for T in Tlist],
            numpy.float64)
        ho = HarmonicOscillator(frequencies=(frequencies, "cm^-1"))
        for i in range(Tlist.shape[0]):
            Cv[i] -= ho.getHeatCapacity(Tlist[i]) / constants.R
        # Subtract out translational modes
        Cv -= 1.5
        # Subtract out external rotational modes
        Cv -= (1.5 if not linear else 1.0)
        # Subtract out PV term (Cp -> Cv)
        Cv -= 1.0

        # Fit remaining frequencies and hindered rotors to the heat capacity data
        from statmechfit import fitStatmechToHeatCapacity
        modes = fitStatmechToHeatCapacity(Tlist, Cv,
                                          numVibrations - len(frequencies),
                                          numRotors, molecule)
        for mode in modes:
            if isinstance(mode, HarmonicOscillator):
                uncertainties = [0 for f in frequencies
                                 ]  # probably shouldn't be zero
                frequencies.extend(mode.frequencies.value_si)
                uncertainties.extend(mode.frequencies.uncertainty)
                mode.frequencies.value_si = numpy.array(
                    frequencies, numpy.float)
                mode.frequencies.uncertainty = numpy.array(
                    uncertainties, numpy.float)
                break
        else:
            modes.insert(
                0, HarmonicOscillator(frequencies=(frequencies, "cm^-1")))

        conformer.modes = modes

        return (conformer, None, None)
Пример #2
0
    def getStatmechData(self, molecule, thermoModel):
        """
        Use the previously-loaded frequency database to generate a set of
        characteristic group frequencies corresponding to the speficied
        `molecule`. The provided thermo data in `thermoModel` is used to fit
        some frequencies and all hindered rotors to heat capacity data.
        """
        conformer = Conformer()
        
        # Compute spin multiplicity
        # For closed-shell molecule the spin multiplicity is 1
        # For monoradicals the spin multiplicity is 2
        # For higher-order radicals the highest allowed spin multiplicity is assumed
        conformer.spinMultiplicity = molecule.getRadicalCount() + 1
        
        # No need to determine rotational and vibrational modes for single atoms
        if len(molecule.atoms) < 2:
            return (conformer, None, None)

        linear = molecule.isLinear()
        numRotors = molecule.countInternalRotors()
        numVibrations = 3 * len(molecule.atoms) - (5 if linear else 6) - numRotors

        # Get characteristic frequency groups and the associated frequencies
        groupCount = self.getFrequencyGroups(molecule)
        frequencies = []
        for entry, count in groupCount.iteritems():
            if count != 0 and entry.data is not None: frequencies.extend(entry.data.generateFrequencies(count))

        # Check that we have the right number of degrees of freedom specified
        if len(frequencies) > numVibrations:
            # We have too many vibrational modes
            difference = len(frequencies) - numVibrations
            # First try to remove hindered rotor modes until the proper number of modes remain
            if numRotors > difference:
                numRotors -= difference
                numVibrations = len(frequencies)
                logging.warning('For {0}, more characteristic frequencies were generated than vibrational modes allowed. Removed {1:d} internal rotors to compensate.'.format(molecule, difference))
            # If that won't work, turn off functional groups until the problem is underspecified again
            else:
                groupsRemoved = 0
                freqsRemoved = 0
                freqCount = len(frequencies)
                while freqCount > numVibrations:
                    minDegeneracy, minEntry = min([(entry.data.symmetry, entry) for entry in groupCount if groupCount[entry] > 0])
                    if groupCount[minEntry] > 1:
                        groupCount[minEntry] -= 1
                    else:
                        del groupCount[minEntry]
                    groupsRemoved += 1
                    freqsRemoved += minDegeneracy
                    freqCount -= minDegeneracy
                # Log warning
                logging.warning('For {0}, more characteristic frequencies were generated than vibrational modes allowed. Removed {1:d} groups ({2:d} frequencies) to compensate.'.format(molecule, groupsRemoved, freqsRemoved))
                # Regenerate characteristic frequencies
                frequencies = []
                for entry, count in groupCount.iteritems():
                    if count != 0: frequencies.extend(entry.data.generateFrequencies(count))

        # Subtract out contributions to heat capacity from the group frequencies
        Tlist = numpy.arange(300.0, 1501.0, 100.0, numpy.float64)
        Cv = numpy.array([thermoModel.getHeatCapacity(T) / constants.R for T in Tlist], numpy.float64)
        ho = HarmonicOscillator(frequencies=(frequencies,"cm^-1"))
        for i in range(Tlist.shape[0]):
            Cv[i] -= ho.getHeatCapacity(Tlist[i]) / constants.R
        # Subtract out translational modes
        Cv -= 1.5
        # Subtract out external rotational modes
        Cv -= (1.5 if not linear else 1.0)
        # Subtract out PV term (Cp -> Cv)
        Cv -= 1.0
        
        # Fit remaining frequencies and hindered rotors to the heat capacity data
        from statmechfit import fitStatmechToHeatCapacity
        modes = fitStatmechToHeatCapacity(Tlist, Cv, numVibrations - len(frequencies), numRotors, molecule)
        for mode in modes:
            if isinstance(mode, HarmonicOscillator):
                uncertainties = [0 for f in frequencies] # probably shouldn't be zero
                frequencies.extend(mode.frequencies.value_si)
                uncertainties.extend(mode.frequencies.uncertainty)
                mode.frequencies.value_si = numpy.array(frequencies, numpy.float)
                mode.frequencies.uncertainty = numpy.array(uncertainties, numpy.float)
                break
        else:
            modes.insert(0, HarmonicOscillator(frequencies=(frequencies,"cm^-1")))

        conformer.modes = modes

        return (conformer, None, None)