def __init__(self, FI=None): na = 'n/a' self.machine_name = '' self.FI = FI self.JobType = 'sp' self.lot, self.basis = '', '' self.lot_suffix = '' self.route_lines, self.l9999 = '', '' #G self.sym = 'C1' self.n_proc = 1 self.n_atoms = 0 self.n_electrons = 0 self.n_primitives = 0 self.charge, self.mult, self.s2 = None, None, 0 self.openShell = False self.solvent, self.solv_model = '', '' self.geoms, self.vector = ListGeoms(), [] self.topologies = [] self.scf_e = 0. self.scf_done, self.ci_cc_done = False, False self.postHF_lot, self.postHF_e = [], [] self.postHF = {} self.scf_conv, self.ci_cc_conv = [], [] self.amplitude = 0.0 self.T1_diagnostic = 0.0 self.opt_iter = 0 self.opt_ok = False self.max_force, self.rms_force = [], [] self.max_displacement, self.rms_displacement = [], [] self.series = None self.scan_param_description = {} self.grad = 0. self.frozen = {} self.n_steps = 0 self.n_states = 0 self.freq_temp, self.freq_ent, self.freq_zpe, self.freq_G = [], [], [], [] self.freqs = [] self.nimag = 0 self.uv = {} self.comments, self.warnings, self.extra = '', '', '' self.chk = None self.OK = False self.blank = False self.time = 0
def __init__(self,FI=None): na = 'n/a' self.machine_name = '' self.FI = FI self.JobType = 'sp' self.lot, self.basis = '','' self.lot_suffix = '' self.route_lines, self.l9999 = '','' #G self.sym = 'C1' self.n_proc = 1 self.n_atoms = 0 self.n_electrons = 0 self.n_primitives = 0 self.charge, self.mult, self.s2 = None, None, 0 self.openShell = False self.solvent, self.solv_model = '','' self.geoms, self.vector = ListGeoms(), [] self.topologies = [] self.scf_e = 0. self.scf_done, self.ci_cc_done = False, False self.postHF_lot, self.postHF_e = [], [] self.postHF = {} self.scf_conv, self.ci_cc_conv = [], [] self.amplitude = 0.0 self.T1_diagnostic = 0.0 self.opt_iter = 0 self.opt_ok = False self.max_force, self.rms_force = [], [] self.max_displacement, self.rms_displacement = [], [] self.series = None self.scan_param_description = {} self.grad = 0. self.frozen = {} self.n_steps = 0 self.n_states = 0 self.freq_temp, self.freq_ent, self.freq_zpe, self.freq_G = [], [], [], [] self.freqs = [] self.nimag = 0 self.uv = {} self.comments, self.warnings, self.extra = '','','' self.chk = None self.OK = False self.blank = False self.time = 0
def parse(self): """ Actual parsing happens here """ t_ifreq_done = False self.all_coords = {} s = 'BLANC' # It got to be initialized! while not self.FI.eof: next(self.FI) if self.FI.eof: break s = self.FI.s.rstrip() # # ---------------------------------------- Read in cartesian coordinates ---------------------------------- # # Have we found coords? enter_coord = False if s.find('CARTESIAN COORDINATES (ANGSTROEM)')==0: coord_type = 'Cartesian Coordinates (Ang)' enter_coord = True # If yes, then read them if enter_coord: try: # Positioning dashes1 = next(self.FI) s = next(self.FI) # Read in coordinates geom = Geom() atnames = [] while len(s)>1: xyz = s.strip().split() try: atn, x,y,z = xyz[0], xyz[1],xyz[2],xyz[3] except: log.warning('Error reading coordinates:\n%s' % (s)) break atnames.append(atn) geom.coord.append('%s %s %s %s' % (atn,x,y,z)) s = next(self.FI) # Add found coordinate to output pc = AtomicProps(attr='atnames',data=atnames) geom.addAtProp(pc,visible=False) # We hide it, because there is no use to show atomic names for each geometry using checkboxes if not coord_type in self.all_coords: self.all_coords[coord_type] = {'all':ListGeoms(),'special':ListGeoms()} self.all_coords[coord_type]['all'].geoms.append(geom) except StopIteration: log.warning('EOF while reading geometry') break # # ------------------------------------------- Route lines ------------------------------------------------- # if s.find('Your calculation utilizes the basis')==0: self.basis = s.split()[5] if s.find(' Ab initio Hamiltonian Method')==0: self.lot = s.split()[5] if s.find(' Exchange Functional')==0: self.lot = s.split()[4] if s.find(' Correlation Functional')==0: s_corr = s.split()[4] if s_corr != self.lot: self.lot = s.split()[4] + s_corr if s.find('Correlation treatment')==0: self.lot = s.split()[3] if s.find('Perturbative triple excitations ... ON')==0: self.lot += '(T)' if s.find('Calculation of F12 correction ... ON')==0: self.lot += '-F12' if s.find('Integral transformation ... All integrals via the RI transformation')==0: self.lot += '-RI' if s.find('K(C) Formation')==0: if 'RI' in s and 'RI' in self.lot: self.lot = self.lot.replace('RI',s.split()[3]) else: self.lot += '+'+s.split()[3] if s.find('Hartree-Fock type HFTyp')==0: if s.split()[3]=='UHF': self.openShell = True if s.find('T1 diagnostic')==0: self.T1_diagnostic = s.split()[3] if s.find('E(CCSD(T)) ...')==0: self.postHF_lot.append('CCSD(T)') self.postHF_e.append(s.split()[2]) self.postHF["CCSD(T)"]=s.split()[2] if s.find('E(CCSD) ...')==0: self.postHF_lot.append('CCSD') self.postHF_e.append(s.split()[2]) self.postHF["CCSD"]=s.split()[2] if s.find(' * SCF CONVERGED AFTER')==0: self.FI.skip_until('Total Energy') self.scf_e = float(self.FI.s.split()[3]) self.scf_done = True for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp('e', self.scf_e) # TODO Read in something like self.best_e instead! # S^2 if s.find('Expectation value of <S**2> :')==0: s_splitted = s.split() before = s_splitted[5] self.s2 = before for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp('s2',self.s2) if s.find(' * Geometry Optimization Run *')==0: self.JobType = 'opt' if 'opt' in self.JobType: if s.find(' ----------------------|Geometry convergence')==0: self.opt_iter += 1 try: next(self.FI) # skip_n Item value next(self.FI) # skip_n ------ for conv in ('max_force','rms_force','max_displacement','rms_displacement'): s = next(self.FI) x, thr = float(s.split()[2]),float(s.split()[3]) conv_param = getattr(self,conv) conv_param.append(x-thr) for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp(conv, x-thr) except: log.warning('EOF in the "Converged?" block') break if s.find(' *** THE OPTIMIZATION HAS CONVERGED ***')==0: self.opt_ok = True # # -------------------------------------------- Scan ------------------------------------------------------- # if s.find(' * Relaxed Surface Scan *')==0: self.JobType = 'scan' if 'scan' in self.JobType: """ Order of scan-related parameters: 1. Geometry, 2. Energy calculated for that geometry 3. Optimization convergence test If Stationary point has been found, we already have geometry with energy attached as prop, so we just pick it up """ # Memorize scan geometries if s.find(' *** THE OPTIMIZATION HAS CONVERGED ***')==0: for ct in self.all_coords.values(): if ct['all']: ct['special'].geoms.append(ct['all'][-1]) # Record scanned parameters # Designed to work properly only for 1D scans! if s.find(' * RELAXED SURFACE SCAN STEP')==0: next(self.FI) s = next(self.FI) param = s[12:45].strip() # Will work properly only for bonds at this point mt=re.compile('Bond \((.*?),(.*?)\)').match(param) param = 'Bond(' + str(1+int(mt.group(1))) + ',' + str(1+int(mt.group(2))) + ')' param_full = float(s[46:59].strip()) #print('|'+s[46:59]+'|'+str(param_full)) for ct in self.all_coords.values(): if ct['special']: ct['special'][-1].addProp(param,param_full) # # ---------------------------------------- Read simple values --------------------------------------------- # #Nproc if s.find(' * Program running with 4') == 0: self.n_cores = s.split()[4] # Read Symmetry if s.find('POINT GROUP')==0: self.sym = s.split()[3] # Read charge_multmetry if s.find('Total Charge Charge')==0: self.charge = s.split(4) if s.find('Multiplicity Mult')==0: self.mult = s.split(4) if 'ORCA TERMINATED NORMALLY' in s: self.OK = True next(self.FI) break # We got here either self.blanc = (s=='BLANC') return
class ElectronicStructure(Top): def __init__(self,FI=None): na = 'n/a' self.machine_name = '' self.FI = FI self.JobType = 'sp' self.lot, self.basis = '','' self.lot_suffix = '' self.route_lines, self.l9999 = '','' #G self.sym = 'C1' self.n_proc = 1 self.n_atoms = 0 self.n_electrons = 0 self.n_primitives = 0 self.charge, self.mult, self.s2 = None, None, 0 self.openShell = False self.solvent, self.solv_model = '','' self.geoms, self.vector = ListGeoms(), [] self.topologies = [] self.scf_e = 0. self.scf_done, self.ci_cc_done = False, False self.postHF_lot, self.postHF_e = [], [] self.scf_conv, self.ci_cc_conv = [], [] self.amplitude = 0.0 self.opt_iter = 0 self.opt_ok = False self.max_force, self.rms_force = [], [] self.max_displacement, self.rms_displacement = [], [] self.series = None self.scan_param_description = {} self.grad = 0. self.frozen = {} self.n_steps = 0 self.n_states = 0 self.freq_temp, self.freq_ent, self.freq_zpe, self.freq_G = [], [], [], [] self.freqs = [] self.nimag = 0 self.uv = {} self.comments, self.warnings, self.extra = '','','' self.chk = None self.OK = False self.blanc = False self.time = 0 def webData(self,StartApplet=True): we = self.settings.Engine3D() io = IO() color = {'err':'red', 'imag':'blue', 'lot':'green'} b2, JmolScript = '', '' comments = [] if self.JobType: sx = self.JobType.upper() if 'irc' in self.JobType: sx += ' ' + self.series.textDirection() sx = web.br + web.tag(sx,'strong') if self.OK: b2 += sx else: b2 += web.tag(sx,"SPAN style='color:%s'" % (color['err'])) if self.lot: if self.basis: self.lot += '/' + self.basis b2 += web.br + web.tag(self.lot.upper(),"SPAN style='color:%s'" % (color['lot'])) if self.solvent: sx = 'Solvation: ' if self.solv_model: sx += '%s(%s)' % (self.solv_model, self.solvent) else: sx += self.solvent b2 += web.br + web.tag(sx,"SPAN style='color:%s'" % (color['lot'])) if self.sym: b2 += web.br + "Symmetry: %s\n" % (self.sym) if self.charge: b2 += web.br + "Charge: %s; " % (self.charge) if self.mult: b2 += "Mult: %s\n" % (self.mult) if self.lot and not self.lot.find('R')==0: b2 += web.br + "S2= %s,\n" % (self.s2) if self.scf_e: b2 += web.br + "E_SCF= %-11.6f\n" % (self.scf_e) if self.amplitude: f_ampl = float(self.amplitude) s_ampl = '%.3f' % (f_ampl) if f_ampl >= 0.1: sx = web.tag(s_ampl,"SPAN style='color:%s'" % (color['err'])) else: sx = s_ampl b2 += web.br + "Max Amplitude= %s\n" % (sx) # Add pics for SP if 'sp' in self.JobType and not self.OK: wftype = '' if not self.ci_cc_done: wftype = 'ci_cc' if not self.scf_done: wftype = 'scf' if wftype: b2 += web.br + wftype + ' not converged...' y = getattr(self,wftype+'_conv') picpath = io.writePic('-sp-conv.png',xname='Step N',yname='E, '+self.settings.EnergyUnits,y=y) b2 += web.img(picpath) # Freq if 'freq' in self.JobType: # Give thermochemistry values for i in range(len(self.freq_temp)): b2 += web.br + "T=%6.2f: H= %10.6f, E+ZPE= %10.6f, G= %10.6f\n" \ % (self.freq_temp[i],self.freq_ent[i],self.freq_zpe[i],self.freq_G[i]) if self.freqs: # Show freqs b2 += web.br + "Freqs: " # Color i-freqs i = 0 while self.freqs[i] < 0: s_freq = "% .1f," % (self.freqs[i]) if i == 0: col = 'imag' else: col = 'err' b2 += web.tag(s_freq, "SPAN style='color:%s'" % (color[col])) i += 1 b2 += "%.1f .. %.1f\n" % (self.freqs[i], self.freqs[-1]) if self.nimag > 0: b2 += web.brn + web.tag('Imaginary Freq(s) found!',"SPAN style='color:%s'" % (color['imag'])) # Frozen if self.frozen: frs = self.frozen.values() self.extra += web.br + 'Frozen parameters detected (highlighted with measurement lines)' JmolScript += we.measureGau(frs) if len(frs)>3: JmolScript += 'set measurementlabels off;' # Opt if 'opt' in self.JobType: b2 += web.br + web.tag('NOpt=%i' % (self.opt_iter),'em') if not self.opt_ok: b2 += web.br + "Stationary Point not found!\n" if (not self.OK) or self.settings.FullGeomInfo: sg = self.geoms y = [sg.toBaseLine(), sg.max_force, sg.rms_force, sg.max_displacement, sg.rms_displacement] ylabel = 'E, %s' % (self.settings.EnergyUnits) picpath = io.writePic('-opt-conv.png', xname='Step N',yname=ylabel, keys=['E','Max Force', 'RMS Force', 'Max Displacement', 'RMS Displacement'], y=y,ny2=4 ) b2 += web.img(picpath) #b2 += self.geoms.plot(xlabel='Opt point') # IRC if 'irc' in self.JobType: b2 += self.series.webData() comments = self.series.comments # Scan if 'scan' in self.JobType: #print self.series.props b2 += self.series.webData() JmolScript += we.measureGau(self.series.props) # TD DFT if 'td' in self.JobType and self.uv: b2 += web.brn + web.tag('UV Spectra','em') + web.brn for w in sorted(self.uv): #if w > 1000.: if self.uv[w] > 0.01: b2 += "%s %s\n" % (w, self.uv[w]) + web.brn b2 += web.brn # # Charges # sx = '' for i in range(len(self.geoms)): g = self.geoms[i] if g.atprops: sx += 'Structure %i: ' % (i+1) for ap in g.atprops: sx += getattr(g,ap).webData() sx += we.JMolButton('label off;color atoms cpk','Off') + web.brn if self.settings.full and hasattr(g,'nbo_analysis'): nbo_b1,nbo_b2 = g.nbo_analysis.webData() sx += nbo_b2 b2 += web.brn + sx # NBO Topology nbobonds = '' bo = ('-','S','D','T','Q') if self.topologies: pass # TODO write this topology to MOL file #for i in self.nbo_topology: #for j in self.nbo_topology[i]: #nbobonds += "%s %s %s " % (bo[self.nbo_topology[i][j]],i,j) if self.comments: b2 += web.br + web.tag('Comments','strong') + ":%s\n" % self.comments if self.warnings: b2 += web.br + web.tag('Warnings','strong') + ":%s\n" % self.warnings if self.extra: b2 += web.br + web.tag(self.extra,'em') b2 += web.br # # ----- b1 ----- # #if self.nbo_topology and not self.vectors: wp = self.geoms.write(fname='.xyz', vectors=self.vector) labeltext = '%s: %s' %(self.JobType,self.lot) if StartApplet: JmolScript += '; ' + we.JMolText(label=labeltext.upper(),script=False) #JmolScript += '; ' + we.JMolText(label='model %{_modelNumber}',position='bottom left', script=False) JmolScript += '; ' + we.JMolText(label='model _modelNumber',position='bottom left', script=False) b1 = we.JMolApplet(webpath=wp, ExtraScript = JmolScript) b1 += web.brn + we.JMolCommandInput() if len(self.geoms)>1: b1 += web.brn + we.MultipleGeoms() else: b1 = we.JMolLoad(webpath=wp, ExtraScript=JmolScript) b1 += '; ' + we.JMolText(label=labeltext.upper(),script=False) #b1 += '; ' + we.JMolText(label='model %{_modelNumber}',position='bottom left', script=False) b1 += '; ' + we.JMolText(label='model _modelNumber',position='bottom left', script=False) log.debug('webData for Gaussian step generated successfully') return b1, b2
class FchkGaussian(ElectronicStructure): """ Shows 3D-properties from the .fchk file """ def __init__(self): self.densities = [] self.openshell = False self.cubes = [] self.isotype='' self.isovalue='0.03' ElectronicStructure.__init__(self) self.OK = True def makeCube(self,prop,name='',colors=''): fcube = self.settings.realPath(prop+'.cube') wpcube = self.settings.webPath(prop+'.cube') command = (self.settings.cubegen, '0', prop, self.file, fcube, self.settings.npoints_cube, 'h') t1 = time.time() log.debug('Trying to run command: "%s"' % (str(command)) ) subprocess.call(command) t2 = time.time() log.debug('Running cubegen: %.1f s' % (t2-t1)) if os.path.exists(fcube): log.debug('%s successfully generated' % (fcube)) else: log.warning('%s has not been created' % (fcube)) c = Cube(name,colors) c.file = fcube c.wpcube = wpcube c.isotype = prop.split('=')[0] c.isovalue = self.isovalue c.parse() return c def parse(self): """ Here, .fchk will be parsed as a text file Probably, we start here, because .fchk contains valuable information which might be used """ try: FI = open(self.file) log.debug('%s was opened for reading' %(self.file)) except: log.error('Cannot open %s for reading' %(self.file)) """ http://www.gaussian.com/g_tech/g_ur/f_formchk.htm All other data contained in the file is located in a labeled line/section set up in one of the following forms: Scalar values appear on the same line as their data label. This line consists of a string describing the data item, a flag indicating the data type, and finally the value: Integer scalars: Name,I,IValue, using format A40,3X,A1,5X,I12. Real scalars: Name,R,Value, using format A40,3X,A1,5X,E22.15. Character string scalars: Name,C,Value, using format A40,3X,A1,5X,A12. Logical scalars: Name,L,Value, using format A40,3X,A1,5X,L1. Vector and array data sections begin with a line naming the data and giving the type and number of values, followed by the data on one or more succeeding lines (as needed): Integer arrays: Name,I,Num, using format A40,3X,A1,3X,'N=',I12. The N= indicates that this is an array, and the string is followed by the number of values. The array elements then follow starting on the next line in format 6I12. Real arrays: Name,R,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string again indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5E16.8. Note that the Real format has been chosen to ensure that at least one space is present between elements, to facilitate reading the data in C. Character string arrays (first type): Name,C,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5A12. Character string arrays (second type): Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 9A8. Logical arrays: Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 72L1. All quantities are in atomic units and in the standard orientation, if that was determined by the Gaussian run. Standard orientation is seldom an interesting visual perspective, but it is the natural orientation for the vector fields. """ def split_array(s,reclength): v = [] nrec = int(math.ceil((len(s)-1.0)/reclength)) for i in range(nrec): rec = s[reclength*i:reclength*(i+1)].strip() v.append(rec) return v self.parsedProps = {} format_arrays = { 'I' : [6.,12], 'R' : [5.,16], 'C' : [5.,12], 'H' : [9.,8], } try: self.comments = FI.next().rstrip() s = FI.next().rstrip() self.JobType, self.lot, self.basis = s[0:10],s[10:20],s[70:80] for s in FI: s = s.rstrip() array_mark = (s[47:49] == 'N=') if array_mark: value = [] prop, vtype, nrec = s[:40].strip(), s[43], int(s[49:]) fa = format_arrays[vtype] nlines = int(math.ceil(nrec/fa[0])) for _ in range(nlines): s = FI.next() v5 = split_array(s,fa[1]) value.extend(v5) else: prop, vtype, value = s[:40].strip(), s[43], s[49:].strip() self.parsedProps[prop] = value except StopIteration: log.warning('Unexpected EOF') FI.close() log.debug('%s parsed successfully' % (self.file)) return def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s)<>0: return True return False # def getGeom(ar,atnum,atnames,start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase+3] x, y, z = map(lambda k: float(k)*Bohr, xyz) g.coord.append('%s %f %f %f' % (atn,x,y,z)) atbase += 3 pc = AtomicProps(attr='atnames',data=atnames) g.addAtProp(pc,visible=False) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before,s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = map(lambda k: int(float(k)), pp['Nuclear charges']) atnum = int(pp['Number of atoms']) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ('Opt point 1 Geometries' in pp) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base,exi = 0,0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'],atnum,atnames,base) e,x = irc_ex[exi:exi+2] g.addProp('x',float(x)) g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base,ezi = 0,0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'],atnum,atnames,base) e,z = opt_ez[ezi:ezi+2] g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'],atnum,atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch,data = charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF','MP2','CI','QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' %(k,float(e)) + web.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt) def generateAllCubes(self): # {A,B}MO=H**O LUMO ALL OccA OccB Valence Virtuals # Laplacian dprops = ['Density', 'Potential'] if self.openshell: dprops.append('Spin') props = ['AMO=H**O','BMO=H**O','AMO=LUMO','BMO=LUMO'] else: props = ['MO=H**O','MO=LUMO'] for d in self.densities: for p in dprops: prop = '%s=%s' % (p,d) c = self.makeCube(prop) self.cubes.append((c,prop)) for p in props: c = self.makeCube(p) self.cubes.append((c,p)) def webData(self): we = self.settings.Engine3D() b1,b2 = ElectronicStructure.webData(self) if self.settings.full: # Show all cubes self.generateAllCubes() s = '' for c,p in self.cubes: first_cube = c.wpcube ctype = p[:p.find('=')] if ctype == 'Density': continue elif ctype == 'Potential': first_cube = c.wpcube.replace('Potential','Density') second_cube = c.wpcube script = we.JMolIsosurface(webpath = first_cube, webpath_other = second_cube, surftype=ctype) else: script = c.s_script s += we.JMolButton(action=script, label=p) b2 += s elif self.isotype: # Show only requested cube p = self.isotype.lower() p_splitted = p.split('=') ctype = p_splitted[0] if len(p_splitted)>1: cvalue = p_splitted[1] if ctype == 'potential': p_pot = p p_dens = p.replace('potential','Density') c_pot = self.makeCube(p_pot) c_dens = self.makeCube(p_dens) first_cube = c_dens.wpcube second_cube = c_pot.wpcube script = we.JMolIsosurface(webpath = first_cube, webpath_other = second_cube, surftype=ctype) else: c = self.makeCube(p) script = c.s_script if ctype=='mo': if cvalue=='h**o': cvalue = self.parsedProps['Number of alpha electrons'] e_orb = float(self.parsedProps['Alpha Orbital Energies'][int(cvalue)-1])*27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype=='amo': e_orb = float(self.parsedProps['Alpha Orbital Energies'][int(cvalue)-1])*27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype=='bmo': e_orb = float(self.parsedProps['Beta Orbital Energies'][int(cvalue)-1])*27.211 b2 += 'E(BMO) = %.3f eV' % (e_orb) b2 += we.JMolButton(action=script, label=p) return b1,b2
def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s)<>0: return True return False # def getGeom(ar,atnum,atnames,start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase+3] x, y, z = map(lambda k: float(k)*Bohr, xyz) g.coord.append('%s %f %f %f' % (atn,x,y,z)) atbase += 3 pc = AtomicProps(attr='atnames',data=atnames) g.addAtProp(pc,visible=False) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before,s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = map(lambda k: int(float(k)), pp['Nuclear charges']) atnum = int(pp['Number of atoms']) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ('Opt point 1 Geometries' in pp) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base,exi = 0,0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'],atnum,atnames,base) e,x = irc_ex[exi:exi+2] g.addProp('x',float(x)) g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base,ezi = 0,0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'],atnum,atnames,base) e,z = opt_ez[ezi:ezi+2] g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'],atnum,atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch,data = charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF','MP2','CI','QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' %(k,float(e)) + web.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt)
class FchkGaussian(ElectronicStructure): """ Shows 3D-properties from the .fchk file """ def __init__(self): self.densities = [] self.openshell = False self.cubes = [] self.isotype = '' self.isovalue = '0.03' ElectronicStructure.__init__(self) self.OK = True def makeCube(self, prop, name='', colors=''): fcube = self.settings.realPath(prop + '.cube') wpcube = self.settings.webPath(prop + '.cube') command = (self.settings.cubegen, '0', prop, self.file, fcube, self.settings.npoints_cube, 'h') t1 = time.time() log.debug('Trying to run command: "%s"' % (str(command))) subprocess.call(command) t2 = time.time() log.debug('Running cubegen: %.1f s' % (t2 - t1)) if os.path.exists(fcube): log.debug('%s successfully generated' % (fcube)) else: log.warning('%s has not been created' % (fcube)) c = Cube(name, colors) c.file = fcube c.wpcube = wpcube c.isotype = prop.split('=')[0] c.isovalue = self.isovalue c.parse() return c def parse(self): """ Here, .fchk will be parsed as a text file Probably, we start here, because .fchk contains valuable information which might be used """ try: FI = open(self.file) log.debug('%s was opened for reading' % (self.file)) except: log.error('Cannot open %s for reading' % (self.file)) """ http://www.gaussian.com/g_tech/g_ur/f_formchk.htm All other data contained in the file is located in a labeled line/section set up in one of the following forms: Scalar values appear on the same line as their data label. This line consists of a string describing the data item, a flag indicating the data type, and finally the value: Integer scalars: Name,I,IValue, using format A40,3X,A1,5X,I12. Real scalars: Name,R,Value, using format A40,3X,A1,5X,E22.15. Character string scalars: Name,C,Value, using format A40,3X,A1,5X,A12. Logical scalars: Name,L,Value, using format A40,3X,A1,5X,L1. Vector and array data sections begin with a line naming the data and giving the type and number of values, followed by the data on one or more succeeding lines (as needed): Integer arrays: Name,I,Num, using format A40,3X,A1,3X,'N=',I12. The N= indicates that this is an array, and the string is followed by the number of values. The array elements then follow starting on the next line in format 6I12. Real arrays: Name,R,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string again indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5E16.8. Note that the Real format has been chosen to ensure that at least one space is present between elements, to facilitate reading the data in C. Character string arrays (first type): Name,C,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5A12. Character string arrays (second type): Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 9A8. Logical arrays: Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 72L1. All quantities are in atomic units and in the standard orientation, if that was determined by the Gaussian run. Standard orientation is seldom an interesting visual perspective, but it is the natural orientation for the vector fields. """ def split_array(s, reclength): v = [] nrec = int(math.ceil((len(s) - 1.0) / reclength)) for i in range(nrec): rec = s[reclength * i:reclength * (i + 1)].strip() v.append(rec) return v self.parsedProps = {} format_arrays = { 'I': [6., 12], 'R': [5., 16], 'C': [5., 12], 'H': [9., 8], } try: self.comments = FI.next().rstrip() s = FI.next().rstrip() self.JobType, self.lot, self.basis = s[0:10], s[10:20], s[70:80] for s in FI: s = s.rstrip() array_mark = (s[47:49] == 'N=') if array_mark: value = [] prop, vtype, nrec = s[:40].strip(), s[43], int(s[49:]) fa = format_arrays[vtype] nlines = int(math.ceil(nrec / fa[0])) for _ in range(nlines): s = FI.next() v5 = split_array(s, fa[1]) value.extend(v5) else: prop, vtype, value = s[:40].strip(), s[43], s[49:].strip() self.parsedProps[prop] = value except StopIteration: log.warning('Unexpected EOF') FI.close() log.debug('%s parsed successfully' % (self.file)) return def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s) <> 0: return True return False # def getGeom(ar, atnum, atnames, start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase + 3] x, y, z = map(lambda k: float(k) * Bohr, xyz) g.coord.append('%s %f %f %f' % (atn, x, y, z)) atbase += 3 pc = AtomicProps(attr='atnames', data=atnames) g.addAtProp( pc, visible=False ) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before, s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = map(lambda k: int(float(k)), pp['Nuclear charges']) atnum = int(pp['Number of atoms']) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ( 'Opt point 1 Geometries' in pp ) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base, exi = 0, 0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'], atnum, atnames, base) e, x = irc_ex[exi:exi + 2] g.addProp('x', float(x)) g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base, ezi = 0, 0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'], atnum, atnames, base) e, z = opt_ez[ezi:ezi + 2] g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'], atnum, atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch, data=charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF', 'MP2', 'CI', 'QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' % (k, float(e)) + web.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt) def generateAllCubes(self): # {A,B}MO=H**O LUMO ALL OccA OccB Valence Virtuals # Laplacian dprops = ['Density', 'Potential'] if self.openshell: dprops.append('Spin') props = ['AMO=H**O', 'BMO=H**O', 'AMO=LUMO', 'BMO=LUMO'] else: props = ['MO=H**O', 'MO=LUMO'] for d in self.densities: for p in dprops: prop = '%s=%s' % (p, d) c = self.makeCube(prop) self.cubes.append((c, prop)) for p in props: c = self.makeCube(p) self.cubes.append((c, p)) def webData(self): we = self.settings.Engine3D() b1, b2 = ElectronicStructure.webData(self) if self.settings.full: # Show all cubes self.generateAllCubes() s = '' for c, p in self.cubes: first_cube = c.wpcube ctype = p[:p.find('=')] if ctype == 'Density': continue elif ctype == 'Potential': first_cube = c.wpcube.replace('Potential', 'Density') second_cube = c.wpcube script = we.JMolIsosurface(webpath=first_cube, webpath_other=second_cube, surftype=ctype) else: script = c.s_script s += we.JMolButton(action=script, label=p) b2 += s elif self.isotype: # Show only requested cube p = self.isotype.lower() p_splitted = p.split('=') ctype = p_splitted[0] if len(p_splitted) > 1: cvalue = p_splitted[1] if ctype == 'potential': p_pot = p p_dens = p.replace('potential', 'Density') c_pot = self.makeCube(p_pot) c_dens = self.makeCube(p_dens) first_cube = c_dens.wpcube second_cube = c_pot.wpcube script = we.JMolIsosurface(webpath=first_cube, webpath_other=second_cube, surftype=ctype) else: c = self.makeCube(p) script = c.s_script if ctype == 'mo': if cvalue == 'h**o': cvalue = self.parsedProps['Number of alpha electrons'] e_orb = float(self.parsedProps['Alpha Orbital Energies'][ int(cvalue) - 1]) * 27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype == 'amo': e_orb = float(self.parsedProps['Alpha Orbital Energies'][ int(cvalue) - 1]) * 27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype == 'bmo': e_orb = float( self.parsedProps['Beta Orbital Energies'][int(cvalue) - 1]) * 27.211 b2 += 'E(BMO) = %.3f eV' % (e_orb) b2 += we.JMolButton(action=script, label=p) return b1, b2
def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s) <> 0: return True return False # def getGeom(ar, atnum, atnames, start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase + 3] x, y, z = map(lambda k: float(k) * Bohr, xyz) g.coord.append('%s %f %f %f' % (atn, x, y, z)) atbase += 3 pc = AtomicProps(attr='atnames', data=atnames) g.addAtProp( pc, visible=False ) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before, s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = map(lambda k: int(float(k)), pp['Nuclear charges']) atnum = int(pp['Number of atoms']) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ( 'Opt point 1 Geometries' in pp ) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base, exi = 0, 0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'], atnum, atnames, base) e, x = irc_ex[exi:exi + 2] g.addProp('x', float(x)) g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base, ezi = 0, 0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'], atnum, atnames, base) e, z = opt_ez[ezi:ezi + 2] g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'], atnum, atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch, data=charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF', 'MP2', 'CI', 'QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' % (k, float(e)) + web.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt)
def parse(self): """ Actual parsing happens here """ t_ifreq_done = False basis_FN = '' rc = self.rc s = 'BLANK' # It got to be initialized! try: while True: next(self.FI) s = self.FI.s.rstrip() # # Try to save some time by skipping parsing of large noninformative blocks of output # # Does not work for AM1 calcs """ # Skip parsing of SCF iterations if s.find(' Cycle')==0: while not s == '': s = next(self.FI).rstrip() """ # Skip parsing of distance matrices if s.find('Distance matrix (angstroms):') == 20: n = len(self.all_coords[coord_type]['all'][-1]) #print('n=',n) a1 = n % 5 an = n num = int((an - a1) / 5) + 1 n_lines_to_skip = num * (a1 + an) / 2 if a1 == 0: num -= 1 n_lines_to_skip += num * (1 + num) / 2 self.FI.skip_n(int(n_lines_to_skip)) s = self.FI.s.rstrip() # # ---------------------------------------- Read in cartesian coordinates ---------------------------------- # # Have we found coords? enter_coord = False if ' orientation:' in s: coord_type = s.split()[0] enter_coord = True if s.find(' Cartesian Coordinates (Ang):') == 0: coord_type = 'Cartesian Coordinates (Ang)' enter_coord = True # If yes, then read them if enter_coord: # Positioning dashes1 = next(self.FI) title1 = next(self.FI) title2 = next(self.FI) dashes2 = next(self.FI) s = next(self.FI) # Read in coordinates geom = Geom() atnames = [] while not '-------' in s: xyz = s.strip().split() try: ati, x, y, z = xyz[1], xyz[-3], xyz[-2], xyz[-1] except: log.warning('Error reading coordinates:\n%s' % (s)) break atn = ChemicalInfo.at_name[int(ati)] atnames.append(atn) geom.coord.append('%s %s %s %s' % (atn, x, y, z)) s = next(self.FI) # Add found coordinate to output pc = AtomicProps(attr='atnames', data=atnames) geom.addAtProp( pc, visible=False ) # We hide it, because there is no use to show atomic names for each geometry using checkboxes if not coord_type in self.all_coords: self.all_coords[coord_type] = { 'all': ListGeoms(), 'special': ListGeoms() } self.all_coords[coord_type]['all'].geoms.append(geom) # # ------------------------------------------- Route lines ------------------------------------------------- # if s.find(' #') == 0: # Read all route lines s2 = s while not '-----' in s2: self.route_lines += ' ' + s2[1:] s2 = next(self.FI).rstrip() self.route_lines = self.route_lines.lower() self.iop = rc['iop'].findall(self.route_lines) self.route_lines = re.sub( 'iop\(.*?\)', '', self.route_lines ) # Quick and dirty: get rid of slash symbols # Get Level of Theory # Look for standard notation: Method/Basis lot = rc['/'].search(self.route_lines) # print self.route_lines if lot: self.lot, self.basis = lot.group(1).split('/') if self.basis == 'gen' and basis_FN: # Read basis from external file self.basis = basis_FN else: # Look for method and basis separately using predefined lists of standard methods and bases lt = self.inroute(self.lot_nobasis, self.route_lines) if lt: self.lot = lt bs = self.inroute(self.def_basis, self.route_lines) if bs: self.basis = bs # Extract %HF in non-standard functionals for iop in self.iop: if '3/76' in iop: encrypted_hf = iop.split('=')[1] str_hf = encrypted_hf[-5:] num_hf = float(str_hf[:3] + '.' + str_hf[3:]) self.lot_suffix += '(%.2f %%HF)' % (num_hf) # Read solvent info if 'scrf' in self.route_lines: solvent = rc['scrf-solv'].search(self.route_lines) if solvent: self.solvent = solvent.group(1) # Get job type from the route line self.route_lines = re.sub( '\(.*?\)', '', self.route_lines ) # Quick and dirty: get rid of parentheses to get a string with only top level commands self.route_lines = re.sub( '=\S*', '', self.route_lines ) # Quick and dirty: get rid of =... to get a string with only top level commands jt = self.inroute(('opt', 'freq', 'irc'), self.route_lines) # Major job types if jt: self.JobType = jt #print('self.route_lines: ',self.route_lines) #print('jt',jt) self.JobType += self.inroute( ('td', 'nmr', 'stable'), self.route_lines, add=True) # Additional job types # Recognize job type on the fly if ' Berny optimization' in s and self.JobType == 'sp': self.JobType = 'opt' if rc['scan'].search(s): self.JobType = 'scan' # # ---------------------------------------- Read archive section ------------------------------------------- # if 'l9999.exe' in s and 'Enter' in s: while not '@' in self.l9999: s2 = next(self.FI).strip() if s2 == '': continue self.l9999 += s2 #print self.l9999 la = self.l9999.replace('\n ', '').split('\\') if len(la) > 5: self.machine_name = la[2] if la[5]: self.basis = la[5] #basis = la[5] #if basis == 'gen': #if basis_FN: #self.basis = ' Basis(?): ' + basis_FN #elif not self.basis: #self.basis = ' Basis: n/a' self.lot = la[4] self.JobType9999 = la[3] if self.JobType != self.JobType9999.lower(): self.JobType += "(%s)" % (self.JobType9999.lower()) # # ---------------------------------------- Read simple values --------------------------------------------- # #Nproc if s.find(' Will use up to') == 0: self.n_cores = s.split()[4] # time if s.find(' Job cpu time:') == 0: s_splitted = s.split() try: n_days = float(s_splitted[3]) n_hours = float(s_splitted[5]) n_mins = float(s_splitted[7]) n_sec = float(s_splitted[9]) self.time = n_days * 24 + n_hours + n_mins / 60 + n_sec / 3600 except: self.time = '***' # n_atoms if s.find('NAtoms=') == 1: s_splitted = s.split() self.n_atoms = int(s_splitted[1]) # n_basis if s.find('basis functions') == 7: s_splitted = s.split() self.n_primitives = int(s_splitted[3]) # Basis if s.find('Standard basis:') == 1: self.basis = s.strip().split(':')[1] # n_electrons if s.find('alpha electrons') == 7: s_splitted = s.split() n_alpha = s_splitted[0] n_beta = s_splitted[3] self.n_electrons = int(n_alpha) + int(n_beta) # S^2 if s.find(' S**2 before annihilation') == 0: s_splitted = s.split() before = s_splitted[3][:-1] after = s_splitted[5] self.s2 = before + '/' + after for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp('s2', self.s2) # CBS-QB3 if ' CBS-QB3 Enthalpy' in s: self.extra += s # Solvent if ' Solvent :' in s: self.solvent = s.split()[2][:-1] # Solvation model if not self.solv_model and 'Model :' in s: self.solv_model = s.strip().split()[2] # Try to guess basis name from the file name if not basis_FN: bas_FN = rc['basis-fn'].match(s) if bas_FN: basis_FN = re.sub('.*\/', '', bas_FN.group(1)) # Read Checkpoint file name if not self.chk: chk = rc['chk'].match(s) if chk: self.chk = chk.group(1) # Read Symmetry if ' Full point group' in s: self.sym = s.split()[3] # Read charge_multmetry if not self.charge: charge_mult = rc['charge-mult'].match(s) if charge_mult: self.charge = charge_mult.group(1) self.mult = charge_mult.group(2) # Collect WF convergence #scf_conv = rc['scf_conv'].match(s) #if not scf_conv: #scf_conv = rc['scf_iter'].match(s) #if scf_conv: #self.scf_conv.append(scf_conv.group(1)) # Read Converged HF/DFT Energy scf_e = rc['scf done'].match(s) if scf_e: if s[14] == 'U': self.openShell = True self.scf_e = float(scf_e.group(1)) self.scf_done = True for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp( 'e', self.scf_e ) # TODO Read in something like self.best_e instead! #CI/CC if not self.ci_cc_done: if ' CI/CC converged in' in s: self.ci_cc_done = True if ' Largest amplitude=' in s: self.amplitude = s.split()[2].replace('D', 'E') # CI/CC Convergence ci_cc_conv = rc['ci_cc_conv'].match(s) if ci_cc_conv: x = float(ci_cc_conv.group(1)) self.ci_cc_conv.append(x) """ Do we really need to parse post-hf energies? # Read post-HF energies if ' EUMP2 = ' in s: self.postHF_lot.append('MP2') self.postHF_e.append(s.split()[-1]) # QCISD(T) qcisd_t = rc['qcisd_t'].match(s) if qcisd_t: self.postHF_lot.append('QCISD(T)') self.postHF_e.append(qcisd_t.group(1)) """ """ #XXX Probably, we don't need it at all as more reliable topology can be read from NBO output # Read in internal coordinates topology if '! Name Definition Value Derivative Info. !' in s: dashes = next(self.FI) s = next(self.FI).strip() while not '----' in s: self.topology.append(s.split()[2]) s = next(self.FI).strip() """ # # ------------------------------------- NBO Topology ----------------------------------- # if 'N A T U R A L B O N D O R B I T A L A N A L Y S I S' in s: nbo_analysis = NBO() nbo_analysis.FI = self.FI nbo_analysis.parse() nbo_analysis.postprocess() self.topologies.append( nbo_analysis.topology ) # Actually, we save a reference, so we can keep using nbo_top for ct in self.all_coords.values(): if ct['all']: last_g = ct['all'][-1] last_g.nbo_analysis = nbo_analysis last_g.addAtProp(nbo_analysis.charges) if nbo_analysis.OpenShell: last_g.addAtProp(nbo_analysis.spins) # # ------------------------------------- NMR chemical shifts ----------------------------------- # if 'SCF GIAO Magnetic shielding tensor (ppm)' in s: nmr = AtomicProps(attr='nmr') s = next(self.FI) while 'Isotropic' in s: c = s.strip().split()[4] nmr.data.append(float(c)) next(self.FI) next(self.FI) next(self.FI) next(self.FI) s = next(self.FI) nmr_proton = AtomicProps(attr='nmr_proton') nmr_proton.data = copy.deepcopy(nmr.data) nmr_carbon = AtomicProps(attr='nmr_carbon') nmr_carbon.data = copy.deepcopy(nmr.data) for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addAtProp(nmr) ct['all'][-1].addAtProp(nmr_proton) ct['all'][-1].addAtProp(nmr_carbon) # # ------------------------------------- Charges ------------------------------------- # for ch in self.chash.keys(): if self.chash[ch]['Entry'] in s: pc = AtomicProps(attr=ch) next(self.FI) s = next(self.FI) while not self.chash[ch]['Stop'] in s: c = s.strip().split()[2] pc.data.append(float(c)) s = next(self.FI) for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addAtProp(pc) # # --------------------------------------------- Opt ------------------------------------------------------- # if 'opt' in self.JobType: if ' Item Value Threshold Converged?' in s: self.opt_iter += 1 for conv in ('max_force', 'rms_force', 'max_displacement', 'rms_displacement'): s = next(self.FI) x, thr = self.floatize(s[27:35]), float(s[40:48]) conv_param = getattr(self, conv) conv_param.append(x - thr) for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp(conv, x - thr) if ' -- Stationary point found.' in s: self.opt_ok = True # # --------------------------------------------- IRC ------------------------------------------------------- # if 'irc' in self.JobType: # IRC geometry was just collected? if 'Magnitude of analytic gradient =' in s: self.grad = float(s.split('=')[1]) if 'Rxn path following direction =' in s: if 'Forward' in s: self.irc_direction = 1 if 'Reverse' in s: self.irc_direction = -1 """ b_optd = ('Optimized point #' in s) and ('Found' in s) b_deltax = ' Delta-x Convergence Met' in s b_flag = 'Setting convergence flag and skipping corrector integration' in s t_irc_point = b_optd or b_deltax or b_flag """ """ G03: Order of IRC-related parameters: 1. Geometry, 2. Energy calculated for that geometry 3. Optimization convergence test G09: For IRC, there is a geometry entry right before the 'NET REACTION COORDINATE' string, and energy has not been attached to it yet, so we do it manually """ if 'NET REACTION COORDINATE UP TO THIS POINT =' in s: x = float(s.split('=')[1]) for ct in self.all_coords.values(): if ct['all']: girc = ct['all'][-1] girc.addProp('x', x * self.irc_direction) girc.addProp('e', self.scf_e) if '/' in str(self.s2): girc.addProp('s2', self.s2.split('/')[1].strip()) ct['special'].geoms.append(girc) if 'Minimum found on this side of the potential' in s\ or 'Begining calculation of the REVERSE path' in s: self.irc_direction *= -1 self.irc_both = True # # -------------------------------------------- Scan ------------------------------------------------------- # if 'scan' in self.JobType: """ Order of scan-related parameters: 1. Geometry, 2. Energy calculated for that geometry 3. Optimization convergence test If Stationary point has been found, we already have geometry with energy attached as prop, so we just pick it up """ # Memorize scan geometries if ' -- Stationary point found.' in s: for ct in self.all_coords.values(): if ct['all']: ct['special'].geoms.append(ct['all'][-1]) # Record scanned parameters for param in self.scan_param_description.values(): if ' ! ' in s and param in s: x = float(s.split()[3]) for ct in self.all_coords.values(): if ct['special']: ct['special'][-1].addProp(param, x) # Keep extended information about scanned parameter sc = rc['scan param'].match(s) if sc: param, param_full = sc.group(1), sc.group(2) self.scan_param_description[param] = param_full # # ------------------------------------- Scan or Opt: Frozen parameters ------------------------------------- # if 'scan' in self.JobType or 'opt' in self.JobType: sc = rc['frozen'].match(s) if sc: self.frozen[sc.group(1)] = sc.group(2) # # ------------------------------------------ Freqs -------------------------------------------------------- # if 'freq' in self.JobType or 'opt' in self.JobType: # T if ' Temperature ' in s: x = float(s.split()[1]) self.freq_temp.append(x) # ZPE, H, G if ' Sum of electronic and zero-point Energies=' in s: x = float(s.split()[-1]) self.freq_zpe.append(x) next(self.FI) # H Htherm = next(self.FI) x = float(Htherm.split('=')[1]) self.freq_ent.append(x) # G Gtherm = next(self.FI) x = float(Gtherm.split('=')[1]) self.freq_G.append(x) # Read in vibrational modes if 'Frequencies' in s: for fr in s.split(' '): if '.' in fr: self.freqs.append(float(fr)) # Read in imaginary frequencies if (not t_ifreq_done) \ and (self.freqs) \ and (self.freqs[0]<0) \ and not rc['alnum'].search(s): ifreq = rc['ifreq'].search(s) if ifreq: x, y, z = ifreq.groups() self.vector.append('%s %s %s' % (x, y, z)) else: t_ifreq_done = True # # --------------------------------------- TD -------------------------------------------------------------- # if 'td' in self.JobType: if 'Excitation energies and oscillator strengths' in s: self.uv = {} uv = rc['excited state'].match(s) if uv: self.n_states = uv.group(1) #print self.n_states l, f = float(uv.group(2)), float(uv.group(3)) self.uv[l] = f #self.uv[uv.group(1)] = uv.group(2) # # --------------------------------------- Stable -------------------------------------------------------------- # if 'stable' in self.JobType: if s.find(' The wavefunction has an' ) == 0 and 'instability' in s: self.extra += s # # ======================================= End of Gau Step ================================================== # if 'Normal termination of Gaussian' in s: self.OK = True break except StopIteration: log.error('Unexpected end of Gaussian file') # We got here either self.blank = (s == 'BLANK') return
class FchkGaussian(ElectronicStructure): """ Shows 3D-properties from the .fchk file """ def __init__(self): self.densities = [] self.openshell = False self.cubes = [] self.isotype='' self.isovalue='0.03' ElectronicStructure.__init__(self) self.OK = True def makeCube(self,prop,name='',colors=''): fcube = self.settings.real_path(prop + '.cube') wpcube = self.settings.web_path(prop + '.cube') command = (self.settings.cubegen, self.settings.nproc, prop, self.file, fcube, self.settings.npoints_cube, 'h') str_command = " ".join(map(str, command)) t1 = time.time() log.debug('Trying to run command: "%s"' % (str_command) ) subprocess.call(map(str,command)) t2 = time.time() log.debug('Running cubegen: %.1f s' % (t2-t1)) if os.path.exists(fcube): log.debug('%s successfully generated' % (fcube)) else: log.warning('%s has not been created' % (fcube)) c = Cube(name,colors) c.file = fcube c.wpcube = wpcube c.isotype = prop.split('=')[0] c.isovalue = self.isovalue c.parse() return c def parse(self): """ Here, .fchk will be parsed as a text file Probably, we start here, because .fchk contains valuable information which might be used """ try: FI = file2(self.file) except: log.error('Cannot open %s for reading' %(self.file)) """ http://www.gaussian.com/g_tech/g_ur/f_formchk.htm All other data contained in the file is located in a labeled line/section set up in one of the following forms: Scalar values appear on the same line as their data label. This line consists of a string describing the data item, a flag indicating the data type, and finally the value: Integer scalars: Name,I,IValue, using format A40,3X,A1,5X,I12. Real scalars: Name,R,Value, using format A40,3X,A1,5X,E22.15. Character string scalars: Name,C,Value, using format A40,3X,A1,5X,A12. Logical scalars: Name,L,Value, using format A40,3X,A1,5X,L1. Vector and array data sections begin with a line naming the data and giving the type and number of values, followed by the data on one or more succeeding lines (as needed): Integer arrays: Name,I,Num, using format A40,3X,A1,3X,'N=',I12. The N= indicates that this is an array, and the string is followed by the number of values. The array elements then follow starting on the next line in format 6I12. Real arrays: Name,R,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string again indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5E16.8. Note that the Real format has been chosen to ensure that at least one space is present between elements, to facilitate reading the data in C. Character string arrays (first type): Name,C,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5A12. Character string arrays (second type): Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 9A8. Logical arrays: Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 72L1. All quantities are in atomic units and in the standard orientation, if that was determined by the Gaussian run. Standard orientation is seldom an interesting visual perspective, but it is the natural orientation for the vector fields. """ def split_array(s,reclength): v = [] nrec = int(math.ceil((len(s)-1.0)/reclength)) for i in range(nrec): rec = s[reclength*i:reclength*(i+1)].strip() v.append(rec) return v self.parsedProps = {} format_arrays = { 'I' : [6.,12], 'R' : [5.,16], 'C' : [5.,12], 'H' : [9.,8], } try: self.comments = next(FI).rstrip() s = next(FI).rstrip() self.JobType, self.lot, self.basis = s[0:10],s[10:20],s[70:80] while True: s = next(FI) if FI.eof: break s = s.rstrip() array_mark = (s[47:49] == 'N=') if array_mark: value = [] prop, vtype, nrec = s[:40].strip(), s[43], int(s[49:]) fa = format_arrays[vtype] nlines = int(math.ceil(nrec/fa[0])) for _ in range(nlines): s = next(FI) v5 = split_array(s,fa[1]) value.extend(v5) else: prop, vtype, value = s[:40].strip(), s[43], s[49:].strip() self.parsedProps[prop] = value except StopIteration: log.warning('Unexpected EOF') FI.close() log.debug('%s parsed successfully' % (self.file)) return def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s)!=0: return True return False # def getGeom(ar,atnum,atnames,start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase+3] x, y, z = list(map(lambda k: float(k)*Bohr, xyz)) g.coord.append('%s %f %f %f' % (atn,x,y,z)) atbase += 3 pc = AtomicProps(attr='atnames',data=atnames) g.addAtProp(pc,visible=False) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # def getHOMOcharges(shell_to_atom_map,shell_types,n_el,mos,basis_size): # def rep(v1,v2): new = [] for i in range(0,len(v1)): for _ in range(v2[i]): new.append(v1[i]) return new # def dict_values_sorted_by_key(d): v = [] for k in sorted(d.keys()): v.append(d[k]) return v # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f shell_codes = {'0':1,'1':3,'-1':4,'2':6,'-2':5,'3':10,'-3':7,'4':14,'-4':9} # # get #primitives for each shell n_basis_func_per_shell = [shell_codes[k] for k in shell_types] #print('shell_to_atom_map',shell_to_atom_map) #print('Shell types',shell_types) #print('n_basis_func_per_shell',n_basis_func_per_shell) # # assign each primitive to atom index atom_map_HOMO = rep(shell_to_atom_map, n_basis_func_per_shell) #print('atom_map_HOMO',atom_map_HOMO) # if len(atom_map_HOMO) != basis_size: log.error('Size of H**O does not match number of primitives') # h**o = mos[(basis_size*(n_el-1)):(basis_size*n_el)] homo2 = [float(c)**2 for c in h**o] norm = sum(homo2) norm_homo2 = [c2/norm for c2 in homo2] #print(norm) #print('norm_homo2',norm_homo2) c2_per_atom = {} for i_atom,c2 in zip(atom_map_HOMO,norm_homo2): int_i_atom = int(i_atom) if int_i_atom in c2_per_atom.keys(): c2_per_atom[int_i_atom] += c2 else: c2_per_atom[int_i_atom] = c2 #print('c2_per_atom',c2_per_atom) sc2 = dict_values_sorted_by_key(c2_per_atom) #print('sc2',sc2) return sc2 # def old_getHOMOcharges(at_numbers,atnames,n_el,mos,basis_size): # def accumu(lis): total = 0 yield total for x in lis: total += x yield total # def homo_atom_contr(at_basis_cum,i): # atom numbering starts from 0 squares = [float(c)**2 for c in h**o[at_basis_cum[i]:at_basis_cum[i+1]]] return sum(squares) # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f n_basis_per_shell = {'0':1,'1':3,'-1':4,'2':6,'-2':5,'3':10,'-3':7} basis_funcs = {"6":15,"7":15,"8":15,"1":2,"35":30} # Specific for 6-31G(d) 6d! # at_basis = [basis_funcs[at_type] for at_type in at_numbers] at_basis_cum = list(accumu(at_basis)) # h**o = mos[(basis_size*(n_el-1)):(basis_size*n_el)] norm_homo = sum([float(c)**2 for c in h**o]) squares = [homo_atom_contr(at_basis_cum,i)/norm_homo for i in range(0,len(at_numbers))] return squares # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before,s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = list(map(lambda k: int(float(k)), pp['Nuclear charges'])) atnum = int(pp['Number of atoms']) at_numbers = pp["Atomic numbers"] n_el = int(pp["Number of alpha electrons"]) mos = pp["Alpha MO coefficients"] basis_size = len(pp["Alpha Orbital Energies"]) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ('Opt point 1 Geometries' in pp) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base,exi = 0,0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'],atnum,atnames,base) e,x = irc_ex[exi:exi+2] g.addProp('x',float(x)) g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base,ezi = 0,0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'],atnum,atnames,base) e,z = opt_ez[ezi:ezi+2] g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'],atnum,atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch,data = charges) g.addAtProp(pc) # Add H**O Charges homo_charges = getHOMOcharges(pp['Shell to atom map'],pp['Shell types'],n_el,mos,basis_size) pc = AtomicProps(attr='HOMO_charges',data = homo_charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF','MP2','CI','QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' % (k,float(e)) + Tools.HTML.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt) def generateAllCubes(self): # {A,B}MO=H**O LUMO ALL OccA OccB Valence Virtuals # Laplacian dprops = ['Density', 'Potential'] if self.openshell: dprops.append('Spin') props = ['AMO=H**O','BMO=H**O','AMO=LUMO','BMO=LUMO'] else: props = ['MO=H**O','MO=LUMO'] for d in self.densities: for p in dprops: prop = '%s=%s' % (p,d) c = self.makeCube(prop) self.cubes.append((c,prop)) for p in props: c = self.makeCube(p) self.cubes.append((c,p)) def webdata(self): we = self.settings.Engine3D() b1,b2 = ElectronicStructure.webdata(self) if self.settings.detailed_print: # Show all cubes self.generateAllCubes() s = '' for c,p in self.cubes: first_cube = c.wpcube ctype = p[:p.find('=')] if ctype == 'Density': continue elif ctype == 'Potential': first_cube = c.wpcube.replace('Potential','Density') second_cube = c.wpcube script = we.jmol_isosurface(webpath = first_cube, webpath_other = second_cube, surftype=ctype) else: script = c.s_script s += we.html_button(action=script, label=p) b2 += s elif self.isotype: # Show only requested cube p = self.isotype.lower() p_splitted = p.split('=') ctype = p_splitted[0] if len(p_splitted)>1: cvalue = p_splitted[1] if ctype == 'potential': p_pot = p p_dens = p.replace('potential','Density') c_pot = self.makeCube(p_pot) c_dens = self.makeCube(p_dens) first_cube = c_dens.wpcube second_cube = c_pot.wpcube script = we.jmol_isosurface(webpath = first_cube, webpath_other = second_cube, surftype=ctype) else: c = self.makeCube(p) script = c.s_script if ctype=='mo': if cvalue=='h**o': cvalue = self.parsedProps['Number of alpha electrons'] if cvalue=='lumo': cvalue = int(self.parsedProps['Number of alpha electrons'])+1 e_orb = float(self.parsedProps['Alpha Orbital Energies'][int(cvalue)-1])*27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype=='amo': e_orb = float(self.parsedProps['Alpha Orbital Energies'][int(cvalue)-1])*27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype=='bmo': e_orb = float(self.parsedProps['Beta Orbital Energies'][int(cvalue)-1])*27.211 b2 += 'E(BMO) = %.3f eV' % (e_orb) b2 += we.html_button(action=script, label=p) b2 += we.html_button('isosurface off', 'Off') return b1,b2
def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s)!=0: return True return False # def getGeom(ar,atnum,atnames,start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase+3] x, y, z = list(map(lambda k: float(k)*Bohr, xyz)) g.coord.append('%s %f %f %f' % (atn,x,y,z)) atbase += 3 pc = AtomicProps(attr='atnames',data=atnames) g.addAtProp(pc,visible=False) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # def getHOMOcharges(shell_to_atom_map,shell_types,n_el,mos,basis_size): # def rep(v1,v2): new = [] for i in range(0,len(v1)): for _ in range(v2[i]): new.append(v1[i]) return new # def dict_values_sorted_by_key(d): v = [] for k in sorted(d.keys()): v.append(d[k]) return v # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f shell_codes = {'0':1,'1':3,'-1':4,'2':6,'-2':5,'3':10,'-3':7,'4':14,'-4':9} # # get #primitives for each shell n_basis_func_per_shell = [shell_codes[k] for k in shell_types] #print('shell_to_atom_map',shell_to_atom_map) #print('Shell types',shell_types) #print('n_basis_func_per_shell',n_basis_func_per_shell) # # assign each primitive to atom index atom_map_HOMO = rep(shell_to_atom_map, n_basis_func_per_shell) #print('atom_map_HOMO',atom_map_HOMO) # if len(atom_map_HOMO) != basis_size: log.error('Size of H**O does not match number of primitives') # h**o = mos[(basis_size*(n_el-1)):(basis_size*n_el)] homo2 = [float(c)**2 for c in h**o] norm = sum(homo2) norm_homo2 = [c2/norm for c2 in homo2] #print(norm) #print('norm_homo2',norm_homo2) c2_per_atom = {} for i_atom,c2 in zip(atom_map_HOMO,norm_homo2): int_i_atom = int(i_atom) if int_i_atom in c2_per_atom.keys(): c2_per_atom[int_i_atom] += c2 else: c2_per_atom[int_i_atom] = c2 #print('c2_per_atom',c2_per_atom) sc2 = dict_values_sorted_by_key(c2_per_atom) #print('sc2',sc2) return sc2 # def old_getHOMOcharges(at_numbers,atnames,n_el,mos,basis_size): # def accumu(lis): total = 0 yield total for x in lis: total += x yield total # def homo_atom_contr(at_basis_cum,i): # atom numbering starts from 0 squares = [float(c)**2 for c in h**o[at_basis_cum[i]:at_basis_cum[i+1]]] return sum(squares) # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f n_basis_per_shell = {'0':1,'1':3,'-1':4,'2':6,'-2':5,'3':10,'-3':7} basis_funcs = {"6":15,"7":15,"8":15,"1":2,"35":30} # Specific for 6-31G(d) 6d! # at_basis = [basis_funcs[at_type] for at_type in at_numbers] at_basis_cum = list(accumu(at_basis)) # h**o = mos[(basis_size*(n_el-1)):(basis_size*n_el)] norm_homo = sum([float(c)**2 for c in h**o]) squares = [homo_atom_contr(at_basis_cum,i)/norm_homo for i in range(0,len(at_numbers))] return squares # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before,s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = list(map(lambda k: int(float(k)), pp['Nuclear charges'])) atnum = int(pp['Number of atoms']) at_numbers = pp["Atomic numbers"] n_el = int(pp["Number of alpha electrons"]) mos = pp["Alpha MO coefficients"] basis_size = len(pp["Alpha Orbital Energies"]) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ('Opt point 1 Geometries' in pp) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base,exi = 0,0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'],atnum,atnames,base) e,x = irc_ex[exi:exi+2] g.addProp('x',float(x)) g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base,ezi = 0,0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'],atnum,atnames,base) e,z = opt_ez[ezi:ezi+2] g.addProp('e',float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'],atnum,atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch,data = charges) g.addAtProp(pc) # Add H**O Charges homo_charges = getHOMOcharges(pp['Shell to atom map'],pp['Shell types'],n_el,mos,basis_size) pc = AtomicProps(attr='HOMO_charges',data = homo_charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF','MP2','CI','QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' % (k,float(e)) + Tools.HTML.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt)
def parse(self): """ Actual parsing happens here """ rc = { '/' : re.compile('(\S*\/\S+)'), 'iop' : re.compile('iop\((.*?)\)'), 'scrf-solv': re.compile('scrf.*solvent\s*=\s*(\w+)',re.IGNORECASE), 's2' : re.compile(' S\*\*2 before annihilation\s+(\S+),.*?\s+(\S+)$'), 'nbo-bond' : re.compile('\) BD \(.*\s+(\S+)\s*-\s*\S+\s+(\S+)'), 'basis-fn' : re.compile('^ AtFile\(1\):\s+(.*?).gbs'), 'chk' : re.compile('^ %%chk\s*=\s*(\S+)'), 'charge-mult' : re.compile('^ Charge =\s+(\S+)\s+Multiplicity =\s+(\S+)'), 'scf done' : re.compile('^ SCF Done.*?=\s+(\S+)'), 'qcisd_t' : re.compile('^ QCISD\(T\)=\s*(\S+)'), 'scf_conv' : re.compile('^ E=\s*(\S+)'), 'scf_iter' : re.compile('^ Iteration\s+\S+\s+EE=\s*(\S+)'), 'ci_cc_conv' : re.compile('^ DE\(Corr\)=\s*\S+\s*E\(CORR\)=\s*(\S+)'), 'xyz' : re.compile('^\s+\S+\s+(\S+).*\s+(\S+)\s+(\S+)\s+(\S+)\s*$'), 'scan param' : re.compile('^ !\s+(\S+)\s+(\S+)\s+(\S+)\s+Scan\s+!$'), 'frozen' : re.compile('^ !\s+(\S+)\s+(\S+)\s+\S+\s+frozen.*!$',re.IGNORECASE), 'alnum' : re.compile('[a-zA-Z]'), 'ifreq' : re.compile('\d+\s+\d+\s+(\S+)\s+(\S+)\s+(\S+)'), 'excited state' : re.compile('^ Excited State\s+(.*?):.*?\s+(\S+)\s*nm f=\s*(\S+)'), 'scan' : re.compile('Scan\s+!$') } self.chash = {} self.chash['NPA'] = {'Entry': 'XXX-XXX', 'Stop': 'XXX-XXX'} self.chash['NPA_spin'] = {'Entry': 'XXX-XXX', 'Stop': 'XXX-XXX'} self.chash['APT'] = {'Entry' : 'APT atomic charges:', 'Stop' : 'Sum of APT' } self.chash['Mulliken'] = {'Entry' : 'Mulliken atomic charges:', 'Stop' : 'Sum of Mulliken' } lot_nobasis = ( 'cbs-qb3','cbs-4m','cbs-apno', 'g1', 'g2', 'g2mp2', 'g3', 'g3mp2', 'g3b3', 'g3mp2b3', 'g4', 'g4mp2', 'g3mp2b3', 'w1u', 'w1bd', 'w1ro', 'b1b95', 'b1lyp', 'b3lyp', 'b3p86', 'b3pw91', 'b95', 'b971', 'b972', 'b97d', 'b98', 'bhandh', 'bhandhlyp', 'bmk', 'brc', 'brx', 'cam-b3lyp', 'g96', 'hcth', 'hcth147', 'hcth407', 'hcth93', 'hfb', 'hfs', 'hse2pbe', 'hseh1pbe', 'hsehpbe', 'kcis', 'lc-wpbe', 'lyp', 'm06', 'm062x', 'm06hf', 'm06l', 'o3lyp', 'p86', 'pbe', 'pbe', 'pbe1pbe', 'pbeh', 'pbeh1pbe', 'pkzb', 'pkzb', 'pw91', 'pw91', 'tpss', 'tpssh', 'v5lyp', 'vp86', 'vsxc', 'vwn', 'vwn5', 'x3lyp', 'xa', 'xalpha', 'mpw', 'mpw1lyp', 'mpw1pbe', 'mpw1pw91', 'mpw3pbe', 'thcth', 'thcthhyb', 'wb97', 'wb97x', 'wb97xd', 'wpbeh', 'mp2', 'mp3', 'mp4', 'mp5', 'b2plyp', 'mpw2plyp', 'ccd','ccsd','ccsd(t)','cid','cisd','qcisd(t)','sac-ci', 'am1','pm3','pm6','cndo','dftba','dftb','zindo','indo', 'amber','dreiding','uff', 'rhf','uhf','hf','casscf','gvb', ) def_basis = ( '3-21g', '6-21g', '4-31g', '6-31g', '6-311g', 'd95v', 'd95', 'shc', 'cep-4g', 'cep-31g', 'cep-121g', 'lanl2mb', 'lanl2dz', 'sdd', 'sddall', 'cc-pvdz', 'cc-pvtz', 'cc-pvqz', 'cc-pv5z', 'cc-pv6z', 'svp', 'sv', 'tzvp', 'tzv', 'qzvp', 'midix', 'epr-ii', 'epr-iii', 'ugbs', 'mtsmall', 'dgdzvp', 'dgdzvp2', 'dgtzvp', 'cbsb7', 'gen','chkbasis', ) self.irc_direction, self.irc_both = 1, False self.all_coords = {} t_ifreq_done = False basis_FN = '' # ------- Helper functions -------- def inroute(lst,s,add=False): result = '' for si in lst: for sj in s.split(): if si.lower()==sj.lower() or ('u'+si.lower())==sj.lower() or ('r'+si.lower())==sj.lower(): if add: result += ' '+si else: return si return result # def floatize(x): if '****' in x: return 10. return float(x) # //----- Helper functions -------- s = 'BLANC' # It got to be initialized! for s in self.FI: s = s.rstrip() # # Try to save some time by skipping parsing of large noninformative blocks of output # try: # Skip parsing of SCF iterations if s.find(' Cycle')==0: while not s == '': s = self.FI.next().rstrip() except: log.warning('Unexpected EOF in the SCF iterations') break try: # Skip parsing of distance matrices if s.find('Distance matrix (angstroms):')==20: n = len(self.all_coords[coord_type]['all'][-1]) m = int(math.ceil(n / 5.)) k = n % 5 n_lines_to_skip = m*(n + k + 2)/2 for i in range(n_lines_to_skip): s = self.FI.next() s = s.rstrip() except: log.warning('Unexpected EOF in the matrix of distances') break # # ---------------------------------------- Read in cartesian coordinates ---------------------------------- # # Have we found coords? enter_coord = False if ' orientation:' in s: coord_type = s.split()[0] enter_coord = True if s.find(' Cartesian Coordinates (Ang):')==0: coord_type = 'Cartesian Coordinates (Ang)' enter_coord = True # If yes, then read them if enter_coord: try: # Positioning dashes1 = self.FI.next() title1 = self.FI.next() title2 = self.FI.next() dashes2 = self.FI.next() s = self.FI.next() # Read in coordinates geom = Geom() atnames = [] while not '-------' in s: xyz = s.strip().split() try: ati, x,y,z = xyz[1], xyz[-3],xyz[-2],xyz[-1] except: log.warning('Error reading coordinates:\n%s' % (s)) break atn = ChemicalInfo.at_name[int(ati)] atnames.append(atn) geom.coord.append('%s %s %s %s' % (atn,x,y,z)) s = self.FI.next() # Add found coordinate to output pc = AtomicProps(attr='atnames',data=atnames) geom.addAtProp(pc,visible=False) # We hide it, because there is no use to show atomic names for each geometry using checkboxes if not coord_type in self.all_coords: self.all_coords[coord_type] = {'all':ListGeoms(),'special':ListGeoms()} self.all_coords[coord_type]['all'].geoms.append(geom) except StopIteration: log.warning('EOF while reading geometry') break # # ------------------------------------------- Route lines ------------------------------------------------- # if s.find(' #')==0: # Read all route lines s2 = s while not '-----' in s2: self.route_lines += s2[1:] try: s2 = self.FI.next().rstrip() except StopIteration: log.warning('EOF in the route section') break self.route_lines = self.route_lines.lower() self.iop = rc['iop'].findall(self.route_lines) self.route_lines = re.sub('iop\(.*?\)','',self.route_lines) # Quick and dirty: get rid of slash symbols # Get Level of Theory # Look for standard notation: Method/Basis lot = rc['/'].search(self.route_lines) # print self.route_lines if lot: self.lot, self.basis = lot.group(1).split('/') if self.basis == 'gen' and basis_FN: # Read basis from external file self.basis = basis_FN else: # Look for method and basis separately using predefined lists of standard methods and bases lt = inroute(lot_nobasis,self.route_lines) if lt: self.lot = lt bs = inroute(def_basis,self.route_lines) if bs: self.basis = bs # Extract %HF in non-standard functionals for iop in self.iop: if '3/76' in iop: encrypted_hf = iop.split('=')[1] str_hf = encrypted_hf[-5:] num_hf = float(str_hf[:3]+'.'+str_hf[3:]) self.lot_suffix += '(%.2f %%HF)' %(num_hf) # Read solvent info if 'scrf' in self.route_lines: solvent = rc['scrf-solv'].search(self.route_lines) if solvent: self.solvent = solvent.group(1) # Get job type from the route line self.route_lines = re.sub('\(.*?\)','',self.route_lines) # Quick and dirty: get rid of parentheses to get a string with only top level commands self.route_lines = re.sub('=\S*','',self.route_lines) # Quick and dirty: get rid of =... to get a string with only top level commands jt = inroute(('opt','freq','irc'),self.route_lines) # Major job types if jt: self.JobType = jt self.JobType += inroute(('td','nmr','stable'),self.route_lines,add=True) # Additional job types # Recognize job type on the fly if ' Berny optimization' in s and self.JobType=='sp': self.JobType = 'opt' if rc['scan'].search(s): self.JobType = 'scan' # # ---------------------------------------- Read archive section ------------------------------------------- # if 'l9999.exe' in s and 'Enter' in s: try: while not '@' in self.l9999: s2 = self.FI.next().strip() if s2=='': continue self.l9999 += s2 except StopIteration: log.warning('EOF while reading l9999') break #print self.l9999 la = self.l9999.replace('\n ','').split('\\') if len(la)>5: self.machine_name = la[2] if la[5]: self.basis = la[5] #basis = la[5] #if basis == 'gen': #if basis_FN: #self.basis = ' Basis(?): ' + basis_FN #elif not self.basis: #self.basis = ' Basis: n/a' self.lot = la[4] self.JobType9999 = la[3] if self.JobType != self.JobType9999.lower(): self.JobType += "(%s)" % (self.JobType9999.lower()) # # ---------------------------------------- Read simple values --------------------------------------------- # #Nproc if s.find(' Will use up to') == 0: self.n_cores = s.split()[4] # time if s.find(' Job cpu time:') == 0: s_splitted = s.split() try: n_days = float(s_splitted[3]) n_hours = float(s_splitted[5]) n_mins = float(s_splitted[7]) n_sec = float(s_splitted[9]) self.time = n_days*24 + n_hours + n_mins/60 + n_sec/3600 except: self.time = '***' # n_atoms if s.find('NAtoms=') == 1: s_splitted = s.split() self.n_atoms = int(s_splitted[1]) # n_basis if s.find('basis functions') == 7: s_splitted = s.split() self.n_primitives = int(s_splitted[3]) # Basis if s.find('Standard basis:') == 1: self.basis = s.strip().split(':')[1] # n_electrons if s.find('alpha electrons') == 7: s_splitted = s.split() n_alpha = s_splitted[0] n_beta = s_splitted[3] self.n_electrons = int(n_alpha) + int(n_beta) # S^2 if s.find(' S**2 before annihilation')==0: s_splitted = s.split() before = s_splitted[3][:-1] after = s_splitted[5] self.s2 = before + '/' + after for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp('s2',self.s2) # CBS-QB3 if ' CBS-QB3 Enthalpy' in s: self.extra += s # Solvent if ' Solvent :' in s: self.solvent = s.split()[2][:-1] # Solvation model if not self.solv_model and 'Model :' in s: self.solv_model = s.strip().split()[2] # Try to guess basis name from the file name if not basis_FN: bas_FN = rc['basis-fn'].match(s) if bas_FN: basis_FN = re.sub('.*\/','',bas_FN.group(1)) # Read Checkpoint file name if not self.chk: chk = rc['chk'].match(s) if chk: self.chk = chk.group(1) # Read Symmetry if ' Full point group' in s: self.sym = s.split()[3] # Read charge_multmetry if not self.charge: charge_mult = rc['charge-mult'].match(s) if charge_mult: self.charge = charge_mult.group(1) self.mult = charge_mult.group(2) # Collect WF convergence #scf_conv = rc['scf_conv'].match(s) #if not scf_conv: #scf_conv = rc['scf_iter'].match(s) #if scf_conv: #self.scf_conv.append(scf_conv.group(1)) # Read Converged HF/DFT Energy scf_e = rc['scf done'].match(s) if scf_e: if s[14]=='U': self.openShell = True self.scf_e = float(scf_e.group(1)) self.scf_done = True for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp('e', self.scf_e) # TODO Read in something like self.best_e instead! #CI/CC if not self.ci_cc_done: if ' CI/CC converged in' in s: self.ci_cc_done = True if ' Largest amplitude=' in s: self.amplitude = s.split()[2].replace('D','E') # CI/CC Convergence ci_cc_conv = rc['ci_cc_conv'].match(s) if ci_cc_conv: x = float(ci_cc_conv.group(1)) self.ci_cc_conv.append(x) """ Do we really need to parse post-hf energies? # Read post-HF energies if ' EUMP2 = ' in s: self.postHF_lot.append('MP2') self.postHF_e.append(s.split()[-1]) # QCISD(T) qcisd_t = rc['qcisd_t'].match(s) if qcisd_t: self.postHF_lot.append('QCISD(T)') self.postHF_e.append(qcisd_t.group(1)) """ """ #XXX Probably, we don't need it at all as more reliable topology can be read from NBO output # Read in internal coordinates topology if '! Name Definition Value Derivative Info. !' in s: dashes = self.FI.next() s = self.FI.next().strip() while not '----' in s: self.topology.append(s.split()[2]) s = self.FI.next().strip() """ # # ------------------------------------- NBO Topology ----------------------------------- # if 'N A T U R A L B O N D O R B I T A L A N A L Y S I S' in s: nbo_analysis = NBO() nbo_analysis.FI = self.FI nbo_analysis.parse() nbo_analysis.postprocess() self.topologies.append(nbo_analysis.topology) # Actually, we save a reference, so we can keep using nbo_top for ct in self.all_coords.values(): if ct['all']: last_g = ct['all'][-1] last_g.nbo_analysis = nbo_analysis last_g.addAtProp(nbo_analysis.charges) if nbo_analysis.OpenShell: last_g.addAtProp(nbo_analysis.spins) # # ------------------------------------- Charges ------------------------------------- # try: for ch in self.chash.keys(): if self.chash[ch]['Entry'] in s: pc = AtomicProps(attr=ch) self.FI.next() s = self.FI.next() while not self.chash[ch]['Stop'] in s: c = s.strip().split()[2] pc.data.append(float(c)) s = self.FI.next() for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addAtProp(pc) except StopIteration: log.warning('EOF while reading charges') break # # --------------------------------------------- Opt ------------------------------------------------------- # if 'opt' in self.JobType: if ' Item Value Threshold Converged?' in s: self.opt_iter += 1 try: for conv in ('max_force','rms_force','max_displacement','rms_displacement'): s = self.FI.next() x, thr = floatize(s[27:35]), float(s[40:48]) conv_param = getattr(self,conv) conv_param.append(x-thr) for ct in self.all_coords.values(): if ct['all']: ct['all'][-1].addProp(conv, x-thr) except: log.warngin('EOF in the "Converged?" block') break if ' -- Stationary point found.' in s: self.opt_ok = True # # --------------------------------------------- IRC ------------------------------------------------------- # if 'irc' in self.JobType: # IRC geometry was just collected? if 'Magnitude of analytic gradient =' in s: self.grad = float(s.split('=')[1]) if 'Rxn path following direction =' in s: if 'Forward' in s: self.irc_direction = 1 if 'Reverse' in s: self.irc_direction = -1 """ b_optd = ('Optimized point #' in s) and ('Found' in s) b_deltax = ' Delta-x Convergence Met' in s b_flag = 'Setting convergence flag and skipping corrector integration' in s t_irc_point = b_optd or b_deltax or b_flag """ """ G03: Order of IRC-related parameters: 1. Geometry, 2. Energy calculated for that geometry 3. Optimization convergence test G09: For IRC, there is a geometry entry right before the 'NET REACTION COORDINATE' string, and energy has not been attached to it yet, so we do it manually """ if 'NET REACTION COORDINATE UP TO THIS POINT =' in s: x = float(s.split('=')[1]) for ct in self.all_coords.values(): if ct['all']: girc = ct['all'][-1] girc.addProp('x', x*self.irc_direction) girc.addProp('e', self.scf_e) if '/' in str(self.s2): girc.addProp('s2', self.s2.split('/')[1].strip()) ct['special'].geoms.append(girc) if 'Minimum found on this side of the potential' in s\ or 'Beginning calculation of the REVERSE path' in s: self.irc_direction *= -1 self.irc_both = True # # -------------------------------------------- Scan ------------------------------------------------------- # if 'scan' in self.JobType: """ Order of scan-related parameters: 1. Geometry, 2. Energy calculated for that geometry 3. Optimization convergence test If Stationary point has been found, we already have geometry with energy attached as prop, so we just pick it up """ # Memorize scan geometries if ' -- Stationary point found.' in s: for ct in self.all_coords.values(): if ct['all']: ct['special'].geoms.append(ct['all'][-1]) # Record scanned parameters for param in self.scan_param_description.values(): if ' ! ' in s and param in s: x = float(s.split()[3]) for ct in self.all_coords.values(): if ct['special']: ct['special'][-1].addProp(param,x) # Keep extended information about scanned parameter sc = rc['scan param'].match(s) if sc: param, param_full = sc.group(1), sc.group(2) self.scan_param_description[param] = param_full # # ------------------------------------- Scan or Opt: Frozen parameters ------------------------------------- # if 'scan' in self.JobType or 'opt' in self.JobType: sc = rc['frozen'].match(s) if sc: self.frozen[sc.group(1)] = sc.group(2) # # ------------------------------------------ Freqs -------------------------------------------------------- # if 'freq' in self.JobType or 'opt' in self.JobType: # T if ' Temperature ' in s: x = float(s.split()[1]) self.freq_temp.append(x) # ZPE, H, G if ' Sum of electronic and zero-point Energies=' in s: try: x = float(s.split()[-1]) self.freq_zpe.append(x) self.FI.next() # H Htherm = self.FI.next() x = float(Htherm.split('=')[1]) self.freq_ent.append(x) # G Gtherm = self.FI.next() x = float(Gtherm.split('=')[1]) self.freq_G.append(x) except: log.warngin('EOF in the Thermochemistry block') break # Read in vibrational modes if 'Frequencies' in s: for fr in s.split(' '): if '.' in fr: self.freqs.append(float(fr)) # Read in imaginary frequencies if (not t_ifreq_done) \ and (self.freqs) \ and (self.freqs[0]<0) \ and not rc['alnum'].search(s): ifreq = rc['ifreq'].search(s) if ifreq: x, y, z = ifreq.groups() self.vector.append('%s %s %s' % (x,y,z)) else: t_ifreq_done = True # # --------------------------------------- TD -------------------------------------------------------------- # if 'td' in self.JobType: uv = rc['excited state'].match(s) if uv: self.n_states = uv.group(1) #print self.n_states l,f = float(uv.group(2)),float(uv.group(3)) self.uv[l] = f #self.uv[uv.group(1)] = uv.group(2) # # --------------------------------------- Stable -------------------------------------------------------------- # if 'stable' in self.JobType: if s.find(' The wavefunction has an')==0 and 'instability' in s: self.extra += s # # ======================================= End of Gau Step ================================================== # if 'Normal termination of Gaussian' in s: self.OK = True break # We got here either else: self.blanc = (s=='BLANC') return
class FchkGaussian(ElectronicStructure): """ Shows 3D-properties from the .fchk file """ def __init__(self): self.densities = [] self.openshell = False self.cubes = [] self.isotype = '' self.isovalue = '0.03' ElectronicStructure.__init__(self) self.OK = True def makeCube(self, prop, name='', colors=''): fcube = self.settings.real_path(prop + '.cube') wpcube = self.settings.web_path(prop + '.cube') command = (self.settings.cubegen, self.settings.nproc, prop, self.file, fcube, self.settings.npoints_cube, 'h') str_command = " ".join(map(str, command)) t1 = time.time() log.debug('Trying to run command: "%s"' % (str_command)) subprocess.call(map(str, command)) t2 = time.time() log.debug('Running cubegen: %.1f s' % (t2 - t1)) if os.path.exists(fcube): log.debug('%s successfully generated' % (fcube)) else: log.warning('%s has not been created' % (fcube)) c = Cube(name, colors) c.file = fcube c.wpcube = wpcube c.isotype = prop.split('=')[0] c.isovalue = self.isovalue c.parse() return c def parse(self): """ Here, .fchk will be parsed as a text file Probably, we start here, because .fchk contains valuable information which might be used """ try: FI = file2(self.file) except: log.error('Cannot open %s for reading' % (self.file)) """ http://www.gaussian.com/g_tech/g_ur/f_formchk.htm All other data contained in the file is located in a labeled line/section set up in one of the following forms: Scalar values appear on the same line as their data label. This line consists of a string describing the data item, a flag indicating the data type, and finally the value: Integer scalars: Name,I,IValue, using format A40,3X,A1,5X,I12. Real scalars: Name,R,Value, using format A40,3X,A1,5X,E22.15. Character string scalars: Name,C,Value, using format A40,3X,A1,5X,A12. Logical scalars: Name,L,Value, using format A40,3X,A1,5X,L1. Vector and array data sections begin with a line naming the data and giving the type and number of values, followed by the data on one or more succeeding lines (as needed): Integer arrays: Name,I,Num, using format A40,3X,A1,3X,'N=',I12. The N= indicates that this is an array, and the string is followed by the number of values. The array elements then follow starting on the next line in format 6I12. Real arrays: Name,R,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string again indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5E16.8. Note that the Real format has been chosen to ensure that at least one space is present between elements, to facilitate reading the data in C. Character string arrays (first type): Name,C,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 5A12. Character string arrays (second type): Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 9A8. Logical arrays: Name,H,Num, using format A40,3X,A1,3X,'N=',I12, where the N= string indicates an array and is followed by the number of elements. The elements themselves follow on succeeding lines in format 72L1. All quantities are in atomic units and in the standard orientation, if that was determined by the Gaussian run. Standard orientation is seldom an interesting visual perspective, but it is the natural orientation for the vector fields. """ def split_array(s, reclength): v = [] nrec = int(math.ceil((len(s) - 1.0) / reclength)) for i in range(nrec): rec = s[reclength * i:reclength * (i + 1)].strip() v.append(rec) return v self.parsedProps = {} format_arrays = { 'I': [6., 12], 'R': [5., 16], 'C': [5., 12], 'H': [9., 8], } try: self.comments = next(FI).rstrip() s = next(FI).rstrip() self.JobType, self.lot, self.basis = s[0:10], s[10:20], s[70:80] while True: s = next(FI) if FI.eof: break s = s.rstrip() array_mark = (s[47:49] == 'N=') if array_mark: value = [] prop, vtype, nrec = s[:40].strip(), s[43], int(s[49:]) fa = format_arrays[vtype] nlines = int(math.ceil(nrec / fa[0])) for _ in range(nlines): s = next(FI) v5 = split_array(s, fa[1]) value.extend(v5) else: prop, vtype, value = s[:40].strip(), s[43], s[49:].strip() self.parsedProps[prop] = value except StopIteration: log.warning('Unexpected EOF') FI.close() log.debug('%s parsed successfully' % (self.file)) return def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s) != 0: return True return False # def getGeom(ar, atnum, atnames, start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase + 3] x, y, z = list(map(lambda k: float(k) * Bohr, xyz)) g.coord.append('%s %f %f %f' % (atn, x, y, z)) atbase += 3 pc = AtomicProps(attr='atnames', data=atnames) g.addAtProp( pc, visible=False ) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # def getHOMOcharges(shell_to_atom_map, shell_types, n_el, mos, basis_size): # def rep(v1, v2): new = [] for i in range(0, len(v1)): for _ in range(v2[i]): new.append(v1[i]) return new # def dict_values_sorted_by_key(d): v = [] for k in sorted(d.keys()): v.append(d[k]) return v # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f shell_codes = { '0': 1, '1': 3, '-1': 4, '2': 6, '-2': 5, '3': 10, '-3': 7, '4': 14, '-4': 9 } # # get #primitives for each shell n_basis_func_per_shell = [shell_codes[k] for k in shell_types] #print('shell_to_atom_map',shell_to_atom_map) #print('Shell types',shell_types) #print('n_basis_func_per_shell',n_basis_func_per_shell) # # assign each primitive to atom index atom_map_HOMO = rep(shell_to_atom_map, n_basis_func_per_shell) #print('atom_map_HOMO',atom_map_HOMO) # if len(atom_map_HOMO) != basis_size: log.error('Size of H**O does not match number of primitives') # h**o = mos[(basis_size * (n_el - 1)):(basis_size * n_el)] homo2 = [float(c)**2 for c in h**o] norm = sum(homo2) norm_homo2 = [c2 / norm for c2 in homo2] #print(norm) #print('norm_homo2',norm_homo2) c2_per_atom = {} for i_atom, c2 in zip(atom_map_HOMO, norm_homo2): int_i_atom = int(i_atom) if int_i_atom in c2_per_atom.keys(): c2_per_atom[int_i_atom] += c2 else: c2_per_atom[int_i_atom] = c2 #print('c2_per_atom',c2_per_atom) sc2 = dict_values_sorted_by_key(c2_per_atom) #print('sc2',sc2) return sc2 # def old_getHOMOcharges(at_numbers, atnames, n_el, mos, basis_size): # def accumu(lis): total = 0 yield total for x in lis: total += x yield total # def homo_atom_contr(at_basis_cum, i): # atom numbering starts from 0 squares = [ float(c)**2 for c in h**o[at_basis_cum[i]:at_basis_cum[i + 1]] ] return sum(squares) # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f n_basis_per_shell = { '0': 1, '1': 3, '-1': 4, '2': 6, '-2': 5, '3': 10, '-3': 7 } basis_funcs = { "6": 15, "7": 15, "8": 15, "1": 2, "35": 30 } # Specific for 6-31G(d) 6d! # at_basis = [basis_funcs[at_type] for at_type in at_numbers] at_basis_cum = list(accumu(at_basis)) # h**o = mos[(basis_size * (n_el - 1)):(basis_size * n_el)] norm_homo = sum([float(c)**2 for c in h**o]) squares = [ homo_atom_contr(at_basis_cum, i) / norm_homo for i in range(0, len(at_numbers)) ] return squares # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before, s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = list(map(lambda k: int(float(k)), pp['Nuclear charges'])) atnum = int(pp['Number of atoms']) at_numbers = pp["Atomic numbers"] n_el = int(pp["Number of alpha electrons"]) mos = pp["Alpha MO coefficients"] basis_size = len(pp["Alpha Orbital Energies"]) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ( 'Opt point 1 Geometries' in pp ) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base, exi = 0, 0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'], atnum, atnames, base) e, x = irc_ex[exi:exi + 2] g.addProp('x', float(x)) g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base, ezi = 0, 0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'], atnum, atnames, base) e, z = opt_ez[ezi:ezi + 2] g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'], atnum, atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch, data=charges) g.addAtProp(pc) # Add H**O Charges homo_charges = getHOMOcharges(pp['Shell to atom map'], pp['Shell types'], n_el, mos, basis_size) pc = AtomicProps(attr='HOMO_charges', data=homo_charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF', 'MP2', 'CI', 'QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' % (k, float(e)) + Tools.HTML.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt) def generateAllCubes(self): # {A,B}MO=H**O LUMO ALL OccA OccB Valence Virtuals # Laplacian dprops = ['Density', 'Potential'] if self.openshell: dprops.append('Spin') props = ['AMO=H**O', 'BMO=H**O', 'AMO=LUMO', 'BMO=LUMO'] else: props = ['MO=H**O', 'MO=LUMO'] for d in self.densities: for p in dprops: prop = '%s=%s' % (p, d) c = self.makeCube(prop) self.cubes.append((c, prop)) for p in props: c = self.makeCube(p) self.cubes.append((c, p)) def webdata(self): we = self.settings.Engine3D() b1, b2 = ElectronicStructure.webdata(self) if self.settings.detailed_print: # Show all cubes self.generateAllCubes() s = '' for c, p in self.cubes: first_cube = c.wpcube ctype = p[:p.find('=')] if ctype == 'Density': continue elif ctype == 'Potential': first_cube = c.wpcube.replace('Potential', 'Density') second_cube = c.wpcube script = we.jmol_isosurface(webpath=first_cube, webpath_other=second_cube, surftype=ctype) else: script = c.s_script s += we.html_button(action=script, label=p) b2 += s elif self.isotype: # Show only requested cube p = self.isotype.lower() p_splitted = p.split('=') ctype = p_splitted[0] if len(p_splitted) > 1: cvalue = p_splitted[1] if ctype == 'potential': p_pot = p p_dens = p.replace('potential', 'Density') c_pot = self.makeCube(p_pot) c_dens = self.makeCube(p_dens) first_cube = c_dens.wpcube second_cube = c_pot.wpcube script = we.jmol_isosurface(webpath=first_cube, webpath_other=second_cube, surftype=ctype) else: c = self.makeCube(p) script = c.s_script if ctype == 'mo': if cvalue == 'h**o': cvalue = self.parsedProps['Number of alpha electrons'] if cvalue == 'lumo': cvalue = int( self.parsedProps['Number of alpha electrons']) + 1 e_orb = float(self.parsedProps['Alpha Orbital Energies'][ int(cvalue) - 1]) * 27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype == 'amo': e_orb = float(self.parsedProps['Alpha Orbital Energies'][ int(cvalue) - 1]) * 27.211 b2 += 'E(AMO) = %.3f eV' % (e_orb) if ctype == 'bmo': e_orb = float( self.parsedProps['Beta Orbital Energies'][int(cvalue) - 1]) * 27.211 b2 += 'E(BMO) = %.3f eV' % (e_orb) b2 += we.html_button(action=script, label=p) b2 += we.html_button('isosurface off', 'Off') return b1, b2
def postprocess(self): # def any_nonzero(ar): for s in ar: if float(s) != 0: return True return False # def getGeom(ar, atnum, atnames, start=0): Bohr = 0.52917721 g = Geom() atbase = start for i in range(atnum): atn = atnames[i] xyz = ar[atbase:atbase + 3] x, y, z = list(map(lambda k: float(k) * Bohr, xyz)) g.coord.append('%s %f %f %f' % (atn, x, y, z)) atbase += 3 pc = AtomicProps(attr='atnames', data=atnames) g.addAtProp( pc, visible=False ) # We hide it, because there is no use to show atomic names for each geometry using checkboxes return g # def getHOMOcharges(shell_to_atom_map, shell_types, n_el, mos, basis_size): # def rep(v1, v2): new = [] for i in range(0, len(v1)): for _ in range(v2[i]): new.append(v1[i]) return new # def dict_values_sorted_by_key(d): v = [] for k in sorted(d.keys()): v.append(d[k]) return v # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f shell_codes = { '0': 1, '1': 3, '-1': 4, '2': 6, '-2': 5, '3': 10, '-3': 7, '4': 14, '-4': 9 } # # get #primitives for each shell n_basis_func_per_shell = [shell_codes[k] for k in shell_types] #print('shell_to_atom_map',shell_to_atom_map) #print('Shell types',shell_types) #print('n_basis_func_per_shell',n_basis_func_per_shell) # # assign each primitive to atom index atom_map_HOMO = rep(shell_to_atom_map, n_basis_func_per_shell) #print('atom_map_HOMO',atom_map_HOMO) # if len(atom_map_HOMO) != basis_size: log.error('Size of H**O does not match number of primitives') # h**o = mos[(basis_size * (n_el - 1)):(basis_size * n_el)] homo2 = [float(c)**2 for c in h**o] norm = sum(homo2) norm_homo2 = [c2 / norm for c2 in homo2] #print(norm) #print('norm_homo2',norm_homo2) c2_per_atom = {} for i_atom, c2 in zip(atom_map_HOMO, norm_homo2): int_i_atom = int(i_atom) if int_i_atom in c2_per_atom.keys(): c2_per_atom[int_i_atom] += c2 else: c2_per_atom[int_i_atom] = c2 #print('c2_per_atom',c2_per_atom) sc2 = dict_values_sorted_by_key(c2_per_atom) #print('sc2',sc2) return sc2 # def old_getHOMOcharges(at_numbers, atnames, n_el, mos, basis_size): # def accumu(lis): total = 0 yield total for x in lis: total += x yield total # def homo_atom_contr(at_basis_cum, i): # atom numbering starts from 0 squares = [ float(c)**2 for c in h**o[at_basis_cum[i]:at_basis_cum[i + 1]] ] return sum(squares) # # Shell types (NShell values): 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f n_basis_per_shell = { '0': 1, '1': 3, '-1': 4, '2': 6, '-2': 5, '3': 10, '-3': 7 } basis_funcs = { "6": 15, "7": 15, "8": 15, "1": 2, "35": 30 } # Specific for 6-31G(d) 6d! # at_basis = [basis_funcs[at_type] for at_type in at_numbers] at_basis_cum = list(accumu(at_basis)) # h**o = mos[(basis_size * (n_el - 1)):(basis_size * n_el)] norm_homo = sum([float(c)**2 for c in h**o]) squares = [ homo_atom_contr(at_basis_cum, i) / norm_homo for i in range(0, len(at_numbers)) ] return squares # pp = self.parsedProps self.charge = pp['Charge'] self.mult = pp['Multiplicity'] self.sym = 'NA' self.solvent = 'NA' if 'S**2' in pp: s2_before = float(pp['S**2']) s2_after = float(pp['S**2 after annihilation']) if s2_before > 0.0: self.openshell = True self.s2 = '%.4f / %.4f' % (s2_before, s2_after) if any_nonzero(pp['External E-field']): self.extra += 'External Electric Field applied' self.scf_e = float(pp['SCF Energy']) self.total_e = pp['Total Energy'] atnames = list(map(lambda k: int(float(k)), pp['Nuclear charges'])) atnum = int(pp['Number of atoms']) at_numbers = pp["Atomic numbers"] n_el = int(pp["Number of alpha electrons"]) mos = pp["Alpha MO coefficients"] basis_size = len(pp["Alpha Orbital Energies"]) self.geoms = ListGeoms() is_irc = ('IRC point 1 Geometries' in pp) is_opt = ( 'Opt point 1 Geometries' in pp ) & False # It might be rather confusing than useful thing, so I'll turn it off for a while if is_irc: self.JobType += ' (irc)' ngeom = int(pp['IRC Number of geometries'][0]) shift = int(pp['IRC Num geometry variables']) irc_ex = pp['IRC point 1 Results for each geome'] base, exi = 0, 0 for i in range(ngeom): g = getGeom(pp['IRC point 1 Geometries'], atnum, atnames, base) e, x = irc_ex[exi:exi + 2] g.addProp('x', float(x)) g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift exi += 2 self.series = IRC(other=self.geoms) elif is_opt: ngeom = int(pp['Optimization Number of geometries'][0]) shift = int(pp['Optimization Num geometry variables']) opt_ez = pp['Opt point 1 Results for each geome'] base, ezi = 0, 0 for i in range(ngeom): g = getGeom(pp['Opt point 1 Geometries'], atnum, atnames, base) e, z = opt_ez[ezi:ezi + 2] g.addProp('e', float(e)) g.to_kcalmol = 627.509 self.geoms.append(g) base += shift ezi += 2 else: g = getGeom(pp['Current cartesian coordinates'], atnum, atnames) # Parse charges for k in pp: if ' Charges' in k: ch = k[:k.find(' ')] charges = pp[k] if any_nonzero(charges): pc = AtomicProps(attr=ch, data=charges) g.addAtProp(pc) # Add H**O Charges homo_charges = getHOMOcharges(pp['Shell to atom map'], pp['Shell types'], n_el, mos, basis_size) pc = AtomicProps(attr='HOMO_charges', data=homo_charges) g.addAtProp(pc) # Record geometry self.geoms.append(g) d_types = ['SCF', 'MP2', 'CI', 'QCI'] for k in pp: # Energies if ' Energy' in k: et = k[:k.find(' ')] e = pp[k] if et == 'SCF': continue self.extra += '%s: %.8f' % (k, float(e)) + Tools.HTML.brn # Densities for dt in d_types: if ('Total %s Density' % dt) in k: self.densities.append(dt)
def __init__(self, other=None): #IRC specific lines self.direction = 1 self.both = False ListGeoms.__init__(self, other)
def __init__(self,other=None): #IRC specific lines self.direction = 1 self.both = False ListGeoms.__init__(self,other)