Пример #1
0
class QE(Calculator):
    """Quantum Espresso ASE Calculator
    
    """
    implemented_properties = ['energy', 'forces', 'stress', 
                              'ibz_kpoint_eigenvalues', 'ibz_kpoints_position', 'ibz_kpoints_weight', 
                              'nspins', 'nbands', 'xc_functional', 'fermi_energy', 'dos']

    default_parameters = {
        'calculation': 'scf',
        'convergence': {'energy': 1E-6}
    }

    def __init__(self, restart=None, ignore_bad_restart_file=False, label=None,
                 atoms=None, **kwargs):
        """Accepted Keypairs
        calculation:
            str - only scf[default] supported at the moment
        ecutwfc: 
            plane wave cuttoff [eV] notice not in Ry!
        nbands:
            number of bands for calculation (see pw.x -> nbnd)
        usesymm:
            whether or not to use symmetry in calculation (True/False)
        maxiter:
            maximum number of iterations in an scf step
        convergence:
            {'energy', <value>} - only one implemented
        kpts:
            (1, 1, 1) - gamma point
            (n1, n2, n3) - Morstead Packing
            [[k1, k2, k3] ... ] - List of kpoints
        prefix: (self.label is meaningless at moment)
            str - string to append to output files
        pseudo:
            {'atom symbol': 'name of pseudo potential', ...} - dictionary of pseudo per atom
        outdir:
            str - relative or absolute path to output directory.
                  Will create it if it does not exist.
        pseudo_dir:
            str - relative or absolute path to pseudo directory.
        occupations:
            str - 'smearing', 'tetrahedra', 'fixed', 'from_input'
        input_dft:
            str - functional to use
        fft_mesh:
            [nr1, nr2, nr3] - fft mesh to use for calculation
        keypairs:
            way to initialize via the base class PWBase. 
            (DO NOT SET CELL OR ATOM POSITIONS via PWBase this is done 
            automagically via ASE Atoms)
        debug:
            if True will print input and output QE files

        AUTOMATICALLY SET KEYPAIRS:
        Set so we can always extract stress and forces:
            control.tstress=True
            control.tprnfor=True
        From atoms object:
            CARD 'CELL PARAMETERS (angstroms)' from atoms.cell
            CARD 'ATOMIC POSITIONS (angstroms)' from atoms[i]
            system.nat from number of unique atoms in atoms[i]
            system.ntyp from len(atoms)
        Thus DO NOT SET:
            A, B, C, cosAB, cosBC, cosAC, celldm(1-6)
            or any of these previously mentioned. Be smart
        """
        Calculator.__init__(self, restart, ignore_bad_restart_file, label, atoms, **kwargs)
        self._pw = None

    def set(self, **kwargs):
        """Sets parameters list and determines if calculation needs to be reset

        *for now it is reset if any parameters change*
        """
        # Read parameters from file
        if 'parameters' in kwargs:
            filename = kwargs.pop('parameters')
            parameters = Parameters.read(filename)
            parameters.update(kwargs)
            kwargs = parameters

        changed_parameters = {}

        for key, value in kwargs.items():
            oldvalue = self.parameters.get(key)
            if key not in self.parameters or not equal(value, oldvalue):
                changed_parameters[key] = value
                self.parameters[key] = value

        if changed_parameters:
            self.reset()

        return changed_parameters

    def reset(self):
        """Clear all information from old calculation."""
        self.results = {}

    def set_atoms(self, atoms):
        self.atoms = atoms.copy()

    def _initialize(self):
        """Updates PWBase to reflect self.parameters and atoms to be ready for a run

        """
        self._pw = PWBase()

        # Setup Unit Cell
        cell = self.atoms.cell
        self._pw.system.add_keypair(('ibrav', 0))
        self._pw.cell_parameters.add_lattice_vec(cell[0], cell[1], cell[2], option='angstrom')

        # Setup Atom Positions
        self._pw.system.add_keypair(('nat', len(self.atoms)))
        for atom in self.atoms:
            self._pw.atomic_positions.add_atom_position(atom.symbol, atom.position, option='angstrom')
        
        # Setup Atom Psuedo Potentials
        from itertools import groupby
        for symbol, atoms in groupby(self.atoms, lambda atom: atom.symbol):
            pseudo_file = self.parameters['pseudo'].get(symbol)
            if pseudo_file:
                self._pw.atomic_species.add_atom_type(symbol, list(atoms)[0].mass, pseudo_file)
            else:
                error_str = "Must provide pseudo potential for all atom types {0}"
                raise Exception(error_str.format(symbol))
        self._pw.system.add_keypair(('ntyp', len(self._pw.atomic_species.atoms)))

        
        # Set User Input Parameters [easy ones first, hard last]
        if self.parameters.get('calculation'):
            self._pw.control.add_keypair(('calculation', self.parameters.get('calculation')))

        if self.parameters.get('ecutwfc'):
            self._pw.system.add_keypair(('ecutwfc', self.parameters['ecutwfc'] / Ry))

        if self.parameters.get('nbands'):
            self._pw.system.add_keypair(('nbnd', self.parameters['nbands']))

        if self.parameters.get('usesymm'):
            self._pw.system.add_keypair(('nosym', not self.parameters['usesymm']))

        if self.parameters.get('maxiter'):
            self._pw.electrons.add_keypair(('electron_maxstep', self.parameters['maxiter']))

        if self.parameters.get('prefix'):
            self._pw.control.add_keypair(('prefix', self.parameters['prefix']))

        if self.parameters.get('outdir'):
            self._pw.control.add_keypair(('outdir', self.parameters['outdir']))

        if self.parameters.get('pseudo_dir'):
            self._pw.control.add_keypair(('pseudo_dir', self.parameters['pseudo_dir']))

        if self.parameters.get('occupations'):
            self._pw.system.add_keypair(('occupations', self.parameters['occupations']))

        if self.parameters.get('input_dft'):
            self._pw.system.add_keypair(('input_dft', self.parameters['input_dft']))

        if self.parameters.get('fft_mesh'):
            fft_mesh = self.parameters['fft_mesh']
            if len(fft_mesh) == 3 and isinstance(fft_mesh[0], int):
                self._pw.system.add_keypairs({'nr1': fft_mesh[0],
                                              'nr2': fft_mesh[1],
                                              'nr3': fft_mesh[2]})
            else:
                error_str = 'FFT MESH = [nr1, nr2, nr3] each is integer'
                raise Exception(error_str)

        if self.parameters.get('convergence'):
            conv = self.parameters['convergence']
            if isinstance(conv, dict):
                if conv.get('energy'):
                    self._pw.electrons.add_keypair(('conv_thr', conv['energy']))
                else:
                    raise Exception('Only energy convergence implemented currently')
            else:
                raise Exception("Unknown convergence format declared (dict required)")

        if self.parameters.get('kpts') is not None:
            kpts = self.parameters['kpts']

            if isinstance(kpts, dict):
                if kpts.get('density'):
                    from ase.calculator.calculator import kptdensity2monkhorstpack
                    self._pw.k_points.from_list(kptdensity2monkhorstpack(
                        kpts['density'], kpts.get('even', False)))
                elif kpts.get('size'):
                    self._pw.k_points.from_monkhorst_pack(
                        kpts['size'], kpts.get('offset', [0, 0, 0]))
                else:
                    raise Exception("Unknown kpts format declared")
            elif hasattr(kpts, "__iter__"):
                self._pw.k_points.from_list(kpts)
            else:
                raise Exception("Unknown kpts format declared")

        # Add all PWBase initializer keypairs [READ VALUES TO NOT SET]
        if self.parameters.get('keypairs'):
            self._pw.add_keypairs_to_namelist(self.parameters['keypairs'])

        # Always calculate stress and strain
        self._pw.control.add_keypairs({
            'tstress': True,
            'tprnfor': True})

        self._pw.validate()

    def _calculate(self, property_name):
        """Preforms calculation.

        """
        if self.parameters.get('debug'):
            return self._pw.run(infile="in", outfile="out", errfile="err")
        return self._pw.run()

    def _calculation_required(self, atoms, property_name):
        # TODO need to do better check on results (using cached idea)
        if self.results == {}:
            return True

        if self.atoms and atoms:
            system_changes = self.check_state(atoms)
            if system_changes:
                return True
        
        return False

    def _set_results(self, results):
        # Extract Results from outfile and convert to appropriate units


        # Updates from output file (some values may not be output!!)
        if self.parameters.get('calculation') != "bands":
            energy = results['calculation']['total energy'] * Ry

            forces = np.array([_[2] for _ in results['calculation']['forces']]) * Ry / Bohr

            stress = np.array(results['calculation']['stress']) * Ry / (Bohr**3)
            # xx, yy, zz, yz, xz, xy
            stress = np.array([stress[0, 0], stress[1, 1], stress[2, 2],
                               stress[1, 2], stress[0, 2], stress[0, 1]])

            self.results.update(
                {'energy': energy,
                 'forces': forces,
                 'stress': stress})

        # Update from data file (guarenteed to be in output)
        fermi_energy = results['data-file']['band-structure-info']['fermi-energy'] * Hartree

        for kpt in results['data-file']['kpoints']:
            kpt['eigenvalues'] = np.array(kpt['eigenvalues']) * Hartree

        self.results.update(
            {'fermi-energy': fermi_energy,
             'xc-functional': results['data-file']['exchange-correlation'],
             'nspins': results['data-file']['band-structure-info']['number spin-components'],
             'nbands': results['data-file']['band-structure-info']['number bands'],
             'charge-density': results['data-file']['charge-density'],
             'ibz-kpoints': results['data-file']['kpoints']})


    @calculation("energy")
    def get_potential_energy(self, atoms=None):
        return self.results['energy']

    @calculation("forces")
    def get_forces(self, atoms=None):
        return self.results['forces']

    @calculation("stress")
    def get_stress(self, atoms=None):
        return self.results['stress']

    @calculation("ibz_kpoint_eigenvalues")
    def get_eigenvalues(self, atoms=None, kpt=0, spin=0):
        if spin == 1:
            raise Exception("Spin systems not implemented yet!")
        return self.results['ibz-kpoints'][kpt]['eigenvalues']

    @calculation("ibz_kpoints_position")
    def get_ibz_k_points(self, atoms=None):
        return np.array([kpoint['coordinate'] for kpoint in self.results['ibz-kpoints']])

    @calculation("ibz_kpoints_weight")
    def get_k_point_weights(self, atoms=None):
        return np.array([kpoint['weight'] for kpoint in self.results['ibz-kpoints']])

    @calculation("nspins")
    def get_number_of_spins(self, atoms=None):
        return self.results['nspins']
    
    @calculation("nbands")
    def get_number_of_bands(self, atoms=None):
        return self.results['nbands']

    @calculation("xc_functional")
    def get_xc_functional(self, atoms=None):
        return self.results['xc_functional']

    @calculation("fermi_energy")
    def get_fermi_level(self, atoms=None):
        return self.results['fermi-energy']
    
    @calculation("pseudo_density")
    def get_pseudo_density(self, atoms=None, spin=None, pad=True):
        if spin != None:
            raise Exception("Spin systems not implemented yet!")
        return self.results['charge-density']

    @calculation("dos")
    def get_dos(self, atoms=None, spin=None, width=0.1, npts=201):
        from ase.dft import DOS
        dos = DOS(self, width=width, npts=npts)
        return dos.get_energies(), dos.get_dos(spin)
Пример #2
0
        'conv_thr': 1E-8,
    }
}

atom_types = [
    ['Si', 28.086, 'Si.pz-vbc.UPF']
]

atom_positions = [
    ['Si', [0.0, 0.0, 0.0]],
    ['Si', [0.25, 0.25, 0.25]]
]

kpoints = (4, 4, 4)

# Create QE Run
qe = PWBase()
qe.add_keypairs_to_namelist(qe_namelist)

for symbol, mass, pseudo in atom_types:
    qe.atomic_species.add_atom_type(symbol, mass, pseudo)

for symbol, position in atom_positions:
    qe.atomic_positions.add_atom_position(symbol, position)

qe.k_points.from_monkhorst_pack([8, 8, 8], [0, 0, 0])

# Validate and Run quantum espresso
qe.validate()
results = qe.run()