예제 #1
0
def gen_perturbations(casfile, supercell='Gamma'):
    """ Genterate input .cell files for CASTEP singlepoint energy simulations
    as part of a phonopy phonon calculation of a solid solution.
    
    str casfile : filename (incl. path) to .castep file to compute phonons of
    np.array(3, 3) supercell : size of real space supercell dictates k-points
    If supercell is 'Gamma', only unit cell displacements are created """
    # Load the mixed data
    chem = casfile.replace('.castep', '')
    cas = rc.readcas(casfile)
    mixatoms = cas.extract_struc()
    spins = cas.get_final_spin()
    press = cas.get_ext_press()

    # Create pure cell
    mixkey = cas.get_mixkey()
    mapping = mixmap.mixmap(mixatoms, mixkey)
    pureatoms = mapping.mix2pure(mixatoms)
    mapping.setcellparams(spins=spins, pressure=press)

    # Generate perturbed cells (displacements) of pure cell
    if supercell == 'Gamma':  # Gamma-point means only single unit cell
        supercell = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    GammaPhonon = phonopy.Phonopy(pureatoms,
                                  supercell,
                                  symprec=1e-4,
                                  factor=phonopy.units.VaspToCm)
    GammaPhonon.generate_displacements(distance=0.05)
    displcells = GammaPhonon.get_supercells_with_displacements()

    # Convert displaced pure cells to mixtures and write files
    for d, displcell in enumerate(displcells):
        mixdispl = mapping.pure2mix(displcells[d])
        cellfile = chem + '_' + str(d) + '.cell'
        mapping.casprint(mixdispl, cellfile)
예제 #2
0
    def __init__(self,
                 phonopy_disp,
                 force_sets,
                 pa='auto',
                 symprec=1e-4,
                 escale='meV'):

        self.cell = phonopy.load(phonopy_disp, primitive_matrix=pa)
        # can we get the primitive matrix here?

        self.phonon = phonopy.Phonopy(
            self.cell.unitcell,
            self.cell.supercell_matrix,
            primitive_matrix=self.cell.primitive_matrix,
            symprec=symprec)

        self.phonon.dataset = phonopy.file_IO.parse_FORCE_SETS(
            filename=force_sets)
        self.phonon.produce_force_constants()

        if escale == 'meV':
            self.scale = THzToEv * 1000
        elif escale == 'THz':
            self.scale = 1

        self.path = None
        self.labels = None
        self.are_bands_computed = False
        self.are_neutron_bands_computed = False
        self.nions = len(self.phonon.primitive.symbols)
예제 #3
0
def get_minimum_displacements(
    unitcell: phonopy.structure.atoms.PhonopyAtoms,
    supercell_matrix: np.ndarray,
    displacement_distance: float,
    phonopy_kw: dict = {},
):
    # note: applying phonopy_kw on load is necessary because phonopy will recompute symmetry
    parprint(f'Getting displacements... ()')
    phonon = phonopy.Phonopy(unitcell,
                             supercell_matrix,
                             factor=phonopy.units.VaspToTHz,
                             **phonopy_kw)
    phonon.generate_displacements(distance=displacement_distance)
    return phonon
예제 #4
0
    def _build_phonopy_object_fhi_aims(self):
        cwd = os.getcwd()
        os.chdir(os.path.dirname(os.path.dirname(self.mainfile)))

        try:
            cell_obj = read_aims('geometry.in')
            self.control_parser.mainfile = 'control.in'
            supercell_matrix = self.control_parser.get('supercell')
            displacement = self.control_parser.get('displacement', 0.001)
            sym = self.control_parser.get('symmetry_thresh', 1e-6)
            try:
                phonopy_obj = phonopy.Phonopy(cell_obj,
                                              supercell_matrix,
                                              symprec=sym)
                phonopy_obj.generate_displacements(distance=displacement)
                supercells = phonopy_obj.get_supercells_with_displacements()
                set_of_forces, relative_paths = read_forces_aims(
                    supercells, logger=self.logger)
            except Exception:
                self.logger.error("Error generating phonopy object.")
                set_of_forces = []
                phonopy_obj = None
                relative_paths = []

            prep_path = self.mainfile.split("phonopy-FHI-aims-displacement-")
            # Try to resolve references as paths relative to the upload root.
            try:
                for path in relative_paths:
                    abs_path = "%s%s" % (prep_path[0], path)
                    rel_path = abs_path.split(nomad.config.fs.staging +
                                              "/")[1].split("/", 3)[3]
                    self.references.append(rel_path)
            except Exception:
                self.logger.warn(
                    "Could not resolve path to a referenced calculation within the upload."
                )

        finally:
            os.chdir(cwd)

        if set_of_forces:
            phonopy_obj.set_forces(set_of_forces)
            phonopy_obj.produce_force_constants()

        self._phonopy_obj = phonopy_obj
