def get_cif_files(mofpath, skip_mofs=None): """ Get the list of CIF files Args: mofpath (string): directory to CIF files skip_mofs (list): list of MOFs to ignore Returns: sorted_cifs (list): alphabetized list of CIF files """ cif_files = [] if skip_mofs is None: skip_mofs = [] for filename in os.listdir(mofpath): filename = filename.strip() if '.cif' in filename or 'POSCAR_' in filename: if '.cif' in filename: refcode = filename.split('.cif')[0] elif 'POSCAR_' in filename: refcode = filename.split('POSCAR_')[1] if refcode not in skip_mofs: cif_files.append(filename) else: pprint('Skipping ' + refcode) sorted_cifs = sorted(cif_files) return sorted_cifs
def cif_to_mof(filepath, niggli): """ Convert file to ASE Atoms object Args: filepath (string): full path to structure file niggli (bool): if Niggli-reduction should be performed Returns: sorted_cifs (list): alphabetized list of CIF files """ tol = 0.8 if niggli and has_pm: if '.cif' in os.path.basename(filepath): parser = CifParser(filepath) pm_mof = parser.get_structures(primitive=True)[0] else: pm.Structure.from_file(filepath, primitive=True) pm_mof.to(filename='POSCAR') mof = read('POSCAR') write('POSCAR', mof) elif niggli and not has_pm: warnings.warn('Pymatgen not installed. Niggli set to False', Warning) else: mof = read(filepath) write('POSCAR', mof) mof = read('POSCAR') d = mof.get_all_distances() min_val = np.min(d[d > 0]) if min_val < tol: pprint('WARNING: Atoms overlap by ' + str(min_val)) return mof
def check_if_new_spin(screener, mof, refcode, acc_level, current_spin): """ Check if new spin converged to old spin Args: screener (class): pymofscreen.screen.screener class mof (ASE Atoms object): MOF structure refcode (string): name of MOF acc_level (string): current accuracy level current_spin (string): current spin level Returns: True or False depending on if new spin converged to old spin """ basepath = screener.basepath spin_levels = screener.spin_levels results_partial_path = os.path.join(basepath, 'results', refcode, acc_level) success_path = os.path.join(results_partial_path, current_spin) incarpath = os.path.join(success_path, 'INCAR') mof = deepcopy(mof) mof = continue_magmoms(mof, incarpath) for prior_spin in spin_levels: if prior_spin == current_spin: continue old_mof_path = os.path.join(results_partial_path, prior_spin, 'OUTCAR') old_incar_path = os.path.join(results_partial_path, prior_spin, 'INCAR') mag_indices = get_mag_indices(mof) old_mof = read(old_mof_path) old_abs_magmoms, old_mag_indices, old_ispin = get_abs_magmoms( old_mof, old_incar_path) mof_mag = mof.get_initial_magnetic_moments()[mag_indices] if old_ispin: old_mof_mag = old_mof.get_magnetic_moments()[mag_indices] else: old_mof_mag = [0] * len(mag_indices) mag_tol = 0.05 if np.sum(np.abs(mof_mag - old_mof_mag) >= mag_tol) == 0: pprint('Skipping rest because ' + current_spin + ' converged to ' + prior_spin) return False return True
def scf_test(self, atoms_overwrite=None, quick_test=False): """ Run SCF test job to check for errors Returns: scf_pass (bool): True if passed SCF test """ outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_level = self.spin_level cif_file = self.cif_file mofpath = self.mofpath spin1_final_mof_path = self.spin1_final_mof_path kpts_lo = self.kpts_dict['kpts_lo'] acc_level = self.acc_levels[self.run_i] niggli = self.niggli calcs = self.calcs if not os.path.isfile(outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): if atoms_overwrite: mof = deepcopy(atoms_overwrite) else: if spin1_final_mof_path is None: mof = cif_to_mof(os.path.join(mofpath, cif_file), niggli) else: mof = read(spin1_final_mof_path) mof = set_initial_magmoms(mof, spin_level) if quick_test: self.calc_swaps.append('nelm=5') self.calc_swaps.append('lwave=False') pprint('Running ' + spin_level + ', ' + acc_level) mof, self.calc_swaps = mof_run(self, mof, calcs('scf_test'), kpts_lo) if quick_test: self.calc_swaps.remove('nelm=5') self.calc_swaps.remove('lwave=False') if mof is not None: write_success(self) else: pprint('^ VASP crashed') write_errors(self, mof) elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_level + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return False warnings = get_warning_msgs(outcar_paths[self.run_i - 1]) self.calc_swaps.extend(warnings) return True
def isif3_lowacc(self): """ Run low accuracy ISIF3 Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_label = self.spin_label kpts_lo = self.kpts_dict['kpts_lo'] acc_level = acc_levels[self.run_i] calcs = self.calcs prior_results_path = os.path.join( self.results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(outcar_paths[self.run_i - 1]) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): mof = prep_new_run(self) converged = False loop_i = 0 n_runs = 15 manage_restart_files(prior_results_path) while not converged and loop_i < n_runs: if loop_i == 10 and 'fire' not in self.calc_swaps and 'zbrent' not in self.calc_swaps: self.calc_swaps.append('fire') pprint('Running ' + spin_label + ', ' + acc_level + ': iteration ' + str(loop_i) + '/' + str(n_runs - 1)) mof, self.calc_swaps = mof_run(self, mof, calcs('isif3_lowacc'), kpts_lo) if mof is None: break converged = mof.calc.converged mof = read('OUTCAR') mof = continue_magmoms(mof, 'INCAR') loop_i += 1 if 'fire' in self.calc_swaps: self.calc_swaps.remove('fire') if mof is not None and converged: write_success(self) else: write_errors(self, mof) elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return None return mof
def cineb_lowacc(self, initial_atoms, final_atoms, n_images): """ Run CI-NEB low accuracy calculation Returns: mof (ASE Atoms object): updated ASE Atoms object """ spin_level = self.spin_level kpts_lo = self.kpts_dict['kpts_lo'] calcs = self.calcs nprocs = self.nprocs pwd = os.getcwd() partial_path = self.results_partial_paths[self.run_i] data_path = os.path.join(partial_path, 'neb.tar.gz') partial_error_path = self.error_partial_paths[self.run_i] error_data_path = os.path.join(partial_error_path, 'neb.tar.gz') neb_conv = False if nprocs % n_images != 0: raise ValueError( str(nprocs) + ' procs not divisible by ' + str(n_images)) if not os.path.isfile(data_path) and not os.path.isfile( error_data_path): if initial_atoms.get_chemical_formula( ) != final_atoms.get_chemical_formula(): raise ValueError('POSCAR1 and POSCAR2 must have same atoms') nebmake(initial_atoms, final_atoms, n_images) initial_atoms = set_initial_magmoms(initial_atoms, spin_level) pprint('Running CI-NEB (pre-dimer)') initial_atoms, self.calc_swaps = mof_run(self, initial_atoms, calcs('cineb_lowacc'), kpts_lo, images=n_images) ediffg = calcs('cineb_lowacc').exp_params['ediffg'] neb_conv = nebef(ediffg) os.chdir(pwd) if neb_conv: write_success(self, neb=True) else: write_errors(self, initial_atoms, neb=True) vtst_cleanup() elif os.path.isfile(data_path): pprint('COMPLETED: CI-NEB (pre-dimer)') neb_conv = True self.run_i += 1 if not neb_conv: pprint('Skipping rest because of errors') return False return neb_conv
def isif2_highacc(self): """ Run high accuracy ISIF2 Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_label = self.spin_label kpts_hi = self.kpts_dict['kpts_hi'] acc_level = acc_levels[self.run_i] calcs = self.calcs prior_results_path = os.path.join( self.results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(outcar_paths[self.run_i - 1]) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): mof = prep_new_run(self) manage_restart_files(prior_results_path) pprint('Running ' + spin_label + ', ' + acc_level) mof, self.calc_swaps = mof_run(self, mof, calcs('isif2_highacc'), kpts_hi) if mof is not None and mof.calc.scf_converged and mof.calc.converged: if 'large_supercell' in self.calc_swaps: self.calc_swaps.remove('large_supercell') mof = read('OUTCAR') mof = continue_magmoms(mof, 'INCAR') mof, self.calc_swaps = mof_run(self, mof, calcs('isif2_highacc'), kpts_hi) if mof is not None and mof.calc.scf_converged and mof.calc.converged: write_success(self) else: write_errors(self, mof) else: write_success(self) else: write_errors(self, mof) elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return None return mof
def isif2_medacc(self): """ Run medium accuracy ISIF2 Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_label = self.spin_label kpts_lo = self.kpts_dict['kpts_lo'] kpts_hi = self.kpts_dict['kpts_hi'] acc_level = acc_levels[self.run_i] calcs = self.calcs prior_results_path = os.path.join( self.results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(outcar_paths[self.run_i - 1]) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): mof = prep_new_run(self) if sum(kpts_lo) == 3 and sum(kpts_hi) > 3: clean_files(['CHGCAR', 'WAVECAR']) else: manage_restart_files(prior_results_path) pprint('Running ' + spin_label + ', ' + acc_level) mof, self.calc_swaps = mof_run(self, mof, calcs('isif2_medacc'), kpts_hi) if mof is not None and mof.calc.scf_converged and mof.calc.converged: write_success(self) else: write_errors(self, mof) elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return None return mof
def final_spe(self): """ Run final single point Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_label = self.spin_label kpts_hi = self.kpts_dict['kpts_hi'] acc_level = acc_levels[self.run_i] calcs = self.calcs self.calc_swaps = [] prior_results_path = os.path.join( self.results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(outcar_paths[self.run_i - 1]) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): mof = prep_new_run(self) manage_restart_files(prior_results_path) pprint('Running ' + spin_label + ', ' + acc_level) mof, self.calc_swaps = mof_run(self, mof, calcs('final_spe'), kpts_hi) if mof is not None and mof.calc.scf_converged: write_success(self) else: write_errors(self, mof) elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return None return mof
def dimer(self): """ Run dimer Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_level = self.spin_level spin_label = self.spin_label prior_spin = self.prior_spin acc_level = acc_levels[self.run_i] prior_acc_level = acc_levels[self.run_i - 1] results_partial_paths = self.results_partial_paths pwd = os.getcwd() if 'lowacc' in acc_level: kpts = self.kpts_dict['kpts_lo'] else: kpts_lo = self.kpts_dict['kpts_lo'] kpts = self.kpts_dict['kpts_hi'] calcs = self.calcs if 'neb' in prior_acc_level and prior_spin is None: prior_results_path = os.path.join( results_partial_paths[self.run_i - 1]) prior_results_file = os.path.join(prior_results_path, 'neb.tar.gz') elif 'lowacc' in acc_level and prior_spin is not None: prior_results_path = os.path.join(results_partial_paths[-1], prior_spin) prior_results_file = os.path.join(prior_results_path, 'OUTCAR') else: prior_results_file = outcar_paths[self.run_i - 1] prior_results_path = os.path.join( results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(prior_results_file) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): if 'scf_test' in prior_acc_level: mof = prep_new_run(self) manage_restart_files(prior_results_path, dimer=False) elif 'neb' in prior_acc_level and prior_spin is None: manage_restart_files(prior_results_path, neb=True) mof = neb2dim() mof = set_initial_magmoms(mof, spin_level) elif 'lowacc' in acc_level and prior_spin is not None: mof = read(prior_results_file) mof = set_initial_magmoms(mof, spin_level) manage_restart_files(prior_results_path, dimer=True, wavechg=False) else: mof = prep_new_run(self) manage_restart_files(prior_results_path, dimer=True) if 'medacc' in acc_level and sum( kpts_lo) == 3 and sum(kpts) > 3: clean_files(['CHGCAR', 'WAVECAR']) if 'highacc' in acc_level and 'large_supercell' in self.calc_swaps: self.calc_swaps.remove('large_supercell') pprint('Running ' + spin_label + ', ' + acc_level) mof, self.calc_swaps = mof_run(self, mof, calcs(acc_level), kpts) if mof is not None and mof.calc.scf_converged and mof.calc.converged: write_success(self) else: write_errors(self, mof) vtst_cleanup() elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) os.chdir(pwd) if mof is None: pprint('Skipping rest because of errors') return None return mof
def isif3_highacc(self): """ Run high accuracy ISIF3 Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_label = self.spin_label kpts_lo = self.kpts_dict['kpts_lo'] kpts_hi = self.kpts_dict['kpts_hi'] acc_level = acc_levels[self.run_i] calcs = self.calcs prior_results_path = os.path.join( self.results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(outcar_paths[self.run_i - 1]) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): mof = prep_new_run(self) converged = False loop_i = 0 n_runs = 15 V_diff = np.inf V_cut = 0.01 V0 = mof.get_volume() if sum(kpts_lo) == 3 and sum(kpts_hi) > 3: clean_files(['CHGCAR', 'WAVECAR']) else: manage_restart_files(prior_results_path) while (not converged or V_diff > V_cut) and loop_i < n_runs: if loop_i == 10 and 'fire' not in self.calc_swaps and 'zbrent' not in self.calc_swaps: self.calc_swaps.append('fire') pprint('Running ' + spin_label + ', ' + acc_level + ': iteration ' + str(loop_i) + '/' + str(n_runs - 1)) mof, self.calc_swaps = mof_run(self, mof, calcs('isif3_highacc'), kpts_hi) if mof is None: break if loop_i > 0: converged = mof.calc.converged mof = read('OUTCAR') V = mof.get_volume() mof = continue_magmoms(mof, 'INCAR') if loop_i > 0: V_diff = np.abs((V - V0)) / V0 V0 = V loop_i += 1 if mof is not None and converged and V_diff <= V_cut and 'large_supercell' in self.calc_swaps: self.calc_swaps.append('nsw=100') self.calc_swaps.remove('large_supercell') pprint('Running ' + spin_label + ', ' + acc_level + ' (LREAL=False)') mof, self.calc_swaps = mof_run(self, mof, calcs('isif3_highacc'), kpts_hi) self.calc_swaps.remove('nsw=100') if mof is not None and mof.calc.converged: write_success(self) else: write_errors(self, mof) else: write_errors(self, mof) if mof is not None and V_diff > V_cut: pprint('^ Change in V of ' + str(V_diff) + ' percent') if 'fire' in self.calc_swaps: self.calc_swaps.remove('fire') elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return None return mof
def isif2_lowacc(self): """ Run low accuracy ISIF2 Returns: mof (ASE Atoms object): updated ASE Atoms object """ acc_levels = self.acc_levels outcar_paths = self.outcar_paths error_outcar_paths = self.error_outcar_paths spin_level = self.spin_level spin_label = self.spin_label cif_file = self.cif_file mofpath = self.mofpath prior_spin = self.prior_spin spin1_final_mof_path = self.spin1_final_mof_path kpts_lo = self.kpts_dict['kpts_lo'] acc_level = acc_levels[self.run_i] niggli = self.niggli calcs = self.calcs prior_results_path = os.path.join( self.results_partial_paths[self.run_i - 1], spin_label) if os.path.isfile(outcar_paths[self.run_i - 1]) and not os.path.isfile( outcar_paths[self.run_i]) and not os.path.isfile( error_outcar_paths[self.run_i]): if prior_spin is None: mof = cif_to_mof(os.path.join(mofpath, cif_file), niggli) else: mof = read(spin1_final_mof_path) manage_restart_files(prior_results_path) mof = set_initial_magmoms(mof, spin_level) fmax = 5.0 pprint('Running ' + spin_label + ', ' + acc_level) mof, dyn, self.calc_swaps = mof_bfgs_run(self, mof, calcs('ase_bfgs'), kpts_lo, fmax=fmax) if mof is not None and dyn: loop_i = 0 converged = False clean_files(['opt.traj']) while mof is not None and loop_i < 4 and not converged and mof.calc.scf_converged: if loop_i == 2 and 'fire' not in self.calc_swaps and 'zbrent' not in self.calc_swaps: self.calc_swaps.append('fire') mof = read('OUTCAR') mof = continue_magmoms(mof, 'INCAR') mof, self.calc_swaps = mof_run(self, mof, calcs('isif2_lowacc'), kpts_lo) if mof is None: break converged = mof.calc.converged loop_i += 1 if 'fire' in self.calc_swaps: self.calc_swaps.remove('fire') if mof is not None and mof.calc.scf_converged and mof.calc.converged: write_success(self) else: write_errors(self, mof) elif os.path.isfile(outcar_paths[self.run_i]): pprint('COMPLETED: ' + spin_label + ', ' + acc_level) mof = prep_next_run(self) if mof is None: pprint('Skipping rest because of errors') return None return mof
def run_screen(self, cif_file, mode, spin_levels=None, acc_levels=None, niggli=True, calculators=None): """ Run high-throughput ionic or volume relaxations Args: cif_file (string): name of CIF file mode (string): 'ionic' or 'volume' spin_levels (list of strings): spin states to consider (defaults to ['spin1','spin2']) acc_levels (list of strings): accuracy levels to consider (defaults to ['scf_test','isif2_lowacc','isif2_medacc','isif2_highacc','final_spe']) niggli (bool): True/False if Niggli-reduction should be done (defaults to niggli=True) calculators (function): function to call respective calculator (defaults to automatically importing from pymofscreen.default_calculators.calcs) Returns: best_mof (ASE Atoms objects): ASE Atoms object for optimized MOF """ #Setup default parameters from pymofscreen.default_calculators import calcs if calculators is None: calculators = calcs self.calcs = calculators basepath = self.basepath self.niggli = niggli if mode == 'ionic': if acc_levels is None: acc_levels = [ 'scf_test', 'isif2_lowacc', 'isif2_medacc', 'isif2_highacc', 'final_spe' ] elif mode == 'ionic_legacy': if acc_levels is None: acc_levels = [ 'scf_test', 'isif2_lowacc', 'isif2_medacc', 'final', 'final_spe' ] elif mode == 'volume': if acc_levels is None: acc_levels = [ 'scf_test', 'isif2_lowacc', 'isif3_lowacc', 'isif3_highacc', 'final_spe' ] elif mode == 'volume_legacy': if acc_levels is None: acc_levels = [ 'scf_test', 'isif2', 'isif3_lowacc', 'isif3_highacc', 'final', 'final_spe' ] else: raise ValueError('Unsupported DFT screening mode') if 'scf_test' not in acc_levels: acc_levels = ['scf_test'] + acc_levels self.acc_levels = acc_levels if spin_levels is None: spin_levels = ['spin1', 'spin2'] self.spin_levels = spin_levels #Make sure MOF isn't running on other process if 'POSCAR_' in cif_file: refcode = cif_file.split('POSCAR_')[1] elif '.cif' in cif_file: refcode = cif_file.split('.cif')[0] else: raise ValueError('Unknown file naming scheme') working_cif_path = os.path.join(basepath, 'working', refcode) if os.path.isfile(working_cif_path): pprint('SKIPPED ' + refcode + ': Running on another process') return None open(working_cif_path, 'w').close() #Get the kpoints kpts_lo, gamma = get_kpts(self, cif_file, 'low') kpts_hi, gamma = get_kpts(self, cif_file, 'high') kpts_dict = {} kpts_dict['kpts_lo'] = kpts_lo kpts_dict['kpts_hi'] = kpts_hi kpts_dict['gamma'] = gamma #Initialize variables E = np.inf mof = None #for each spin level, optimize the structure for i, spin_level in enumerate(spin_levels): pprint('***STARTING ' + refcode + ': ' + spin_level + '***') #Check if spin state should be skipped if spin_level != spin_levels[0]: prior_spin = spin_levels[i - 1] else: prior_spin = None if i > 0: skip_low_spin = check_if_skip_low_spin(self, mof, refcode, prior_spin) if (prior_spin == 'spin1' or prior_spin == 'high_spin') and skip_low_spin: pprint('Skipping ' + spin_level + ' run') continue same_spin = False #Set up workflow object wf = workflows(self, cif_file, kpts_dict, spin_level, prior_spin) #for each accuracy level, optimize structure for acc_level in acc_levels: if acc_level == 'scf_test': scf_pass = wf.scf_test() if not scf_pass: os.remove(working_cif_path) return None if acc_levels[-1] == 'scf_test': os.remove(working_cif_path) return scf_pass elif acc_level == 'isif2_lowacc' or (acc_level == 'isif2' and mode == 'volume_legacy'): mof = wf.isif2_lowacc() if mof is None: os.remove(working_cif_path) return None if i > 0: is_new_spin = check_if_new_spin( self, mof, refcode, acc_level, spin_level) if not is_new_spin: same_spin = True break elif acc_level == 'isif2_medacc': mof = wf.isif2_medacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'isif2_highacc' or (acc_level == 'final' and 'legacy' in mode): mof = wf.isif2_highacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'isif3_lowacc': mof = wf.isif3_lowacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'isif3_highacc': mof = wf.isif3_highacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'final_spe': mof = wf.final_spe() if mof is None: os.remove(working_cif_path) return None else: raise ValueError('Unsupported accuracy level') #***********SAVE and CONTINUE*********** if same_spin: continue E_temp = mof.get_potential_energy() if E_temp < E: best_mof = deepcopy(mof) os.remove(working_cif_path) return best_mof
def run_ts_screen(self, name, initial_atoms, final_atoms, n_images=4, cif_file=None, spin_levels=None, acc_levels=None, calculators=None): """ Run high-throughput TS calculation Args: name (string): name of CIF file initial_atoms (ASE Atoms object): initial structure final_atoms (ASE Atoms object): final structure n_images (int): number of NEB images cif_file (string): name of CIF file to generate kpoints if set to 'Auto' spin_levels (list of strings): spin states to consider acc_levels (list of strings): accuracy levels to consider calculators (function): function to call respective calculator (defaults to automatically importing from pymofscreen.default_calculators.calcs) Returns: best_mof (ASE Atoms objects): ASE Atoms object for optimized MOF given by cif_file (lowest energy spin state) """ #Setup default parameters from pymofscreen.default_calculators import calcs if calculators is None: calculators = calcs self.calcs = calculators basepath = self.basepath self.niggli = False self.spin_levels = spin_levels if spin_levels is None: spin_levels = ['spin1', 'spin2'] self.spin_levels = spin_levels if acc_levels is None: acc_levels = [ 'scf_test', 'cineb_lowacc', 'dimer_lowacc', 'dimer_medacc', 'dimer_highacc', 'final_spe' ] if 'cineb_lowacc' not in acc_levels: acc_levels = ['cineb_lowacc'] + acc_levels if 'scf_test' not in acc_levels: acc_levels = ['scf_test'] + acc_levels self.acc_levels = acc_levels kpts_path = self.kpts_path if kpts_path == 'Auto' and cif_file is None: raise ValueError('Specify a CIF file if using automatic KPPA') #Ensure initial/final state have the same composition if initial_atoms.get_chemical_formula( ) != final_atoms.get_chemical_formula(): pprint( 'SKIPPED: Atoms not identical between initial and final state') return None #Make sure MOF isn't running on other process working_cif_path = os.path.join(basepath, 'working', name) if os.path.isfile(working_cif_path): pprint('SKIPPED ' + name + ': Running on another process') return None open(working_cif_path, 'w').close() #Get the kpoints if kpts_path == 'Auto': kpts_lo, gamma = get_kpts(self, cif_file, 'low') kpts_hi, gamma = get_kpts(self, cif_file, 'high') else: kpts_lo, gamma = get_kpts(self, name.split('_TS')[0], 'low') kpts_hi, gamma = get_kpts(self, name.split('_TS')[0], 'high') kpts_dict = {} kpts_dict['kpts_lo'] = kpts_lo kpts_dict['kpts_hi'] = kpts_hi kpts_dict['gamma'] = gamma #Initialize variables E = np.inf mof = None #for each spin level, optimize the structure for i, spin_level in enumerate(spin_levels): pprint('***STARTING ' + name + ': ' + spin_level + '***') #Check if spin state should be skipped if spin_level != spin_levels[0]: prior_spin = spin_levels[i - 1] else: prior_spin = None if i > 0: skip_low_spin = check_if_skip_low_spin(self, mof, name, prior_spin) if (prior_spin == 'spin1' or prior_spin == 'high_spin') and skip_low_spin: pprint('Skipping ' + spin_level + ' run') continue same_spin = False #Set up workflow object wf = workflows(self, name, kpts_dict, spin_level, prior_spin) #for each accuracy level, optimize structure for acc_level in acc_levels: if acc_level == 'scf_test': scf_pass = wf.scf_test(atoms_overwrite=initial_atoms, quick_test=True) if not scf_pass: os.remove(working_cif_path) return None if acc_levels[-1] == 'scf_test': os.remove(working_cif_path) return scf_pass elif acc_level == 'cineb_lowacc' and i == 0: neb_conv = wf.cineb_lowacc(initial_atoms, final_atoms, n_images) if not neb_conv: os.remove(working_cif_path) return None if acc_levels[-1] == 'cineb_lowacc': os.remove(working_cif_path) return neb_conv elif acc_level == 'cineb_lowacc' and i > 0: wf.run_i += 1 continue elif 'dimer' in acc_level: mof = wf.dimer() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'final_spe': mof = wf.final_spe() if mof is None: os.remove(working_cif_path) return None result_path = os.path.join(basepath, 'results', name) newmodecar = os.path.join(result_path, acc_levels[-2], spin_level, 'NEWMODECAR') newmodecar_spe = os.path.join(result_path, acc_level, spin_level, 'NEWMODECAR') copyfile(newmodecar, newmodecar_spe) else: raise ValueError('Unsupported accuracy level') if acc_level == 'dimer_lowacc' and i > 0: is_new_spin = check_if_new_spin(self, mof, name, acc_level, spin_level) if not is_new_spin: same_spin = True break #***********SAVE and CONTINUE*********** if same_spin: continue E_temp = mof.get_potential_energy() if E_temp < E: best_mof = deepcopy(mof) os.remove(working_cif_path) return best_mof
def run_screen(self,cif_file,mode,niggli=True,spin_levels=None,acc_levels=None,calculators=None,nupdowns=None): """ Run high-throughput ionic or volume relaxations Args: cif_file (string): name of CIF file mode (string): 'ionic', 'volume', or 'ts' niggli (bool): True/False if Niggli-reduction should be done (defaults to niggli=True) spin_levels (list of lists or list of strings): spin states to consider. If provided as a list of lists, each sub-list represents the initial magmom for each atom in cif_file. If a list of string, the strings must be 'high', 'low', 'all-#', 'mixed', or 'AFM_high' to use pre-set initial magmoms (defaults to ['high','low']) acc_levels (list of strings): accuracy levels to consider calculators (function): function to call respective calculator (defaults to automatically importing from pymofscreen.default_calculators.calcs) nupdowns (list of ints): value to set NUPDOWN (defaults to None) Returns: best_mof (ASE Atoms objects): ASE Atoms object for optimized MOF """ #Setup default parameters from pymofscreen.default_calculators import calcs if calculators is None: calculators = calcs self.calcs = calculators self.niggli = niggli basepath = self.basepath if mode == 'ionic': if acc_levels is None: acc_levels = ['scf_test','isif2_lowacc','isif2_medacc', 'isif2_highacc','final_spe'] elif mode == 'volume': if acc_levels is None: acc_levels = ['scf_test','isif2_lowacc','isif3_lowacc', 'isif3_highacc','final_spe'] elif mode == 'ts': if acc_levels is None: acc_levels = ['scf_test','dimer_lowacc'] else: raise ValueError('Unsupported DFT screening mode') if 'scf_test' not in acc_levels: acc_levels = ['scf_test']+acc_levels self.acc_levels = acc_levels if spin_levels is None: spin_levels = ['high','low'] if not isinstance(spin_levels,list): spin_levels = [spin_levels] self.spin_levels = spin_levels if nupdowns is None: nupdowns = [None]*len(spin_levels) elif nupdowns is not None and len(nupdowns) != len(spin_levels): raise ValueError('Length of nupdowns must equal spin_levels') self.nupdowns = nupdowns spin_labels = ['spin'+str(i+1) for i,j in enumerate(spin_levels)] self.spin_labels = spin_labels #Make sure MOF isn't running on other process if 'POSCAR_' in cif_file: refcode = cif_file.split('POSCAR_')[1] elif '.cif' in cif_file: refcode = cif_file.split('.cif')[0] else: raise ValueError('Unknown file naming scheme') working_cif_path = os.path.join(basepath,'working',refcode) if os.path.isfile(working_cif_path): pprint('SKIPPED '+refcode+': Running on another process') return None open(working_cif_path,'w').close() #Get the kpoints kpts_lo, gamma = get_kpts(self,cif_file,'low') kpts_hi, gamma = get_kpts(self,cif_file,'high') kpts_dict = {} kpts_dict['kpts_lo'] = kpts_lo kpts_dict['kpts_hi'] = kpts_hi kpts_dict['gamma'] = gamma #Initialize variables E = np.inf mof = None prior_spin = None #for each spin level, optimize the structure for i, spin_level in enumerate(spin_levels): spin_label = spin_labels[i] self.spin_label = spin_label self.nupdown = nupdowns[i] pprint('***STARTING '+refcode+': '+spin_label+'***') #Check if spin state should be skipped if i > 0: prior_spin = spin_labels[i-1] if spin_levels[i-1] == 'high': skip_low_spin = check_if_skip_low_spin(self,mof,refcode,prior_spin) if skip_low_spin: pprint('Skipping low spin due to low magmoms in prior run') continue same_spin = False #Set up workflow object wf = workflows(self,cif_file,kpts_dict,spin_level,prior_spin) #for each accuracy level, optimize structure for acc_level in acc_levels: if acc_level == 'scf_test': scf_pass = wf.scf_test() if not scf_pass: os.remove(working_cif_path) return None if acc_levels[-1] == 'scf_test': os.remove(working_cif_path) return scf_pass elif acc_level == 'isif2_lowacc': mof = wf.isif2_lowacc() if mof is None: os.remove(working_cif_path) return None if i > 0: is_new_spin = check_if_new_spin(self,mof,refcode,acc_level,spin_label) if not is_new_spin: same_spin = True break elif acc_level == 'isif2_medacc': mof = wf.isif2_medacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'isif2_highacc': mof = wf.isif2_highacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'isif3_lowacc': mof = wf.isif3_lowacc() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'isif3_highacc': mof = wf.isif3_highacc() if mof is None: os.remove(working_cif_path) return None elif 'dimer' in acc_level: mof = wf.dimer() if mof is None: os.remove(working_cif_path) return None elif acc_level == 'final_spe': mof = wf.final_spe() if mof is None: os.remove(working_cif_path) return None if 'dimer' in acc_levels[-2]: result_path = os.path.join(basepath,'results',refcode) newmodecar = os.path.join(result_path,acc_levels[-2],spin_label,'NEWMODECAR') newmodecar_spe = os.path.join(result_path,acc_level,spin_label,'NEWMODECAR') copyfile(newmodecar,newmodecar_spe) else: raise ValueError('Unsupported accuracy level') #***********SAVE and CONTINUE*********** if same_spin: continue E_temp = mof.get_potential_energy() if E_temp < E: best_mof = deepcopy(mof) os.remove(working_cif_path) return best_mof