def build_ToRelax(self, enumDicts, species, AFM=False, start=1, end=None): from aBuild.enumeration import Enumerate from aBuild.utility import unpackProtos, getAllPerms, getProtoPaths from aBuild.database.crystal import Crystal from os import remove, path print('Building to-relax.cfg') print(enumDicts) nEnums = len(enumDicts) knary = len(species) for ilat in range(nEnums): lat = enumDicts[ilat]["name"] if lat == 'protos': structures = getProtoPaths(knary) for struct in structures: print("Proto structure:", struct) scrambleOrder = getAllPerms(knary, justCyclic='uniqueUnaries' in struct) for scramble in scrambleOrder: thisCrystal = Crystal(struct, species) #print("Atom counts before scramble {}".format(thisCrystal.atom_counts)) thisCrystal.scrambleAtoms(scramble) if not thisCrystal.concsOK( concRestrictions=enumDicts[ilat]["concs"]): continue print(thisCrystal.title) mindist = thisCrystal.minDist print( mindist, "actual min dist<-----------------------------------------------------------------------------------------------------" ) if mindist > 2 and thisCrystal.nAtoms < 60: if not AFM: print('Adding to file') #print("Atom counts after scramble {}".format(thisCrystal.atom_counts)) with open(path.join(self.root, 'to-relax.cfg'), 'a+') as f: f.writelines('\n'.join( thisCrystal.lines('mtprelax'))) elif thisCrystal.getAFMPlanes([1, 0, 0]): print("Original Crystal is AFM compatible") with open( path.join(self.root, 'to-relax.cfg_' + str(start)), 'a+') as f: f.writelines('\n'.join( thisCrystal.lines('mtprelax'))) # break else: print("Checking super-periodics") superCrystal = thisCrystal.superPeriodics(2) if superCrystal != []: print('Found a super-Periodic that works') print(superCrystal.basis, 'basis') print(superCrystal.atom_counts, 'atom counts') with open( path.join( self.root, 'to-relax.cfg_' + str(start)), 'a+') as f: f.writelines('\n'.join( superCrystal.lines('mtprelax'))) # break else: enumLattice = Enumerate(enumDicts[ilat]) print(end, 'end') if end == None: end = enumLattice.nConfigs + 1 filetag = '' else: filetag = '_' + str(start) #for struct in range(enumLattice.nConfigs+1): for struct in range(start, end): print("Lattice", lat, "structure:", struct) enumLattice.generatePOSCAR(struct) thisCrystal = Crystal( path.join(enumLattice.root, "poscar.{}.{}".format(lat, struct)), species) if not AFM: print('Writing to file!!!!!', end) with open( path.join(self.root, 'to_relax.cfg' + filetag), 'a+') as f: f.writelines('\n'.join( thisCrystal.lines('mtprelax'))) elif thisCrystal.getAFMPlanes([1, 0, 0]): print("Original Crystal is AFM compatible") with open( path.join(self.root, 'to_relax.cfg' + filetag), 'a+') as f: f.writelines('\n'.join( thisCrystal.lines('mtprelax'))) else: print("Checking super-periodics") superCrystal = thisCrystal.superPeriodics(2) if superCrystal != []: print('Found a super-Periodic that works') with open( path.join(self.root, 'to_relax.cfg' + filetag), 'a+') as f: f.writelines('\n'.join( superCrystal.lines('mtprelax'))) # print(thisCrystal.appMinDist,' approp Min Dist') # print(thisCrystal.minDist, 'actual min dist') # with open(path.join(self.root,'to-relax.cfg'),'a+') as f: # f.writelines('\n'.join(thisCrystal.lines('mtprelax') )) delpath = path.join(enumLattice.root, "poscar.{}.{}".format(lat, struct)) remove(delpath) end = None
#print("Atom counts before scramble {}".format(thisCrystal.atom_counts)) thisCrystal.scrambleAtoms(scramble) if not thisCrystal.concsOK(concRestrictions = enumDicts[ilat]["concs"]): continue print(thisCrystal.title) mindist = thisCrystal.minDist print(mindist, "actual min dist<-----------------------------------------------------------------------------------------------------") <<<<<<< HEAD if mindist > 2 and thisCrystal.nAtoms < 60: if not AFM: print('Adding to file') #print("Atom counts after scramble {}".format(thisCrystal.atom_counts)) with open(path.join(self.root,'to-relax.cfg'),'a+') as f: f.writelines('\n'.join(thisCrystal.lines('mtprelax') ) ) elif thisCrystal.getAFMPlanes([1,0,0]): print("Original Crystal is AFM compatible") with open(path.join(self.root,'to-relax.cfg_' + str(start)),'a+') as f: f.writelines('\n'.join(thisCrystal.lines('mtprelax') )) # break else: print("Checking super-periodics") superCrystal = thisCrystal.superPeriodics(2) if superCrystal != []: print('Found a super-Periodic that works') print(superCrystal.basis,'basis') print(superCrystal.atom_counts, 'atom counts') with open(path.join(self.root,'to-relax.cfg_' + str(start)),'a+') as f: f.writelines('\n'.join(superCrystal.lines('mtprelax') )) # break
def init_enum(self, enumdicts, systemSpecies, runGetKpoints=True): from aBuild.enumeration import Enumerate from aBuild.calculators.vasp import VASP from aBuild.database.crystal import Crystal from aBuild.jobs import Job from random import randrange from aBuild.utility import chdir from numpy import array from os import remove, path # from crystal import Crystal from os import path import os # if not path.isdir(self.root): # os.mkdir(self.root) print("Building database from enumerations") self.crystals = [] # configIndex = startPoint = self._starting_point for eDict in enumdicts: enumController = Enumerate(eDict) if enumController.nEnumStructs == 0: msg.warn( 'There are no enumerated structures for lattice type {}. Not building any VASP folders for them.' .format(self.enumDicts[index]["lattice"])) enumController.buildInputFile() enumController.enumerate() # Loop to generate random structures for a given lattice type for i in range(eDict["nconfigs"]): rStruct = 16254 #randrange(1,enumController.nEnumStructs) print('Adding {} structure # {} to database'.format( eDict["lattice"], rStruct)) with open('structNums', 'a+') as f: f.write(eDict["name"] + ' ' + str(rStruct) + '\n') #print("Building VASP folder for {} structure #: {}".format(eDict["lattice"],rStruct)) enumController.generatePOSCAR(rStruct) poscarpath = path.join( enumController.root, "poscar.{}.{}".format(eDict["name"], rStruct)) thisCrystal = Crystal( poscarpath, systemSpecies=systemSpecies ) #title = ' '.join([self.enumDicts[index]["lattice"]," str #: {}"]).format(rStruct) if self.restrictions is None: self.crystals.append(thisCrystal) elif thisCrystal.getAFMPlanes([1, 0, 0]): print('parent is AFM compatible') self.crystals.append(thisCrystal) import sys sys.exit() else: superCrystal = thisCrystal.superPeriodics(2) if superCrystal != []: print('super periodic structures is AFM compatible') print(superCrystal.minDist, 'minDist') print(superCrystal.basis, ' basis') print(array(superCrystal.Bv_direct), 'direct') print(array(superCrystal.Bv_cartesian), 'cartesian') self.crystals.append(superCrystal) import sys sys.exit() else: print("Can't find an AFM compatible structure") import sys sys.exit() # self.crystals.append(thisCrystal) delpath = path.join( enumController.root, "poscar.{}.{}".format(eDict["name"], rStruct)) remove(delpath)
class VASP: """Class to handle all of the VASP input and output files. Args: specs (dict or str): Either a dictionary containing all of the necessary settings or a path to a folder that contains all of the files needed. root (str): Path to the calculation folder incar (dict): Dictionary containing the INCAR tags to be used potcars (dict): Dictionary containing the necessary settings to find the correct POTCARS. <directory> : where the potcars are located <> kpoints (dict): KPOINTS settings crystal (CRYSTAL OBJ): Crystal description """ def __init__(self, specs,systemSpecies = None, directory = None): from aBuild.database.crystal import Crystal #Initialize from a dictionary if isinstance(specs,dict): if self._all_present(specs): self.POTCAR = POTCAR(specs["potcars"]) self.KPOINTS = KPOINTS(specs["kpoints"]) if isinstance(specs["crystal"],Crystal): self.crystal = specs["crystal"] else: self.crystal = Crystal(specs["crystal"],specs["species"]) self.handleSpecialTags(specs) self.INCAR = INCAR(specs["incar"]) else: msg.fatal("I don't have all the necessary information to initialize: {}".format(specs.keys())) #Initialize from a path elif isinstance(specs, str): self.POTCAR = POTCAR(path.join(specs,'POTCAR')) self.KPOINTS = KPOINTS(path.join(specs,'KPOINTS')) self.crystal = Crystal(path.join(specs,'POSCAR'),systemSpecies,crystalSpecies = self.POTCAR.species) self.directory = specs else: msg.fatal("Unable to initialize a VASP object from the data that you passed in:", specs) if directory is not None: self.directory = directory def _all_present(self,specs): required = ["incar","potcars","kpoints","crystal","species"] for tag in required: if tag not in specs.keys(): return False return True def handleSpecialTags(self,specs): special = ["AFM","FM"] if "FM" in specs.keys(): specs["incar"]["ispin"] = 2 specs["incar"]["magmom"] = '' for idx,species in enumerate(sorted(specs["FM"],reverse = True)): specs["incar"]["magmom"] += ' '.join(map(str, [ specs["FM"][species] ] * self.crystal.atom_counts[idx])) specs["incar"]["magmom"] += ' ' elif "AFM" in specs.keys(): if self.crystal.AFMPlanes == None: self.crystal.getAFMPlanes([1,0,0]) if self.crystal.AFMPlanes == None: msg.info("You supposedly had an AFM crystal, but I'm not finding the planes") return specs["incar"]["ispin"] = 2 #Put in nonzero spin values specs["incar"]["magmom"] = ' '.join(map(str,self.crystal.AFMPlanes)) + ' ' atomsLeft = self.crystal.nAtoms - len(self.crystal.AFMPlanes) specs["incar"]["magmom"] += ' '.join(map(str,[0 for x in range(atomsLeft)])) # VASP does not like to have zeros in the atom_counts list # but I want to keep track of which atoms are in the crystal. # This routine is just here to remove any zeros before I write to # the POSCAR file. def check_atom_counts_zero(self): from numpy import array,any print(self.crystal.atom_counts, 'atom counts') print(any(self.crystal.atom_counts == 0)) if any(self.crystal.atom_counts == 0): from numpy import where idxKeep = list(where( self.crystal.atom_counts > 0)[0]) self.POTCAR.species = list(array(self.POTCAR.species)[idxKeep]) self.crystal.atom_counts = self.crystal.atom_counts[idxKeep] def _check_tag_exists(self,file,tag): from aBuild.utility import grep lines = grep(file,tag) if lines == []: return False else: return True def _check_file_exists(self,file): files = os.listdir('./') if file in files: return True else: return False def status(self): from os import path from time import time from aBuild.utility import grep import os fTagStatic = '------------------------ aborting loop because EDIFF is reached ----------------------------------------\n' fTagRelax = ' writing wavefunctions' ctime = time() print('checking directory {}'.format(self.directory)) with chdir(self.directory): outcar = self._check_file_exists('OUTCAR') incar = self._check_file_exists('INCAR') kpoints = self._check_file_exists('KPOINTS') potcar = self._check_file_exists('POTCAR') poscar = self._check_file_exists('POSCAR') output = self._check_file_exists('vasp_output') oszicar = self._check_file_exists('OSZICAR') inputs = incar and kpoints and potcar and poscar ''' Check to see if the input files are present if they aren't, no need to proceed, just return 'not setup' ''' if not inputs: return 'not setup' ''' If the OUTCAR file is present, we know that we're either running, finished successfully, or finished with errors! ''' elif outcar: # OUTCAR present sgrcon = grep('vasp_output','SGRCON') tooclose = grep('vasp_output','HOPE') finalenergyline = grep('OUTCAR','free energy') generalerror = grep('vasp_output','ERROR') # Check to make sure I've converged electonically. if grep('OSZICAR','DAV:') != []: electronicIteration = int(grep('OSZICAR','DAV:')[-1].split()[1]) else: electronicIteration = 0 if grep('INCAR','nsw') != []: nsw = int(grep('INCAR','nsw')[0].split('=')[1]) if nsw == 0: nsw = 1 else: nsw = 1 if grep('OSZICAR','F=') != []: ionicIteration = int(grep('OSZICAR','F=')[-1].split()[0]) else: ionicIteration = 1 if grep('INCAR','nelm') != []: maxelectronic = grep('INCAR','nelm')[0].split('=')[1] else: maxelectronic = 60 if ionicIteration == nsw and int(electronicIteration) == int(maxelectronic): return 'unconverged' ''' Let's first check to see if this is a static calculation or a relaxation because the tag to check for is different.''' if incar: relax = grep('INCAR','IBRION') if '-1' not in relax or relax is []: static = True else: static = False else: return 'not setup' ''' Check finish tag for static calc''' if static and self._check_tag_exists('OUTCAR', fTagStatic): #finish tag found if finalenergyline != []: #Let's double check return 'done' else: # Apparently not, why? return 'idk' ''' Check finish tag for relax calc''' elif self._check_tag_exists('OUTCAR',fTagRelax): #Looks like it's done if finalenergyline != []: # Let's double check return 'done' else: # Apparently not, why? return 'idk' else: ''' Check how long since the last file write. If it was recent then we're probably running.''' time = path.getmtime('OUTCAR') if (ctime - time) < 3600: # If the OUTCAR was modified in the last hour # the calculation is probably still running. return 'running' elif sgrcon: return 'sgrcon' elif generalerror: return 'error' elif tooclose: return 'warning' else: return 'too long' else: return 'not started' if output: warning = grep('output','RRRRR') != [] or grep('output','AAAAAA') != [] if warning: return 'warning' return folderstat @staticmethod def from_file(runpath): incar = INCAR.from_file(runpath) kpoint = KPOINTS.from_file(runpath) potcars = POTCARS.from_file(runpath) result = VASP(runpath = runpath,incar=incar,kpoints=kpoints,potcars=potcars) return result def buildFolder(self,runGetKPoints = True): from aBuild.calculators.vasp import POSCAR self.KPOINTS.rGP = runGetKPoints self.INCAR.writeINCAR() print("INCAR built") self.crystal.write('POSCAR_orig') print("POSCAR_orig built") self.check_atom_counts_zero() self.crystal.write('POSCAR') print("POSCAR built") self.KPOINTS.writeKPOINTS() print("KPOINTS built") self.POTCAR.writePOTCAR() #print("POTCAR built") def read_forces(self,allIonic = True): with open('POSCAR','r') as file: poslines = file.readlines() if any(c.isalpha() for c in poslines[5].strip()): #It's a CONTCAR nAtoms = sum([int(i) for i in poslines[6].split()]) else: nAtoms = sum([int(i) for i in poslines[5].split()]) with open('OUTCAR', 'r') as file: lines = file.readlines() n = 0 if allIonic: forces = [] n = 0 found = False for line in lines: if line.rfind('TOTAL-FORCE') > -1: found = True singleItForces = [] for i in range(nAtoms): singleItForces.append(np.array([float(f) for f in lines[n + i + 2].split()[3:6]])) msg.info('Found forces for {} atoms.'.format(nAtoms)) if not '--' in lines[n+nAtoms + 2]: msg.fatal('It appears that there are forces for more atoms than I was expecting!') if allIonic: forces.append(singleItForces) n+=1 if not found: msg.info("Couldn't find forces for this calc") return None if not allIonic: forces = singleItForces if allIonic and len(forces) == 1: return forces[0] return forces def read_fermi(self): """Method that reads Fermi energy from OUTCAR file""" E_f = None for line in open('OUTCAR', 'r'): if line.rfind('E-fermi') > -1: E_f = float(line.split()[2]) return E_f def read_nbands(self): for line in open('OUTCAR', 'r'): line = self.strip_warnings(line) if line.rfind('NBANDS') > -1: nBands = int(line.split()[-1]) return nBands def read_energy(self, allElectronic=False): energyZ = None energyF = None if allElectronic: energyF = [] energyZ = [] for line in open('OUTCAR', 'r'): # Free energy if line.lower().startswith(' free energy toten') or line.lower().startswith(' free energy toten'): if allElectronic: energyF.append(float(line.split()[-2])) else: energyF = float(line.split()[-2]) # Extrapolated zero point energy if line.startswith(' energy without entropy'): if allElectronic: energyZ.append(float(line.split()[-1])) else: energyZ = float(line.split()[-1]) return energyF,energyZ def read_stress(self): stress = None for line in open('OUTCAR'): if line.find(' in kB ') != -1: stress = -np.array([float(a) for a in line.split()[2:]]) stress = stress[[0, 1, 2, 4, 5, 3]] * 1e-1 * .00624151# * ase.units.GPa.. Gets me to Giga Pascals return stress def read_results(self, allElectronic = False, allIonic=False): if self.directory is not None and self.status() in ['done','unconverged']: with chdir(self.directory): self.crystal.results = {} self.crystal.results["warning"] = False self.crystal.results["energyF"],self.crystal.results["energyZ"] = self.read_energy(allElectronic=allElectronic) self.crystal.results["forces"] = self.read_forces(allIonic=allIonic) self.crystal.results["stress"] = self.read_stress() #self.POTCAR = POTCAR.from_POTCAR() self.crystal.results["species"] = self.POTCAR.species self.crystal.results["energypatom"] = self.crystal.results["energyF"]/self.crystal.nAtoms if abs(self.crystal.results["energyF"]) > 1000: self.crystal.results["warning"] = True if 'pure' not in self.directory: self.crystal.results["fEnth"] = self.formationEnergy else: self.crystal.results["fEnth"] = 0 else: print(self.status(), 'not reading results') self.crystal.results = None msg.info("Unable to extract necessary information from directory! ({})".format(self.directory)) def add_to_results(self,key,item): if self.crystal.results is None: self.crystal.results = {} self.crystal.results[key] = item @property def formationEnergy(self): pures = [] for i in range(self.crystal.nTypes): pureDir = path.join(path.split(self.directory)[0], 'pure' + self.crystal.species[i]) pureVASP = VASP(pureDir,systemSpecies = self.crystal.species) pureVASP.read_results() pures.append(pureVASP) try: formationEnergy = self.crystal.results["energyF"]/self.crystal.nAtoms - sum( [ pures[i].crystal.results["energyF"]/pures[i].crystal.nAtoms * self.crystal.concentrations[i] for i in range(self.crystal.nTypes)]) except: formationEnergy = 10000 return formationEnergy