def dump(self, f): """Write all the information about the rotor to a file This method is part of the PartFun API and should never be called directly. It will only work properly once the init_part_fun method is called. Arguments: | ``f`` -- A file-like object. """ Info.dump(self, f) # parameters print >> f, " Dihedral indexes: %s" % " ".join( str(i) for i in self.rot_scan.dihedral) print >> f, " Top indexes: %s" % " ".join( str(i) for i in self.rot_scan.top_indexes) print >> f, " Rotational symmetry: %i" % self.rotsym print >> f, " Even potential: %s" % self.even if self.rot_scan.potential is None: print >> f, " This is a free rotor" else: angles, energies = self.rot_scan.potential print >> f, " This is a hindered rotor" print >> f, " Maximum number of cosines in the fit: %i" % self.dofmax angles, energies = self.potential fit_energies = self.hb.eval_fn(angles, self.v_coeffs) rmsd = ((fit_energies - energies)**2).mean()**0.5 rms = (energies**2).mean()**0.5 rrmsd = rmsd / rms print >> f, " RMSD of the fit [kJ/mol]: %.2f" % (rmsd / kjmol) print >> f, " Relative RMSD of the fit the fit [%%]: %.1f" % ( rrmsd * 100) print >> f, " Pearson R^2 of the fit [%%]: %.3f" % ( (1 - rrmsd**2) * 100) print >> f, " Potential: Angle [deg] Energy [kJ/mol]" for i in xrange(len(angles)): print >> f, " % 7.2f %6.1f" % ( angles[i] / deg, energies[i] / kjmol) print >> f, " Number of QM energy levels: %i" % self.num_levels # derived quantities print >> f, " Center [A]: % 8.2f % 8.2f % 8.2f" % tuple( self.center / angstrom) print >> f, " Axis [1]: % 8.2f % 8.2f % 8.2f" % tuple(self.axis) print >> f, " Moment [amu*bohr**2]: %f" % (self.moment / amu) print >> f, " Reduced moment [amu*bohr**2]: %f" % ( self.reduced_moment / amu) print >> f, " Cancel wavenumber [1/cm]: %.1f" % ( self.cancel_freq / (lightspeed / centimeter)) print >> f, " The cancelation wavenumber is %s." % self.cancel_method self.dump_values(f, "Energy levels [kJ/mol]", self.energy_levels / kjmol, "% 8.2f", 8) if self.hb is not None: print >> f, " Number of basis functions: %i" % (self.hb.size) print >> f, " Zero-point contribution [kJ/mol]: %.7f" % ( self.free_energy(0.0) / kjmol)
def dump(self, f): """Write all the information about the rotor to a file This method is part of the PartFun API and should never be called directly. It will only work properly once the init_part_fun method is called. Arguments: | ``f`` -- A file-like object. """ Info.dump(self, f) # parameters print >> f, " Dihedral indexes: %s" % " ".join(str(i) for i in self.rot_scan.dihedral) print >> f, " Top indexes: %s" % " ".join(str(i) for i in self.rot_scan.top_indexes) print >> f, " Rotational symmetry: %i" % self.rotsym print >> f, " Even potential: %s" % self.even if self.rot_scan.potential is None: print >> f, " This is a free rotor" else: angles, energies = self.rot_scan.potential print >> f, " This is a hindered rotor" print >> f, " Maximum number of cosines in the fit: %i" % self.dofmax angles, energies = self.potential fit_energies = self.hb.eval_fn(angles, self.v_coeffs) rmsd = ((fit_energies - energies)**2).mean()**0.5 rms = (energies**2).mean()**0.5 rrmsd = rmsd/rms print >> f, " RMSD of the fit [kJ/mol]: %.2f" % (rmsd/kjmol) print >> f, " Relative RMSD of the fit the fit [%%]: %.1f" % (rrmsd*100) print >> f, " Pearson R^2 of the fit [%%]: %.3f" % ((1-rrmsd**2)*100) print >> f, " Potential: Angle [deg] Energy [kJ/mol]" for i in xrange(len(angles)): print >> f, " % 7.2f %6.1f" % (angles[i]/deg, energies[i]/kjmol) print >> f, " Number of QM energy levels: %i" % self.num_levels # derived quantities print >> f, " Center [A]: % 8.2f % 8.2f % 8.2f" % tuple(self.center/angstrom) print >> f, " Axis [1]: % 8.2f % 8.2f % 8.2f" % tuple(self.axis) print >> f, " Moment [amu*bohr**2]: %f" % (self.moment/amu) print >> f, " Reduced moment [amu*bohr**2]: %f" % (self.reduced_moment/amu) print >> f, " Cancel wavenumber [1/cm]: %.1f" % (self.cancel_freq/(lightspeed/centimeter)) print >> f, " The cancelation wavenumber is %s." % self.cancel_method self.dump_values(f, "Energy levels [kJ/mol]", self.energy_levels/kjmol, "% 8.2f", 8) if self.hb is not None: print >> f, " Number of basis functions: %i" % (self.hb.size) print >> f, " Zero-point contribution [kJ/mol]: %.7f" % (self.free_energy(0.0)/kjmol)
def __init__(self, rot_scan, molecule=None, cancel_freq='mbh', suffix=None, rotsym=1, even=False, num_levels=50, dofmax=5, v_threshold=0.01, large_fixed=False): """ Arguments: | ``rot_scan`` -- A rotational scan object. (free or hindered rotor information) Optional arguments: | ``molecule`` -- Molecule to which the rotor applies, is used to compute the cancelation frequency. Not required when the cancel_freq argument is present. | ``cancel_freq`` -- The frequency to cancel in the vibrational partition function. This can also be 'mbh' or 'scan' to indicate that the cancel frequency should be computed using the MBH method or based on the second order derivative of the rotational potential. Note that the latter option is only possible in the case of the hindered rotor formalism. [default='mbh'] | ``suffix`` -- A name suffix used to distinguish between different rotors. | ``rotsym`` -- The rotational symmetry of the rotor. [default=1] | ``even`` -- True of the rotor is not chiral, i.e. when it has an even potential | ``num_levels`` -- The number of energy levels considered in the QM treatment of the rotor [default=50] | ``dofmax`` -- The maximum number of cosines used to represent the torsional potential. if the potential is not even, the same number of sines is also used. [default=5] | ``v_threshold`` -- Tolerance on the relative error between the Fourier expansion and the data points of the scan. [default=0.01]. Absolute errors smaller than 1 kJ/mol are always ignored. | ``large_fixed`` -- When True, assume that the large part of the system is fixed in space while the small part rotates. (this means that the absolute moment of the rotor is used instead of the relative moment) In case the Fourier expansion of the potential represents a poor fit (determined by v_threshold), a ValueError is raised. It means that you have to check your torsional scan datapoints for errors. """ self.rot_scan = rot_scan self.cancel_freq = cancel_freq self.cancel_method = 'given by the user' # the cancelation frequency if self.cancel_freq == 'mbh': self.cancel_freq = compute_cancel_frequency_mbh( molecule, self.rot_scan.dihedral, self.rot_scan.top_indexes) self.cancel_method = 'computed with mbh' elif self.cancel_freq == 'scan': if self.rot_scan.potential is None: raise RotorError( "The option cancel_freq='scan' can only be used with hindered rotors." ) self.cancel_method = 'derived from the torsional potential' # the actual computation is performed later else: raise ValueError("Could not interpret cancel_freq=%s" % cancel_freq) # if suffix is None: self.suffix = "_".join(str(i) for i in rot_scan.top_indexes) else: self.suffix = suffix self.rotsym = rotsym self.even = even self.num_levels = num_levels self.dofmax = dofmax self.v_threshold = v_threshold self.large_fixed = large_fixed if rot_scan.potential is None: Info.__init__(self, "free_rotor_%s" % self.suffix) else: Info.__init__(self, "hindered_rotor_%s" % self.suffix) StatFysTerms.__init__(self, 2) # two terms
def __init__(self, rot_scan, molecule=None, cancel_freq='mbh', suffix=None, rotsym=1, even=False, num_levels=50, dofmax=5, v_threshold=0.01, large_fixed=False): """ Arguments: | ``rot_scan`` -- A rotational scan object. (free or hindered rotor information) Optional arguments: | ``molecule`` -- Molecule to which the rotor applies, is used to compute the cancelation frequency. Not required when the cancel_freq argument is present. | ``cancel_freq`` -- The frequency to cancel in the vibrational partition function. This can also be 'mbh' or 'scan' to indicate that the cancel frequency should be computed using the MBH method or based on the second order derivative of the rotational potential. Note that the latter option is only possible in the case of the hindered rotor formalism. [default='mbh'] | ``suffix`` -- A name suffix used to distinguish between different rotors. | ``rotsym`` -- The rotational symmetry of the rotor. [default=1] | ``even`` -- True of the rotor is not chiral, i.e. when it has an even potential | ``num_levels`` -- The number of energy levels considered in the QM treatment of the rotor [default=50] | ``dofmax`` -- The maximum number of cosines used to represent the torsional potential. if the potential is not even, the same number of sines is also used. [default=5] | ``v_threshold`` -- Tolerance on the relative error between the Fourier expansion and the data points of the scan. [default=0.01]. Absolute errors smaller than 1 kJ/mol are always ignored. | ``large_fixed`` -- When True, assume that the large part of the system is fixed in space while the small part rotates. (this means that the absolute moment of the rotor is used instead of the relative moment) In case the Fourier expansion of the potential represents a poor fit (determined by v_threshold), a ValueError is raised. It means that you have to check your torsional scan datapoints for errors. """ self.rot_scan = rot_scan self.cancel_freq = cancel_freq self.cancel_method = 'given by the user' # the cancelation frequency if self.cancel_freq == 'mbh': self.cancel_freq = compute_cancel_frequency_mbh( molecule, self.rot_scan.dihedral, self.rot_scan.top_indexes ) self.cancel_method = 'computed with mbh' elif self.cancel_freq == 'scan': if self.rot_scan.potential is None: raise RotorError("The option cancel_freq='scan' can only be used with hindered rotors.") self.cancel_method = 'derived from the torsional potential' # the actual computation is performed later else: raise ValueError("Could not interpret cancel_freq=%s" % cancel_freq) # if suffix is None: self.suffix = "_".join(str(i) for i in rot_scan.top_indexes) else: self.suffix = suffix self.rotsym = rotsym self.even = even self.num_levels = num_levels self.dofmax = dofmax self.v_threshold = v_threshold self.large_fixed = large_fixed if rot_scan.potential is None: Info.__init__(self, "free_rotor_%s" % self.suffix) else: Info.__init__(self, "hindered_rotor_%s" % self.suffix) StatFysTerms.__init__(self, 2) # two terms