예제 #5
0
def get_deperm_from_phonopy_sc_to_ase_sc(natoms, repeats):
    """ Get permutation that maps data for a phonopy supercell into data for an ASE supercell.

    I.e. at index ``ase_index``, the output will hold ``phonopy_index``. """
    import phonopy
    from phonopy.structure.atoms import PhonopyAtoms
    assert np.array(repeats).shape == (3, )

    # Generate a supercell with a bunch of simple integer positions
    unitcell_symbols = ['C'] * natoms
    unitcell_positions = np.outer(np.arange(natoms),
                                  [1, 1, 1])  # [0,0,0], [1,1,1], [2,2,2], ...
    unitcell_lattice = np.eye(3) * natoms
    p_atoms = PhonopyAtoms(symbols=unitcell_symbols,
                           positions=unitcell_positions,
                           cell=unitcell_lattice)
    a_atoms = ase.Atoms(symbols=unitcell_symbols,
                        positions=unitcell_positions,
                        cell=unitcell_lattice)

    # get supercells.  (is_symmetry=False in phonopy is because otherwise it may warn about the reduced symmetry of the supercell)
    a_sc_positions = (a_atoms * repeats).get_positions()
    p_sc_positions = phonopy.Phonopy(
        p_atoms, supercell_matrix=np.diag(repeats),
        is_symmetry=False).supercell.get_positions()

    # Positions had better be integers
    a_sc_positions_int = np.rint(a_sc_positions)
    p_sc_positions_int = np.rint(p_sc_positions)
    np.testing.assert_allclose(a_sc_positions, a_sc_positions_int, atol=1e-8)
    np.testing.assert_allclose(p_sc_positions, p_sc_positions_int, atol=1e-8)
    # Make sure nothing funny happened to atoms within the cell
    assert (a_sc_positions_int[0] == [0, 0, 0]).all()
    assert (p_sc_positions_int[0] == [0, 0, 0]).all()

    # Find how each can be converted into a simple intermediate order.  (lexically sorted)
    deperm_ase_to_lexical = _lexsort_rows(a_sc_positions_int)
    deperm_phonopy_to_lexical = _lexsort_rows(p_sc_positions_int)
    assert (a_sc_positions_int[deperm_ase_to_lexical] ==
            p_sc_positions_int[deperm_phonopy_to_lexical]).all()

    # Compose
    deperm_lexical_to_ase = np.argsort(deperm_ase_to_lexical)
    return deperm_phonopy_to_lexical[deperm_lexical_to_ase]
예제 #6
0
    def run_task(self, fw_spec):
        unitcell = read_vasp("POSCAR-unitcell")
        phonon = phonopy.Phonopy(unitcell, self.get("supercell"))

        supercell = phonon.get_supercell()
        phonon.generate_displacements()
        supercells = phonon.supercells_with_displacements
        ids = np.arange(len(supercells)) + 1
        write_supercells_with_displacements(supercell, supercells, ids)
        units = get_default_physical_units("vasp")
        phpy_yaml = PhonopyYaml(physical_units=units,
                                settings={
                                    'force_sets': False,
                                    'born_effective_charge': False,
                                    'dielectric_constant': False,
                                    'displacements': True
                                })
        phpy_yaml.set_phonon_info(phonon)
        with open("phonopy_disp.yaml", 'w') as w:
            w.write(str(phpy_yaml))
예제 #7
0
def get_minimum_displacements(
        cachepath: str,
        unitcell: phonopy.structure.atoms.PhonopyAtoms,
        supercell_matrix: np.ndarray,
        displacement_distance: float,
        phonopy_kw: dict = {},
        ):
    # note: applying phonopy_kw on load is necessary because phonopy will recompute symmetry
    load = lambda: phonopy.load(cachepath, produce_fc=False, **phonopy_kw)
    if os.path.exists(cachepath):
        parprint(f'Found existing {cachepath}')
        return load()
    world.barrier()  # avoid race condition where rank 0 creates file before others enter
    parprint(f'Getting displacements... ({cachepath})')

    if world.rank == 0:
        phonon = phonopy.Phonopy(unitcell, supercell_matrix, factor=phonopy.units.VaspToTHz, **phonopy_kw)
        phonon.generate_displacements(distance=displacement_distance)
        parprint(f'Saving displacements...')
        phonon.save(cachepath)

    world.barrier()
    parprint(f'Loading displacements...')
    return load()
