def load_dftb_calculator(directory, atoms): """ Set the tag for a muon in an atoms object Args: directory (str): path to a directory to load DFTB+ results Returns: calculator (ase.calculator.SinglePointCalculator): a single point calculator for the results of the DFTB+ calculation """ results_file = os.path.join(directory, "results.tag") temp_file = os.path.join(directory, "results.tag.bak") # We need to backup the results file here because # .read_results() will remove the results file with BackupFile(results_file, temp_file): calc = Dftb(atoms=atoms) calc.atoms_input = atoms calc.directory = directory calc.read_results() return SinglePointCalculator(atoms, energy=calc.get_potential_energy(), forces=calc.get_forces(), charges=calc.get_charges(atoms), stress=calc.get_stress(atoms))
def save_muonconf_dftb(a, folder, params, dftbargs={}): from pymuonsuite.data.dftb_pars import DFTBArgs name = os.path.split(folder)[-1] a.set_pbc(params['dftb_pbc']) dargs = DFTBArgs(params['dftb_set']) custom_species = a.get_array('castep_custom_species') muon_index = np.where(custom_species == params['mu_symbol'])[0][0] is_spinpol = params.get('spin_polarized', False) if is_spinpol: dargs.set_optional('spinpol.json', True) # Add muon mass args = dargs.args args['Driver_'] = 'ConjugateGradient' args['Driver_Masses_'] = '' args['Driver_Masses_Mass_'] = '' args['Driver_Masses_Mass_Atoms'] = '{}'.format(muon_index) args['Driver_Masses_Mass_MassPerAtom [amu]'] = '0.1138' args['Driver_MaxForceComponent [eV/AA]'] = params['geom_force_tol'] args['Driver_MaxSteps'] = params['geom_steps'] args['Driver_MaxSccIterations'] = params['max_scc_steps'] if is_spinpol: # Configure initial spins spins = np.array(a.get_initial_magnetic_moments()) args['Hamiltonian_SpinPolarisation_InitialSpins'] = '{' args['Hamiltonian_SpinPolarisation_' + 'InitialSpins_AllAtomSpins'] = '{' + '\n'.join( map(str, spins)) + '}' args['Hamiltonian_SpinPolarisation_UnpairedElectrons'] = str( np.sum(spins)) # Add any custom arguments if isinstance(dftbargs, DFTBArgs): args.update(dftbargs.args) else: args.update(dftbargs) if params['dftb_pbc']: dcalc = Dftb(label=name, atoms=a, kpts=params['k_points_grid'], run_manyDftb_steps=True, **args) else: dcalc = Dftb(label=name, atoms=a, run_manyDftb_steps=True, **args) dcalc.directory = folder dcalc.write_input(a)
def make_dftb_calc(atoms, folder, params): """Make a DFTB+ calculator with the supplied parameters Args: atoms (ase.Atoms): an atoms object to create the DFTB+ calculator for folder (str): directory where the structure files are located. params (dict): dictionary of parameters to pass to the calculator Returns: dcalc (ase.calculators.dftb.Dftb): DFTB+ calculator setup with the provided parameters """ name = os.path.split(folder)[-1] atoms.set_pbc(params['dftb_pbc']) dargs = DFTBArgs(params['dftb_set']) # Add muon mass args = dargs.args args['Driver_'] = 'ConjugateGradient' args['Driver_Masses_'] = '' args['Driver_Masses_Mass_'] = '' args['Driver_Masses_Mass_Atoms'] = '{}'.format(len(atoms.positions)) args['Driver_Masses_Mass_MassPerAtom [amu]'] = '0.1138' if 'geom_force_tol' in params: args['Driver_MaxForceComponent [eV/AA]'] = params['geom_force_tol'] # Max steps is zero because we don't want to optimize with DFTB+. Instead # we just evaluate using DFTB+ let the ASE LBFGS optimizer do the work. args['Driver_MaxSteps'] = '0' # The run_manyDftb_steps option is required here to prevent DFTB+ # overwriting some of the options we set. if params['dftb_pbc']: dcalc = Dftb(label=name, atoms=atoms, kpts=params['k_points_grid'], run_manyDftb_steps=True, **args) else: dcalc = Dftb(label=name, atoms=atoms, run_manyDftb_steps=True, **args) dcalc.directory = folder return dcalc
def dftb_read_input(folder): """Read a DFTB+ output non-destructively. Args: directory (str): path to a directory to load DFTB+ results Returns: atoms (ase.Atoms): an atomic structure with the results attached in a SinglePointCalculator """ atoms = io.read(os.path.join(folder, 'geo_end.gen')) atoms.info['name'] = os.path.split(folder)[-1] results_file = os.path.join(folder, "results.tag") if os.path.isfile(results_file): # DFTB+ was used to perform the optimisation temp_file = os.path.join(folder, "results.tag.bak") # We need to backup the results file here because # .read_results() will remove the results file with BackupFile(results_file, temp_file): calc = Dftb(atoms=atoms) calc.atoms_input = atoms calc.directory = folder calc.do_forces = True try: calc.read_results() except: # Failed for ANY reason atoms.set_calculator(None) return atoms energy = calc.get_potential_energy() forces = calc.get_forces() charges = calc.get_charges(atoms) calc = SinglePointCalculator(atoms, energy=energy, forces=forces, charges=charges) atoms.set_calculator(calc) return atoms
def read(self, folder, sname=None, read_spinpol=False, read_phonons=False, **kwargs): ''' Read a DFTB+ output non-destructively. | | Args: | folder (str) : path to a directory to load DFTB+ results | sname (str): name to label the atoms with and/or of the | .phonons.pkl file to be read | Returns: | atoms (ase.Atoms): an atomic structure with the results | attached in a SinglePointCalculator ''' try: with silence_stdio(): atoms = io.read(os.path.join(folder, 'geo_end.gen')) except IOError: raise IOError("ERROR: No geo_end.gen file found in {}." .format(os.path.abspath(folder))) except Exception as e: raise IOError("ERROR: Could not read {file}, due to error: {error}" .format(file='geo_end.gen', error=e)) if sname is None: atoms.info['name'] = os.path.split(folder)[-1] else: atoms.info['name'] = sname results_file = os.path.join(folder, "results.tag") if os.path.isfile(results_file): # DFTB+ was used to perform the optimisation temp_file = os.path.join(folder, "results.tag.bak") # We need to backup the results file here because # .read_results() will remove the results file with BackupFile(results_file, temp_file): calc = Dftb(atoms=atoms) calc.atoms_input = atoms calc.directory = folder calc.do_forces = True calc.read_results() energy = calc.get_potential_energy() forces = calc.get_forces() charges = calc.get_charges(atoms) calc = SinglePointCalculator(atoms, energy=energy, forces=forces, charges=charges) atoms.calc = calc if read_spinpol: try: pops = parse_spinpol_dftb(folder) hfine = [] for i in range(len(atoms)): hf = compute_hfine_mullpop(atoms, pops, self_i=i, fermi=True, fermi_neigh=True) hfine.append(hf) atoms.set_array('hyperfine', np.array(hfine)) except (IndexError, IOError) as e: raise IOError('Could not read hyperfine details due to error: ' '{0}'.format(e)) if read_phonons: try: if sname is not None: phonon_source_file = os.path.join(folder, sname + '.phonons.pkl') else: print("Phonons filename was not given, searching for any" " .phonons.pkl file.") phonon_source_file = glob.glob( os.path.join(folder, '*.phonons.pkl'))[0] self._read_dftb_phonons(atoms, phonon_source_file) except IndexError: raise IOError("No .phonons.pkl files found in {}." .format(os.path.abspath(folder))) except IOError: raise IOError("{} could not be found." .format(phonon_source_file)) except Exception as e: raise IOError('Could not read {file} due to error: {error}' .format(file=phonon_source_file, error=e)) return atoms