class FCHK(object): def __init__(self, fchkname='gaussian.fchk', w=None): self.mol = VibToolsMolecule() self.modes = None self.filename = fchkname self.lwl = w if os.path.splitext(self.filename)[1] in [".bz2", ".BZ2"]: self.bz2 = True else: self.bz2 = False freqs = property(lambda self: self.modes.freqs) def read(self): self.mol.read_from_fchk(filename=self.filename) self.modes = VibModes(self.mol.nmodes, self.mol) if self.lwl is None: self.lwl = self.get_pulsation() def get_pulsation(self): """ Get the w value used in the TDHF(or equivalent) approach """ lwls = self.get_property("Frequencies for FD properties") # Only take the first non-zero pulsation in the followings lwl = None if lwls is not None: for puls in lwls: if puls > 0.0: lwl = float(puls) break print("Pulsations in the calculations:", lwls) print("Pulsation used:", lwl) else: print("No Pulsation found in the file. Assume Zero") lwl = 0.000 return lwl def get_property(self, prop): """ Read a property name "property" in the fchk file return a numpy vector """ f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block "property" first = 0 last = 0 cols = {"R": 5, "I": 6} numpytype = {"R": "d", "I": "int"} stype = "R" size = 0 for i, l in enumerate(lines): if prop in l: l = l[len(prop):] if "N=" in l: # read array of data pattern = r"\d+" size = int(re.findall(pattern, l)[0]) typepattern = r" [IR] " try: stype = re.findall(typepattern, l)[0].strip() except IndexError: raise Exception( "The type of data in fchk is not recognized") first = i + 1 nlines = (size - 1) // cols[stype] + 1 last = first + nlines break else: # single data typepattern = r" [IR] " try: stype = re.findall(typepattern, l)[0].strip() except IndexError: raise Exception( "The type of data in fchk is not recognized") if stype == 'I': pattern = r"\d+" return int(re.findall(pattern, l)[0]) elif stype == 'R': pattern = r"-?\d+\.\d*[ED]?[+\-]?\d*" return float(re.findall(pattern, l)[0]) lines = lines[first:last] if len(lines) == 0: return None # read the data data = [] for line in lines: data.extend([float(fl) for fl in line.split()]) data = numpy.array(data, numpytype[stype]) if data.size != size: raise Exception( "The number of data recovered [%i] is not the same as the size written in the file [%i]" % (data.size, size)) return data def get_forces(self): natoms = self.mol.natoms data = self.get_property("Cartesian Gradient") if isinstance(data, numpy.ndarray): forces = -data.reshape(natoms, 3) return forces def get_hessian(self): natoms = self.mol.natoms # find the block of date containing the Hessian # get the data (triangular matrix) data = self.get_property("Cartesian Force Constants") if data is None: return None index = 0 hessian = numpy.zeros((3 * natoms, 3 * natoms)) for i in range(3 * natoms): for j in range(i + 1): hessian[i, j] = data[index] index += 1 # fill the other half of the hessian matrix hessian = hessian + hessian.transpose() - numpy.diag( hessian.diagonal()) return hessian def read_normalmodes(self): f = open(self.filename, 'r') lines = f.readlines() f.close() natoms = self.mol.natoms freqs = numpy.zeros((self.modes.nmodes)) redmass = numpy.zeros((self.modes.nmodes)) normalmodes = numpy.zeros((0, )) first1 = 0 last1 = 0 first2 = 0 last2 = 0 for i, l in enumerate(lines): if "Vib-E2" in l: pattern = r"\d+" size = int(re.findall(pattern, l)[1]) first1 = i + 1 last1 = i + 1 + (size - 1) // 5 + 1 elif "Vib-Modes" in l: pattern = r"\d+" size = int(re.findall(pattern, l)[0]) first2 = i + 1 last2 = i + 1 + (size - 1) // 5 + 1 lines1 = lines[first1:last1] lines2 = lines[first2:last2] data = [] self.modes = VibModes(self.mol.nmodes, self.mol) if len(lines1) == 0: print( "No Normal modes found in fchk. Ask saveNM in the calculation." ) print("Compute from hessian instead") prop = Property(self) hessian_mw = prop.get_hessian_mw() if hessian_mw is not None: self.modes.ComputeModes_without_TR(hessian_mw) return for i, sline in enumerate(lines1): line = [float(fl) for fl in sline.split()] data.extend(line) for i in range(self.modes.nmodes): freqs[i] = data[i] redmass[i] = data[i + self.modes.nmodes] for sline in lines2: array = [float(fl) for fl in sline.split()] array = numpy.array(array) normalmodes = numpy.append(normalmodes, array) self.modes.set_modes_c_norm( normalmodes.reshape((self.modes.nmodes, 3 * natoms)), redmass) self.modes.set_freqs(freqs) def get_molecularorbitalenergies(self): """ Get the molecular orbital energies """ # get the block with the MO coefficients moen = self.get_property("Alpha Orbital Energies") return moen def get_molecularorbitalcoeffs(self): """ Get the molecular orbitals LCAO coefficients Output = M_ij with i which correspond to a MO and j to a AO Output = C+ """ # get the block with the MO coefficients mo = self.get_property("Alpha MO coefficients") if isinstance(mo, numpy.ndarray): nAO = self.get_property("Number of basis functions") nMO = self.get_property("Number of independent functions") mo = mo.reshape((nMO, nAO)) return mo def get_dipole(self): """ get dipole """ dipole = self.get_property("Dipole Moment") return dipole def get_dipole_deriv_c(self): """ get dmudx """ dmudx = self.get_property("Dipole Derivatives") if isinstance(dmudx, numpy.ndarray): dmudx = dmudx.reshape(self.mol.natoms, 3, 3) return dmudx get_AAT = (lambda self: self.get_magneticdipole_deriv_c()) def get_magneticdipole_deriv_c(self): """ get atomic axial tensor """ aat = self.get_property("AAT") if isinstance(aat, numpy.ndarray): aat = aat.reshape(self.mol.natoms, 3, 3) return aat def get_pollen(self): """ get polarizability """ alpha = {} #dictionary with alpha values for different pulsations frequencies = self.get_property("Frequencies for FD properties") data = self.get_property("Alpha(-w,w)") if data is None: dummy = self.get_property("Polarizability") if isinstance(dummy, numpy.ndarray): data = numpy.zeros((3, 3)) data[0, 0] = dummy[0] data[1, 0] = dummy[1] data[0, 1] = dummy[1] data[1, 1] = dummy[2] data[2, 0] = dummy[3] data[0, 2] = dummy[3] data[2, 1] = dummy[4] data[1, 2] = dummy[4] data[2, 2] = dummy[5] frequencies = [0.00000] if isinstance(data, numpy.ndarray): data = data.reshape((-1, 3, 3)) for lwl, a in zip(frequencies, data): alpha[(lwl, )] = a if len(alpha) == 0: return None return alpha def get_gtenlen(self): """ Read G'(-w;w) """ Gprime = {} #dictionary with Gprime values for different pulsations frequencies = self.get_property("Frequencies for FD properties") data = self.get_property("FD Optical Rotation Tensor") if isinstance(data, numpy.ndarray): data = data.reshape((-1, 3, 3)) for lwl, a in zip(frequencies, data): Gprime[( lwl, )] = a * lwl # gaussian gives the transpose of G'/w if len(Gprime) == 0: return None return Gprime def get_aten(self): """ read UpperA(-w;w) """ UpperA = {} #dictionary with UpperA values for different pulsations frequencies = self.get_property("Frequencies for FD properties") data = self.get_property("D-Q polarizability") if isinstance(data, numpy.ndarray): data = data.reshape((-1, 3, 6)) for lwl, a in zip(frequencies, data): a *= 1.5 # gaussian give the traceless A but not multiply by 3/2 UpperA[(lwl, )] = numpy.zeros((3, 3, 3)) UpperA[(lwl, )][:, 0, 0] = a[:, 0] UpperA[(lwl, )][:, 1, 1] = a[:, 1] UpperA[(lwl, )][:, 2, 2] = a[:, 2] UpperA[(lwl, )][:, 0, 1] = a[:, 3] UpperA[(lwl, )][:, 1, 0] = a[:, 3] UpperA[(lwl, )][:, 0, 2] = a[:, 4] UpperA[(lwl, )][:, 2, 0] = a[:, 4] UpperA[(lwl, )][:, 1, 2] = a[:, 5] UpperA[(lwl, )][:, 2, 1] = a[:, 5] if len(UpperA) == 0: return None return UpperA def get_pollen_deriv_c(self): """ Get dALphaDX """ dalphadx = {} #dictionary with dalphadx frequencies = self.get_property("Frequencies for DFD properties") data = self.get_property("Derivative Alpha(-w,w)") if data is None: dummy = self.get_property("Polarizability Derivatives") if isinstance(dummy, numpy.ndarray): data = numpy.zeros((self.mol.natoms * 3, 3, 3)) data[:, 0, 0] = dummy[0::6] data[:, 1, 0] = dummy[1::6] data[:, 0, 1] = dummy[1::6] data[:, 1, 1] = dummy[2::6] data[:, 2, 0] = dummy[3::6] data[:, 0, 2] = dummy[3::6] data[:, 2, 1] = dummy[4::6] data[:, 1, 2] = dummy[4::6] data[:, 2, 2] = dummy[5::6] frequencies = [0.00000] else: data = None if isinstance(data, numpy.ndarray): data = data.reshape((-1, self.mol.natoms, 3, 3, 3)) for lwl, dadx in zip(frequencies, data): dalphadx[(lwl, )] = dadx if len(dalphadx) == 0: return None return dalphadx def get_gtenlen_deriv_c(self): """ Get dGprimeDX """ dgprimedx = {} #dictionary with dgprimedx frequencies = self.get_property("Frequencies for DFD properties") data = self.get_property("Derivative FD Optical Rotation Tensor") if isinstance(data, numpy.ndarray): data = data.reshape((-1, self.mol.natoms, 3, 3, 3)) for lwl, dadx in zip(frequencies, data): dgprimedx[( lwl, )] = dadx * lwl # gaussian gives the transpose of G'/w if len(dgprimedx) == 0: return None return dgprimedx def get_aten_deriv_c(self): """ Get dUpperADX """ dupperAdx = { } #dictionary with dupperAdx values for different pulsations frequencies = self.get_property("Frequencies for DFD properties") data = self.get_property("Derivative D-Q polarizability") if isinstance(data, numpy.ndarray): data = data.reshape((-1, self.mol.natoms, 3, 3, 6)) for lwl, dadx in zip(frequencies, data): dadx *= 1.5 # gaussian give the traceless A but not multiply by 3/2 dupperAdx[(lwl, )] = numpy.zeros((self.mol.natoms, 3, 3, 3, 3)) dupperAdx[(lwl, )][:, :, :, 0, 0] = dadx[:, :, :, 0] dupperAdx[(lwl, )][:, :, :, 1, 1] = dadx[:, :, :, 1] dupperAdx[(lwl, )][:, :, :, 2, 2] = dadx[:, :, :, 2] dupperAdx[(lwl, )][:, :, :, 0, 1] = dadx[:, :, :, 3] dupperAdx[(lwl, )][:, :, :, 1, 0] = dadx[:, :, :, 3] dupperAdx[(lwl, )][:, :, :, 0, 2] = dadx[:, :, :, 4] dupperAdx[(lwl, )][:, :, :, 2, 0] = dadx[:, :, :, 4] dupperAdx[(lwl, )][:, :, :, 1, 2] = dadx[:, :, :, 5] dupperAdx[(lwl, )][:, :, :, 2, 1] = dadx[:, :, :, 5] if len(dupperAdx) == 0: return None return dupperAdx def get_energy_transition(self): data = self.get_property("ETran state values") GSen = self.get_property("SCF Energy") if (data is None) or (GSen is None): return None data = data.reshape((-1, 16)) # 16 values for each transition # energy, 3x <g|\mu|e>, 3x <g|mu_vel|e>, 3x <g|m|e>, 6 unknown energies = data[:, 0] # Make the difference with the smallest value of energy energies -= GSen return energies def get_dipole_transition(self): diptrans = self.get_property("ETran state values") if isinstance(diptrans, numpy.ndarray): diptrans = diptrans.reshape( (-1, 16)) # 16 values for each transition # energy, 3x <g|\mu|e>, 3x <g|mu_vel|e>, 3x <g|m|e>, 6 unknown diptrans = diptrans[:, 1:4] return diptrans def get_magneticdipole_transition(self): diptrans = self.get_property("ETran state values") if isinstance(diptrans, numpy.ndarray): diptrans = diptrans.reshape( (-1, 16)) # 16 values for each transition # energy, 3x <g|\mu|e>, 3x <g|mu_vel|e>, 3x <g|m|e>, 6 unknown diptrans = diptrans[:, 7:10] return diptrans def get_magneticshielding(self): """ Get GIAO nuclear magnetic shielding """ shielding = self.get_property("NMR shielding") if isinstance(shielding, numpy.ndarray): shielding = shielding.reshape((self.mol.natoms, 3, 3)) # order of the elements: for each atom: XX, YX, ZX, XY, YY, ZY, XZ, YZ, ZZ # reorder the elements for each atom: XX, XY, XZ, YX, YY, YZ, ZX, ZY, ZZ shielding = numpy.transpose(shielding, axes=(0, 2, 1)) shielding = shielding * Constants.a**2 * 1.0e6 return shielding def get_density(self): """ Get the non-perturbed density """ data = self.get_property("Total SCF Density") density = None if isinstance(data, numpy.ndarray): nbasis = self.get_property("Number of basis functions") index = 0 density = numpy.zeros((nbasis, nbasis)) for i in range(nbasis): for j in range(i + 1): density[i, j] = data[index] index += 1 # fill the other half of the density matrix density = density + density.transpose() - numpy.diag( density.diagonal()) return density def get_spindensity(self): """ Get the spin density """ data = self.get_property("Spin SCF Density") density = None if isinstance(data, numpy.ndarray): nbasis = self.get_property("Number of basis functions") index = 0 density = numpy.zeros((nbasis, nbasis)) for i in range(nbasis): for j in range(i + 1): density[i, j] = data[index] index += 1 # fill the other half of the density matrix density = density + density.transpose() - numpy.diag( density.diagonal()) return density def get_density_magnetic(self): """ Get the perturbed density by magnetic field """ data = self.get_property("Magnetic Field P1 (GIAO)") density = None if isinstance(data, numpy.ndarray): nbasis = self.get_property("Number of basis functions") index = 0 density = numpy.zeros((3, nbasis, nbasis)) for idir in range(3): for i in range(nbasis): for j in range(i + 1): density[idir, i, j] = data[index] index += 1 # fill the other half of the density matrix density[idir] = density[idir] - density[idir].transpose() return density def get_basisset(self): """ read the basis set return a list of AO objects """ nshell = self.get_property("Number of contracted shells") shelltype = self.get_property("Shell types") primitive_pershell = self.get_property( "Number of primitives per shell") shell2atom = self.get_property("Shell to atom map") primitive_exp = self.get_property("Primitive exponents") primitive_coeff = self.get_property("Contraction coefficients") primitive_coeff_p = self.get_property( "P(S=P) Contraction coefficients") atomicnumbers = self.get_property("Atomic numbers") # basis set list basisset = [] start = 0 for ishell in range(nshell): nprim = primitive_pershell[ishell] # get the exponents and coeffs for all the primitive of the shell exponents = primitive_exp[start:start + nprim] coefficients = primitive_coeff[start:start + nprim] coefficients_p = None if primitive_coeff_p is not None: if numpy.nonzero( primitive_coeff_p[start:start + nprim])[0].size != 0: coefficients_p = primitive_coeff_p[start:start + nprim] iatom = shell2atom[ishell] an = int(atomicnumbers[iatom - 1]) atype = int(shelltype[ishell]) shell = BasisSet.SHELL(iatom, an, atype, exponents=exponents, coefficients=coefficients, coefficients_p=coefficients_p) basisset.append(shell) start = start + nprim if len(basisset) == 0: basisset = None else: basisset = BasisSet.BasisSet(basisset) # basisset.check_contractedcoeff() return basisset
class DATA(object): def __init__(self, logname='file.data', w=None): self.mol = VibToolsMolecule() self.modes = None self.filename = logname self.lwl = w freqs = property(lambda self: self.modes.freqs) def read(self): self.mol.read_from_data(filename=self.filename) self.modes = VibModes(self.mol.nmodes, self.mol) if self.lwl is None: self.lwl = self.get_pulsation() def get_pulsation(self): """ Get the first Pulsation value in the data file """ f = open(self.filename, 'r') lines = f.readlines() f.close() pattern = r"[+-]?0\.\d{6}" lwls = [] for l in lines: if "Pulsation:" in l: alist = re.findall(pattern, l) w = [float(x) for x in alist] for puls in w: if puls not in lwls: lwls.append(puls) # # Only take the first non-zero pulsation in the followings lwl = None if len(lwls) != 0: for puls in lwls: if puls > 0.0: lwl = puls break print("Pulsations in the calculations:", lwls) print("Pulsation used:", lwl) return lwl get_forces = (lambda self: self.get_mechproperty("Forces")) get_hessian = (lambda self: self.get_mechproperty("Hessian")) get_cubic_forces = (lambda self: self.get_mechproperty("Cubic Forces")) def read_normalmodes(self): f = open(self.filename, 'r') lines = f.readlines() f.close() natoms = self.mol.natoms # find the block of date containing the normal modes first = 0 last = 0 for i, l in enumerate(lines): if "[Vibrational Normal Modes]" in l: first = i + 2 elif (first != 0) and "[" in l: last = i break elif (first != 0) and i == len(lines) - 1: last = i + 1 break lines = lines[first:last] freqs = [] redmass = [] normalmodes = numpy.zeros((0, )) for i, l in enumerate(lines): if "Frequencies " in l: freqs.extend(l.split()[1:]) elif "Reduced-masses" in l: redmass.extend(l.split()[1:]) elif "Coord" in l: block = lines[i + 1:i + 3 * natoms + 1] array = [] for blockline in block: array.append([float(fl) for fl in blockline.split()[3:]]) array = numpy.array(array) array = array.transpose() normalmodes = numpy.append(normalmodes, array) freqs = numpy.array([float(fl) for fl in freqs]) redmass = numpy.array([float(fl) for fl in redmass]) self.modes = VibModes(self.mol.nmodes, self.mol) if normalmodes.shape == (0, ): print("No normal modes found") print("Compute from hessian instead") from VibTools import Properties prop = Properties.Property(self) hessian_mw = prop.get_hessian_mw() if hessian_mw is not None: self.modes.ComputeModes_without_TR(hessian_mw) return else: self.modes.set_modes_c_norm( normalmodes.reshape((self.modes.nmodes, 3 * natoms)), redmass) self.modes.set_freqs(freqs) def read_property(self, lines): """ read the property from a string """ # remove empty lines and lines which start with Param lines = [l for l in lines if not len(l.strip()) == 0] lines = [l for l in lines if not l.startswith('Param')] nblines = len(lines) prop = numpy.zeros((nblines, 3)) for iline, l in enumerate(lines): prop[iline] = [float(x) for x in l.strip().split()] return prop.ravel() def read_property_transition(self, lines): """ read the property transition from a string """ # remove empty lines and lines which start with Param lines = [l for l in lines if not len(l.strip()) == 0] lines = [l for l in lines if not l.startswith('Param')] nblines = len(lines) prop = [] for l in lines: prop.append([float(x) for x in l.strip().split()[1:]]) prop = numpy.array(prop) return prop.reshape((nblines, -1)) def get_mechproperty(self, name): """ get any type of mechanical property param name:name of the property """ f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block of data containing the mechanical property prop = None for i, l in enumerate(lines): if "[" + name.strip() + "]" in l: nbelems = int(lines[i + 1].split()[1]) if nbelems == 3 * self.mol.natoms: nblines = self.mol.natoms else: nblines = nbelems // 3 + nbelems // (3 * self.mol.natoms) prop = self.read_property(lines[i + 2:i + 2 + nblines]) break return prop def get_property(self, name): """ get any type of property param name:name of the property using the various operators from the response function add _deriv_c for cartesian first-order derivative add _deriv_c2 for cartesian second-order derivative return: a dictinary with pulsations as keys """ from VibTools import Properties f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block of data containing the property props = [] list_omegas = [] puls_size = Properties.get_pulsation_size(name) for i, l in enumerate(lines): if "[" + name.strip() + "]" in l: omegas = [] if puls_size > 0: omegas = [ float(x) for x in lines[i + 1].strip().split()[1:] ] print("[%s] found with pulsation %s" % (name.strip(), omegas)) prop_size = Properties.get_property_size(name) nblines = int(prop_size // 3) nparams = 1 # deriv_c? if name.find("_deriv") != -1: try: order = int(name[name.find("_deriv") + 8:]) except ValueError: order = 1 nparams = (3 * self.mol.natoms)**order nblines = (nblines + 1) * nparams prop = self.read_property(lines[i + 2:i + 2 + nblines]) if nparams > 1: prop = prop.reshape((nparams, prop_size)) list_omegas.append(tuple(omegas)) props.append(prop) if len(props) == 0: return None props = dict(zip(list_omegas, props)) if puls_size == 0: props = props[()] return props def get_property_transition(self, name): """ get any type of property transition param name:name of the property using the various operators from the response function add _deriv_c for cartesian first-order derivative add _deriv_c2 for cartesian second-order derivative """ from VibTools import Properties f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block of data containing the property prop = None for i, l in enumerate(lines): if "[" + name.strip() + "_transition]" in l: # if w is not None: # omegas=[float(x) for x in lines[i+1].strip().split()[1:]] # if tuple(omegas) != w: # continue nstates = int(lines[i + 1].strip().split()[1]) prop_size = Properties.get_property_size(name) nblines = nstates nparams = 1 # deriv_c? if name.find("_deriv") != -1: try: order = int(name[name.find("_deriv") + 8:]) except ValueError: order = 1 nparams = (3 * self.mol.natoms)**order nblines = (nblines + 1) * nparams prop = self.read_property_transition(lines[i + 2:i + 2 + nblines]) if nparams > 1: prop = prop.reshape((-1, nparams, prop_size)) break return prop def get_FCfactors(self, istate): """ get Franck Condon factors for state istate """ f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block of data containing the property FCfactors = None tline = "[FC factors of state %i]" % (istate) for i, l in enumerate(lines): if tline in l: nbfc = int(lines[i + 2].strip().split()[1]) FCfactors = [] for line in lines[i + 4:i + 4 + nbfc]: FCfactors.append( [float(x) for x in line.strip().split()[0:2]]) FCfactors = numpy.array(FCfactors).transpose() return FCfactors def get_Delta_factors(self, istate): """ get Dimensionless Delta factors for state istate """ f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block of data containing the property Delta = None tline = "[Dimensionless Delta factors for state %i]" % (istate) for i, l in enumerate(lines): if tline in l: nbmodes = int(lines[i + 1].strip().split()[1]) Delta = [] for line in lines[i + 3:i + 3 + nbmodes]: Delta.append(float(line.strip())) Delta = numpy.array(Delta) return Delta def get_HuangRhys_factors(self, istate): """ get HuangRhys factors for state istate """ f = open(self.filename, 'r') lines = f.readlines() f.close() # find the block of data containing the property HR = None tline = "[Huang-Rhys factors for state %i]" % (istate) for i, l in enumerate(lines): if tline in l: nbmodes = int(lines[i + 1].strip().split()[1]) HR = [] for line in lines[i + 3:i + 3 + nbmodes]: HR.append(float(line.strip())) HR = numpy.array(HR) return HR