예제 #8
0
def phonon(lammps_command,
           ucell,
           potential,
           mpi_command=None,
           a_mult=3,
           b_mult=3,
           c_mult=3,
           distance=0.01,
           symprec=1e-5):

    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']

    # Generate pair_info
    pair_info = potential.pair_info(ucell.symbols)

    # Use spglib to find primitive unit cell of ucell
    convcell = ucell.dump('spglib_cell')
    primcell = spglib.find_primitive(convcell, symprec=symprec)
    primucell = am.load('spglib_cell', primcell,
                        symbols=ucell.symbols).normalize()

    # Initialize Phonopy object
    phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms'),
                             [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]])
    phonon.generate_displacements(distance=distance)

    # Loop over displaced supercells to compute forces
    forcearrays = []
    for supercell in phonon.supercells_with_displacements:

        # Save to LAMMPS data file
        system = am.load('phonopy_Atoms', supercell)
        system_info = system.dump('atom_data', f='disp.dat')

        # Define lammps variables
        lammps_variables = {}
        lammps_variables['atomman_system_info'] = system_info
        lammps_variables['atomman_pair_info'] = pair_info

        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables[
                'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'

        # Write lammps input script
        template_file = Path(script_dir, 'phonon.template')
        lammps_script = 'phonon.in'
        with open(template_file) as f:
            template = f.read()
        with open(lammps_script, 'w') as f:
            f.write(
                iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

        # Run LAMMPS
        lmp.run(lammps_command, 'phonon.in', mpi_command=mpi_command)

        # Extract forces from dump file
        results = am.load('atom_dump', 'forces.dump')
        forces = uc.set_in_units(results.atoms.force, lammps_units['force'])
        forcearrays.append(forces)

    # Set computed forces
    phonon.set_forces(forcearrays)

    # Save to yaml file
    phonon.save('phonopy_params.yaml')

    # Compute band structure
    phonon.produce_force_constants()
    phonon.auto_band_structure(plot=True)

    plt.savefig(Path('.', 'band.png'), dpi=400)
    plt.close()

    # Compute total density of states
    phonon.auto_total_dos(plot=True)
    plt.savefig('total_dos.png', dpi=400)
    plt.close()

    # Compute partial density of states
    phonon.auto_projected_dos(plot=True)
    plt.savefig('projected_dos.png', dpi=400)
    plt.close()

    # Compute thermal properties
    phonon.run_thermal_properties()
    phonon.plot_thermal_properties()
    plt.savefig('thermal.png', dpi=400)
    plt.close()

    return {}
예제 #9
0
def analyze_phonons(m_data):
    THz_per_invcm = ase.units._c * 1.0e-10

    at0 = ase.atoms.Atoms(symbols = m_data['symb'], 
        scaled_positions = np.asarray(m_data['scaled_pos']),
        masses = np.asarray(m_data['m']), cell = np.asarray(m_data['c']), pbc=[True]*3)
    phonopy_atoms = phonopy.structure.atoms.PhonopyAtoms(symbols = m_data['symb'], 
        scaled_positions = np.asarray(m_data['scaled_pos']),
        masses = np.asarray(m_data['m']), cell = np.asarray(m_data['c']))
    phonons = phonopy.Phonopy(phonopy_atoms, m_data['n_cell'], factor=m_data['unit_factor'])
    phonons.generate_displacements(distance=m_data['dx'])

    phonons.produce_force_constants(forces=np.asarray(m_data['all_forces']), calculate_full_force_constants=True )
    phonons.symmetrize_force_constants()

    # DOS
    n_dos_mesh = 16
    phonons.run_mesh( [n_dos_mesh]*3 )

    phonons.run_total_dos()
    frequencies = phonons.get_total_dos_dict()['frequency_points']
    PHdos = phonons.get_total_dos_dict()['total_dos']/len(at0)

    #contains by columns the frequencies in cm^{-1} and the vDOS
    #the vDOS ins in units of "number of states/(unit cell x frequency[cm^{-1}])" 
    # i.e. if you integrate the vDOS throughout frequency, it will be 3N, where N is the number of atoms in the unit cell
    m_data['DOS'] = { 'freq' : frequencies/THz_per_invcm, 'val' : PHdos*THz_per_invcm }

    # band path
    if 'band_path' in m_data:
        band_path = m_data['band_path']
        band_n = [20]

        lat = at0.get_cell().get_bravais_lattice()
        special_points = lat.get_special_points()
        path_pts = []
        path_labels = []
        pt = []
        for s in " ".join(band_path).split():
            try:
                qi = float(s)
                pt.append(qi)
            except:
                if len(pt) > 0:
                    raise RuntimeError("got non-float after 1 or more floats")
                for p in s:
                    try:
                        path_pts.append(np.asarray(special_points[p]))
                        path_labels.append(p)
                    except KeyError:
                        raise RuntimeError("Failed to find special point {}, known {}".format(p, special_points.keys()))
            if len(pt) == 3:
                path_pts.append(np.asarray(pt))
                path_labels.append("{:.2f}_{:.2f}_{:.2f}".format(*pt))
                pt = []

        if len(band_n) == 1:
            band_n *= len(path_pts)-1
        assert len(band_n) == len(path_pts)-1

        q_pts = []
        q_pt_labels = []
        for (seg_i, (label, n, p0, p1)) in enumerate(zip(path_labels[0:-1], band_n, path_pts[0:-1], path_pts[1:])):
            for p_i in range(n):
                x = float(p_i)/float(n)

                q_pts.append ( (1.0-x) * p0 + x*p1 )

                if p_i == 0:
                    q_pt_labels.append(label)
                else:
                    q_pt_labels.append(".")

        q_pts.append(path_pts[-1])
        q_pt_labels.append(path_labels[-1])

        phonons.run_band_structure([q_pts])
        bs = phonons.get_band_structure_dict()
        m_data['BAND_PATH'] = {
            'positions' : bs['distances'][0],
            'frequencies' : bs['frequencies'][0]/THz_per_invcm,
            'labels' : q_pt_labels }
예제 #10
0
def calc_phonons(casfile,
                 supercell='Gamma',
                 method=2,
                 postol=0.001,
                 bands=None,
                 verbose=False):
    """ Compute phonons from completed singlepoint CASTEP calculations of
    perturbations about a relaxed cell.
    Note that this function assumes that the perturbed files are in the
    same directory and use the same naming convention as the parent.

    str casfile : filename (incl. path) to .castep file to compute phonons of
    np.array(3, 3) supercell : size of real space supercell dictates k-points
    If supercell is 'Gamma', only unit cell displacements are created.
    int method : can be 1 (regenerate displacements and assume the same) or
    2 (process all castep files that follow displacement naming convention)
    float postol : Tolerance (Ang) when ion considered displaced (2 only)
    np.array() bands : k-points to compute frequencies at
    bool verbose : function can take a while to run -> prints checkpoints.
    
    returns
    tuple (q_points, distances, frequencies, eigvecs) : output of
    phonopy_instance.get_band_structure() command.
    These are all lists of lists of np.arrays, where the relavent entry of
    frequencies is a list of freqs (in cm-1) of the form np.array(3*phonions)
    and the relevant entry of eigvecs is a matrix of phonon eigenvectors of 
    the form np.array(3*phonions, 3*phonions). """
    # Set up the PHONOPY object for the relaxed cell
    chem = casfile.replace('.castep', '')
    cas = rc.readcas(casfile)
    print("\nreading " + casfile + "\n")
    mixatoms = cas.extract_struc()
    mixkey = cas.get_mixkey()
    mapping = mixmap.mixmap(mixatoms, mixkey)
    pureatoms = mapping.mix2pure(mixatoms)
    pureions = pureatoms.get_number_of_atoms()
    if supercell == 'Gamma':  # Gamma-point means only single unit cell
        supercell = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
        bands = [np.array([0.0])]
    PhononObj = phonopy.Phonopy(pureatoms,
                                supercell,
                                factor=phonopy.units.VaspToCm)

    if method == 1:
        """ Generate more displs and ASSUME that they are the same as before.
        Less stable than method 2 and should only be used as a check. """
        # Generate perturbed pure cells
        PhononObj.generate_displacements(distance=0.05)
        displcells = PhononObj.get_supercells_with_displacements()
        Ndispls = len(displcells)
        sets_of_forces = np.zeros((Ndispls, pureions, 3))
        for d in range(Ndispls):
            displcasfile = chem + '_' + str(d) + '.castep'
            if verbose:
                print("reading " + displcasfile)
            displmix = rc.readcas(displcasfile)
            displmixforces = displmix.get_forces()
            displpureforces = mapping.mix2pure_forces(displmixforces)
            sets_of_forces[d] = displpureforces
            PhononObj.set_forces(sets_of_forces)

    elif method == 2:
        """ Load all the displaced structures and create a displacement_dataset
        object to load to the PHONOPY instance. """
        # Detect displacement files
        allcasfiles = glob.glob(chem + '_*.castep')
        displcasfiles = []
        for casfile in allcasfiles:
            n = casfile.replace(chem + '_', '').replace('.castep', '')
            try:
                int(n)
                displcasfiles.append(casfile)
            except ValueError:
                pass  # file doesn't follow displacement naming convention
        Ndispls = len(displcasfiles)
        first_atoms_list = []
        pureposns = pureatoms.get_positions()

        for d in range(Ndispls):
            # Load displacement files and compute displ relative to parent
            displcasfile = chem + '_' + str(d) + '.castep'
            if verbose:
                print("reading " + displcasfile)
            displmix = rc.readcas(displcasfile)
            displmixatoms = displmix.extract_struc()
            displpureatoms = mapping.mix2pure(displmixatoms)
            displpureatoms = pinpos.reorder_atoms(pureatoms, displpureatoms)
            puretotaldispl = displpureatoms.get_positions() - pureposns

            # Check that exactly one ion is displaced per perturbed cell
            displions = []
            for i in range(pureions):
                displmagnitude = np.linalg.norm(puretotaldispl[i, :])
                if displmagnitude > postol:
                    displions.append(i)
            if len(displions) > 1:
                raise IndexError('The following ions were all displaced by ' +
                                 'more than ' + str(postol) + ': ' +
                                 ' '.join([str(i) for i in displions]))
            elif not displions:
                raise IndexError('No ions were found to be displaced by ' +
                                 'more than ' + str(postol))
            displion = displions[0]

            # Extract (pure) forces and construct first_atoms_list
            # this list is later fed to construct displacement_dataset
            displmixforces = displmix.get_forces()
            displpureforces = mapping.mix2pure_forces(displmixforces)
            first_atoms_dict = {}
            first_atoms_dict['number'] = displion
            first_atoms_dict['displacement'] = puretotaldispl[displion, :]
            first_atoms_dict['forces'] = displpureforces
            first_atoms_list += [first_atoms_dict]

        # Now construct displacement_dataset (for all perturbations)
        displacement_dataset = {}
        displacement_dataset['natom'] = pureions
        displacement_dataset['first_atoms'] = first_atoms_list
        PhononObj.set_displacement_dataset(displacement_dataset)
    # End of Method 2

    # Forces & displacements are now set so may compute the force constants
    if verbose:
        print("\nConstructing force constant matrix")
    PhononObj.produce_force_constants()
    phonopy.harmonic.force_constants.set_translational_invariance(
        PhononObj.force_constants)
    FCs = PhononObj.force_constants
    (FC1, FC2, FC3, FC4) = FCs.shape
    PhononObj.set_force_constants(FCs)
    # Force constants at this point ALREADY obey the ASR near perfectly
    # (i.e. columns/rows sum to zero)

    # Compute frequencies from dynamical matrix
    if verbose:
        print("\nDiagonalising dynamical matrix")
    PhononObj.set_band_structure(bands, is_eigenvectors=True)
    return PhononObj.get_band_structure()
예제 #11
0
def phononcalc(lammps_command,
               ucell,
               potential,
               mpi_command=None,
               a_mult=3,
               b_mult=3,
               c_mult=3,
               distance=0.01,
               symprec=1e-5,
               savefile='phonopy_params.yaml',
               plot=True,
               lammps_date=None):
    """
    Uses phonopy to compute the phonons for a unit cell structure using a
    LAMMPS interatomic potential.

    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    ucell : atomman.System
        The unit cell system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    a_mult : int, optional
        The a size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    b_mult : int, optional
        The b size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    c_mult : int, optional
        The c size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    distance : float, optional
        The atomic displacement distance used for computing the phonons.
        Default value is 0.01.
    symprec : float, optional
        Absolute length tolerance to use in identifying symmetry of atomic
        sites and system boundaries. Default value is 1e-5.
    savefile: str, optional
        The name of the phonopy yaml backup file.  Default value is
        'phonopy_params.yaml'.
    plot : bool, optional
        Flag indicating if band structure and DOS figures are to be generated.
        Default value is True.
    lammps_date : datetime.date, optional
        The version date associated with lammps_command.  If not given, the
        version will be identified.
    """
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Get lammps version date
    if lammps_date is None:
        lammps_date = lmp.checkversion(lammps_command)['date']

    # Use spglib to find primitive unit cell of ucell
    convcell = ucell.dump('spglib_cell')
    primcell = spglib.find_primitive(convcell, symprec=symprec)
    primucell = am.load('spglib_cell', primcell,
                        symbols=ucell.symbols).normalize()

    # Initialize Phonopy object
    phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms',
                                            symbols=potential.elements(
                                                primucell.symbols)),
                             [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]],
                             factor=phonopy.units.VaspToTHz)
    phonon.generate_displacements(distance=distance)

    # Loop over displaced supercells to compute forces
    forcearrays = []
    for supercell in phonon.supercells_with_displacements:

        # Save to LAMMPS data file
        system = am.load('phonopy_Atoms', supercell, symbols=primucell.symbols)
        system_info = system.dump('atom_data',
                                  f='disp.dat',
                                  potential=potential)

        # Define lammps variables
        lammps_variables = {}
        lammps_variables['atomman_system_pair_info'] = system_info

        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables[
                'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'

        # Write lammps input script
        lammps_script = 'phonon.in'
        template = read_calc_file('iprPy.calculation.phonon',
                                  'phonon.template')
        with open(lammps_script, 'w') as f:
            f.write(filltemplate(template, lammps_variables, '<', '>'))

        # Run LAMMPS
        lmp.run(lammps_command,
                script_name=lammps_script,
                mpi_command=mpi_command)

        # Extract forces from dump file
        forcestructure = am.load('atom_dump', 'forces.dump')
        forces = uc.set_in_units(forcestructure.atoms.force,
                                 lammps_units['force'])
        forcearrays.append(forces)

    results = {}

    # Set computed forces
    phonon.set_forces(forcearrays)

    # Save to yaml file
    phonon.save(savefile)

    # Compute band structure
    phonon.produce_force_constants()
    phonon.auto_band_structure(plot=plot)
    results['band_structure'] = phonon.get_band_structure_dict()
    if plot:
        plt.ylabel('Frequency (THz)')
        plt.savefig(Path('.', 'band.png'), dpi=400)
        plt.close()

    # Compute density of states
    phonon.auto_total_dos(plot=False)
    phonon.auto_projected_dos(plot=False)
    dos = phonon.get_total_dos_dict()
    dos['frequency'] = uc.set_in_units(dos.pop('frequency_points'), 'THz')
    dos['projected_dos'] = phonon.get_projected_dos_dict()['projected_dos']
    results['dos'] = dos

    # Compute thermal properties
    phonon.run_thermal_properties()
    results['thermal_properties'] = phonon.get_thermal_properties_dict()

    results['phonon'] = phonon
    return results
예제 #12
0
def write_displacement_matrix(poscar_file, fcs_file, T, n, use_smalldisp,
                              imaginary_freq, grid):
    """
    Write the displacement matrix depending of T, force constants and
    structure, integrated over the Brillouin zone.
    """

    SYMPREC = 1e-5
    NQ = grid

    if not os.path.isfile(poscar_file):
        sys.exit("The specified POSCAR file does not exist.")
    na, nb, nc = [int(i) for i in n]
    if min(na, nb, nc) < 1:
        sys.exit("All dimensions must be positive integers")

    poscar = generate_conf.read_POSCAR(poscar_file)
    natoms = poscar["numbers"].sum()
    ntot = na * nb * nc * natoms
    nmodes = 3 * natoms
    ncells = na * nb * nc
    matrix = np.zeros((ncells * nmodes, ncells * nmodes), dtype=np.complex128)
    local_matrix = np.zeros((ncells * nmodes, ncells * nmodes),
                            dtype=np.complex128)

    if use_smalldisp:
        for idiag in range(ncells * nmodes):
            matrix[idiag, idiag] = 0.000001
        return matrix

    if not os.path.isfile(fcs_file):
        sys.exit("The specified FORCE_CONSTANTS file does not exist.")

    supercell_matrix = np.diag([na, nb, nc])

    structure = phonopy.interface.read_crystal_structure(poscar_file,
                                                         "vasp")[0]
    fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file)

    phonon = phonopy.Phonopy(
        structure,
        supercell_matrix,
        primitive_matrix=None,
        factor=phonopy.units.VaspToTHz,
        dynamical_matrix_decimals=None,
        force_constants_decimals=None,
        symprec=SYMPREC,
        is_symmetry=True,
        log_level=0)
    if os.path.isfile("BORN"):
        nac_params = phonopy.file_IO.get_born_parameters(
            open("BORN"), phonon.get_primitive(),
            phonon.get_primitive_symmetry())
        phonon.set_nac_params(nac_params=nac_params)

    phonon.set_force_constants(fc)

    masses = phonon.get_supercell().get_masses(
    ) * codata.physical_constants["atomic mass constant"][0]

    qpoints = create_mesh(NQ)
    nqpoints = qpoints.shape[0]

    # Initializations and preliminaries
    comm = MPI.COMM_WORLD  # get MPI communicator object
    size = comm.size  # total number of processes
    rank = comm.rank  # rank of this process

    full_qlist = list(range(nqpoints))
    qlist = full_qlist[rank::size]

    for qpt in qlist:
        local_matrix += qpoint_worker((qpoints[qpt, :], phonon, T, na, nb, nc,
                                       imaginary_freq, poscar["positions"]))

    # Reduce all qpoints
    comm.Reduce(local_matrix, matrix, op=MPI.SUM, root=0)

    if rank == 0:
        matrix *= codata.hbar / 2. / nqpoints / 2. / np.pi / 1e12  # m**2
        for i, j in itertools.product(
                range(ncells * nmodes), range(ncells * nmodes)):
            matrix[i, j] /= np.sqrt(masses[i // 3] * masses[j // 3])

        matrix = 1e18 * matrix.real  # nm**2
    comm.Barrier()
    return matrix
예제 #13
0
def write_displacement_matrix_gamma(sposcar_file, fcs_file, T, n,
                                    use_smalldisp, imaginary_freq):
    """
    Writes the displacement matrix depending of T, force constants and
    structure, using only the gamma-point of the supercell.
    """

    SYMPREC = 1e-5

    if not os.path.isfile(sposcar_file):
        sys.exit("The specified POSCAR file does not exist.")
    na, nb, nc = [1, 1, 1]

    poscar = generate_conf.read_POSCAR(sposcar_file)
    natoms = poscar["numbers"].sum()
    ntot = na * nb * nc * natoms
    nmodes = 3 * natoms
    ncells = na * nb * nc
    matrix = np.zeros((ncells * nmodes, ncells * nmodes), dtype=np.complex128)

    if use_smalldisp:
        for idiag in range(ncells * nmodes):
            matrix[idiag, idiag] = 0.000001
        return matrix

    if not os.path.isfile(fcs_file):
        sys.exit("The specified FORCE_CONSTANTS file does not exist.")

    supercell_matrix = np.diag([na, nb, nc])

    structure = phonopy.interface.read_crystal_structure(sposcar_file,
                                                         "vasp")[0]
    fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file)

    phonon = phonopy.Phonopy(
        structure,
        supercell_matrix,
        primitive_matrix=None,
        factor=phonopy.units.VaspToTHz,
        dynamical_matrix_decimals=None,
        force_constants_decimals=None,
        symprec=SYMPREC,
        is_symmetry=True,
        log_level=0)
    if os.path.isfile("BORN"):
        nac_params = phonopy.file_IO.get_born_parameters(
            open("BORN"), phonon.get_primitive(),
            phonon.get_primitive_symmetry())
        phonon.set_nac_params(nac_params=nac_params)

    phonon.set_force_constants(fc)

    masses = phonon.get_supercell().get_masses(
    ) * codata.physical_constants["atomic mass constant"][0]

    nqpoints = 1
    print("nqpoints: " + repr(nqpoints))

    matrix = qpoint_worker(([0., 0., 0.], phonon, T, na, nb, nc,
                            imaginary_freq, poscar["positions"]))

    matrix *= codata.hbar / 2. / nqpoints / 2. / np.pi / 1e12  # m**2
    for i, j in itertools.product(
            range(ncells * nmodes), range(ncells * nmodes)):
        matrix[i, j] /= np.sqrt(masses[i // 3] * masses[j // 3])

    matrix = 1e18 * matrix.real  # nm**2
    return matrix
예제 #14
0
def write_mode_gruneisen_gamma(poscar_file, sposcar_file, n, fcs_file,
                               fcs_3rd_file, imaginary_freq, filename):
    """
    Write the gruneisen parameters depending of T, force constants and
    structure, using only the gamma point of the supercell.
    """

    SYMPREC = 1e-5
    NQ = 1

    if not os.path.isfile(poscar_file):
        sys.exit("Gruneisen: the specified POSCAR file does not exist.")

    poscar = generate_conf.read_POSCAR(poscar_file)
    sposcar = generate_conf.read_POSCAR(sposcar_file)
    corresp = symmetry.calc_corresp(poscar, sposcar, n)
    ncells = n[0] * n[1] * n[2]
    natoms = poscar["numbers"].sum()
    nmodes = 3 * natoms * ncells

    supercell_matrix = np.diag([1, 1, 1])

    if not os.path.isfile(fcs_file):
        sys.exit("The specified FORCE_CONSTANTS file does not exist.")
    if not os.path.isfile(fcs_3rd_file):
        sys.exit("The specified FORCE_CONSTANTS_3RD file does not exist.")

    fcs_3rd, atom_info, R_j, R_k, abc = read_3rd_fcs_asinfile(
        fcs_3rd_file, poscar_file)
    nblocks = len(fcs_3rd)
    poscar_positions = sp.dot(poscar["lattvec"], poscar["positions"]).T * 10.
    cartesian_positions = np.array([
        sp.dot(poscar["lattvec"], R_k[iblock])[abc[iblock, 2]] * 10. +
        poscar_positions[atom_info[iblock, 2], abc[iblock, 2]]
        for iblock in range(nblocks)
    ])

    structure = phonopy.interface.read_crystal_structure(sposcar_file,
                                                         "vasp")[0]
    fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file)

    phonon = phonopy.Phonopy(structure,
                             supercell_matrix,
                             primitive_matrix=None,
                             factor=phonopy.units.VaspToTHz,
                             dynamical_matrix_decimals=None,
                             force_constants_decimals=None,
                             symprec=SYMPREC,
                             is_symmetry=True,
                             log_level=0)
    if os.path.isfile("BORN"):
        nac_params = phonopy.file_IO.get_born_parameters(
            open("BORN"), phonon.get_primitive(),
            phonon.get_primitive_symmetry())
        phonon.set_nac_params(nac_params=nac_params)

    phonon.set_force_constants(fc)

    masses = phonon.get_unitcell().get_masses()
    massesi = np.array([
        masses[corresp.index([atom_info[iblock, 0], 0, 0, 0])]
        for iblock in range(nblocks)
    ])
    massesj = np.array([
        masses[corresp.index([atom_info[iblock, 1], 0, 0, 0])]
        for iblock in range(nblocks)
    ])

    print("nqpoints: " + str(NQ))
    mesh = [[0., 0., 0.]]

    w_grun = []
    m_grun = []
    f_grun = []
    tot_cv = 0.
    r = range(nmodes)
    for q in mesh:
        f, psi = phonon.get_frequencies_with_eigenvectors(q)
        factorj = 1.0
        for im in r:
            if (f[im] < -1.e-4):
                print("ATTENTION: IMAGINARY FREQUENCIES ->"
                      " CONVERTED TO POSITIVE VALUE")
                if imaginary_freq == -1:
                    f[im] = abs(f[im])
                else:
                    f[im] = imaginary_freq
            if (f[im] > 1.e-4):
                lpsi = psi[:, im].reshape(-1, 3)
                psii = np.conj(
                    np.array([
                        lpsi[corresp.index([atom_info[iblock, 0], 0, 0, 0]),
                             abc[iblock, 0]] for iblock in range(nblocks)
                    ]))
                psij = np.array([
                    lpsi[corresp.index([
                        atom_info[iblock, 1], R_j[iblock, 0] %
                        n[0], R_j[iblock, 1] % n[1], R_j[iblock, 2] % n[2]
                    ]), abc[iblock, 1]] for iblock in range(nblocks)
                ])
                m_gruni = mode_gruneisen(f[im], psii, psij, massesi, massesj,
                                         cartesian_positions, fcs_3rd,
                                         factorj).real * ncells
                m_grun.append(m_gruni)
                f_grun.append(f[im])
            else:
                m_grun.append(np.array([0., 0., 0.]))
                f_grun.append(f[im])

    with open(filename, 'w') as f:
        for i in range(len(f_grun)):
            f.write(
                str(f_grun[i]) + '  ' + str(m_grun[i][0]) + '  ' +
                str(m_grun[i][1]) + '  ' + str(m_grun[i][2]) + '\n')

    m_grun = np.array(m_grun)
    f_grun = np.array(f_grun)
    return f_grun, m_grun
예제 #15
0
def write_mode_gruneisen(poscar_file, n, fcs_file, fcs_3rd_file,
                         imaginary_freq, grid, filename):
    """
    Write the gruneisen parameters depending of T, force constants and
    structure, in the whole Brillouin zone.
    """
    SYMPREC = 1e-5

    if not os.path.isfile(poscar_file):
        sys.exit("Gruneisen: the specified POSCAR file does not exist.")

    poscar = generate_conf.read_POSCAR(poscar_file)
    ncells = n[0] * n[1] * n[2]
    natoms = poscar["numbers"].sum()
    nmodes = 3 * natoms

    supercell_matrix = np.diag([n[0], n[1], n[2]])

    if not os.path.isfile(fcs_file):
        sys.exit("The specified FORCE_CONSTANTS file does not exist.")
    if not os.path.isfile(fcs_3rd_file):
        sys.exit("The specified FORCE_CONSTANTS_3RD file does not exist.")

    fcs_3rd, atom_info, R_j, R_k, abc = read_3rd_fcs_asinfile(
        fcs_3rd_file, poscar_file)
    nblocks = len(fcs_3rd)
    poscar_positions = sp.dot(poscar["lattvec"], poscar["positions"]).T * 10.
    cartesian_positions = np.array([
        sp.dot(poscar["lattvec"], R_k[iblock])[abc[iblock, 2]] * 10. +
        poscar_positions[atom_info[iblock, 2], abc[iblock, 2]]
        for iblock in range(nblocks)
    ])

    structure = phonopy.interface.read_crystal_structure(poscar_file,
                                                         "vasp")[0]
    fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file)

    phonon = phonopy.Phonopy(structure,
                             supercell_matrix,
                             primitive_matrix=None,
                             factor=phonopy.units.VaspToTHz,
                             dynamical_matrix_decimals=None,
                             force_constants_decimals=None,
                             symprec=SYMPREC,
                             is_symmetry=True,
                             log_level=0)
    if os.path.isfile("BORN"):
        nac_params = phonopy.file_IO.get_born_parameters(
            open("BORN"), phonon.get_primitive(),
            phonon.get_primitive_symmetry())
        phonon.set_nac_params(nac_params=nac_params)

    phonon.set_force_constants(fc)

    masses = phonon.get_unitcell().get_masses()
    massesi = masses[atom_info[:, 0]]
    massesj = masses[atom_info[:, 1]]

    NQ = grid
    mesh = create_mesh(NQ)
    nqpoints = mesh.shape[0]

    m_grun = []
    f_grun = []
    r = range(nmodes)

    # Initializations and preliminaries
    comm = MPI.COMM_WORLD  # get MPI communicator object
    size = comm.size  # total number of processes
    rank = comm.rank  # rank of this process

    # Scatter qpoints across cores.
    full_qlist = list(range(nqpoints))
    qlist = full_qlist[rank::size]

    for qpt in qlist:
        q = mesh[qpt, :]
        f, psi = phonon.get_frequencies_with_eigenvectors(q)
        factor = np.exp(2j * np.pi * np.dot(q, poscar["positions"]))
        factorj = np.exp(2j * np.pi * np.dot(R_j, q))
        for im in r:
            if (f[im] < -1.e-4):
                print("ATTENTION: IMAGINARY FREQUENCIES ->"
                      " CONVERTED TO POSITIVE VALUE")
                if imaginary_freq == -1:
                    f[im] = abs(f[im])
                else:
                    f[im] = imaginary_freq
            if (f[im] > 1.e-4):
                lpsi = psi[:, im].reshape(-1, 3) * factor[:, np.newaxis]
                psii = np.conj(
                    np.array([
                        lpsi[atom_info[iblock, 0], abc[iblock, 0]]
                        for iblock in range(nblocks)
                    ]))
                psij = np.array([
                    lpsi[atom_info[iblock, 1], abc[iblock, 1]]
                    for iblock in range(nblocks)
                ])
                m_gruni = mode_gruneisen(f[im], psii, psij, massesi, massesj,
                                         cartesian_positions, fcs_3rd,
                                         factorj).real
                m_grun.append(m_gruni)
                f_grun.append(f[im])
            else:
                m_grun.append(np.array([0., 0., 0.]))
                f_grun.append(f[im])
    m_grun = np.array(m_grun)
    f_grun = np.array(f_grun)

    # Gather all qpoints
    m_grun = comm.gather(m_grun, root=0)
    f_grun = comm.gather(f_grun, root=0)

    if rank == 0:
        m_grun = np.concatenate(m_grun)
        f_grun = np.concatenate(f_grun)
        with open(filename, 'w') as f:
            for i in range(f_grun.shape[0]):
                f.write(
                    str(f_grun[i]) + '  ' + str(m_grun[i][0]) + '  ' +
                    str(m_grun[i][1]) + '  ' + str(m_grun[i][2]) + '\n')

    m_grun = comm.bcast(m_grun, root=0)
    f_grun = comm.bcast(f_grun, root=0)

    return f_grun, m_grun
예제 #16
0
def do_phonons(bulk_struct_tests, n_supercell, band_paths=None, dx=0.01):
    if band_paths is not None and len(band_paths) != len(bulk_struct_tests):
        raise RuntimeError(
            "got {} bulk structs but different {} band paths".format(
                len(bulk_struct_tests), len(band_paths)))
    properties = {}
    for bulk_i, bulk_struct_test in enumerate(bulk_struct_tests):
        at0 = get_relaxed_bulk(bulk_struct_test)

        # magnetic moments could change the symmetry, ignored here for now
        phonopy_atoms = phonopy.structure.atoms.PhonopyAtoms(
            symbols=at0.get_chemical_symbols(),
            scaled_positions=at0.get_scaled_positions(),
            masses=at0.get_masses(),
            cell=at0.get_cell())

        phonons = phonopy.Phonopy(phonopy_atoms,
                                  np.diag([n_supercell] * 3),
                                  factor=phonopy.units.VaspToTHz)
        phonons.generate_displacements(distance=dx)

        # convert from chosen Phonopy units (THz) to cm^-1
        THz_per_invcm = ase.units._c * 1.0e-10

        ####################################################################################################
        # if args.SETUP:
        sys.stderr.write("SETUP\n")

        displ_supercells = phonons.get_supercells_with_displacements()

        at0_sc = at0 * n_supercell
        # reorder at0_sc to match order from phonopy
        at = ase.Atoms(pbc=True,
                       cell=displ_supercells[0].get_cell(),
                       positions=displ_supercells[0].get_positions(),
                       numbers=displ_supercells[0].get_atomic_numbers())
        matched_pos = np.zeros(at.positions.shape)
        mapping = [-1] * len(at)
        at0_sc_scaled_pos = at0_sc.get_scaled_positions()
        at_scaled_pos = at.get_scaled_positions()
        for at0_sc_i in range(len(at)):
            scaled_dists = at0_sc_scaled_pos[at0_sc_i] - at_scaled_pos
            scaled_dists -= np.round(scaled_dists)
            closest_i = np.argmin(np.linalg.norm(scaled_dists, axis=1))
            matched_pos[at0_sc_i] = at.positions[closest_i]
            mapping[closest_i] = at0_sc_i
        if -1 in mapping:
            raise RuntimeError(
                "Failed to map orig and displaced atom positions")
        at0_sc = at0_sc[mapping]

        # ase.io.write("UNDISPL.{}".format(file_label), at0_sc)

        # create displaced cells
        sys.stderr.write("Creating {} displacements\n".format(
            len(displ_supercells)))
        at_sets = []
        for (displ_i, displ_config) in enumerate(displ_supercells):
            at = ase.Atoms(pbc=True,
                           cell=displ_config.get_cell(),
                           positions=displ_config.get_positions(),
                           numbers=displ_config.get_atomic_numbers())
            at_sets.append(at)
            # ase.io.write("DISPL_{}.{}".format(displ_i, file_label), at)

        ####################################################################################################
        all_forces = []
        # if args.CALCULATE:
        sys.stderr.write("CALCULATE\n")

        sys.stderr.write("Calculating for {} displacements\n".format(
            len(at_sets)))

        evaluate(at0_sc)
        f0 = at0_sc.get_forces()
        # np.savetxt("FORCES.UNDISPL.{}".format(file_label), f0)

        for (displ_i, at) in enumerate(at_sets):
            at0_sc.set_positions(at.positions)
            at0_sc.set_cell(at.get_cell())
            sys.stderr.write(
                "start evaluate displacement {}\n".format(displ_i))
            evaluate(at0_sc)
            f = at0_sc.get_forces()
            all_forces.append(f)
            # np.savetxt("FORCES.DISPL_{}.{}".format(displ_i, file_label), all_forces[-1])

        ####################################################################################################
        # if args.PROCESS:
        sys.stderr.write("PROCESS\n")

        Nat = all_forces[0].shape[0]
        for f in all_forces:
            f -= f0
            f -= np.outer(np.ones(Nat), np.sum(f, axis=0) / Nat)
        all_forces = np.asarray(all_forces)

        properties[bulk_struct_test] = {
            'dx': dx,
            'all_forces': all_forces.tolist(),
            'symb': at0.get_chemical_symbols(),
            'scaled_pos': at0.get_scaled_positions().tolist(),
            'm': at0.get_masses().tolist(),
            'c': at0.get_cell().tolist(),
            'n_cell': np.diag([n_supercell] * 3).tolist(),
            'unit_factor': phonopy.units.VaspToTHz,
            'band_path': band_paths[bulk_i]
        }

        ####################################################################################################

    return properties