def write_force_constants(self, fc_format='hdf5'): if self.fc is not None: if fc_format == 'hdf5': p2s_map = self.primitive.get_primitive_to_supercell_map() write_force_constants_to_hdf5(self.fc, p2s_map=p2s_map) else: write_FORCE_CONSTANTS(self.fc)
def get_FC_constant_pressure(self): temperatures = self.get_qha_temperatures() volumes = self.phonopy_qha.get_volume_temperature() for t, v in zip(temperatures[::20], volumes[::20]): fc = self.fc_fit.get_total_force_constants(temperature=t, volume=v) write_FORCE_CONSTANTS(fc.get_array(), filename='FC_{}'.format(t))
def create_FORCE_CONSTANTS(filename, options, log_level): fc_and_atom_types = read_force_constant_vasprun_xml(filename) if not fc_and_atom_types: print print "\'%s\' dones not contain necessary information." % filename return 1 force_constants, atom_types = fc_and_atom_types if options.is_hdf5: try: import h5py except ImportError: print print "You need to install python-h5py." return 1 write_force_constants_to_hdf5(force_constants) if log_level > 0: print "force_constants.hdf5 has been created from vasprun.xml." else: write_FORCE_CONSTANTS(force_constants) if log_level > 0: print "FORCE_CONSTANTS has been created from vasprun.xml." if log_level > 0: print "Atom types:", atom_types return 0
def create_FORCE_CONSTANTS(filename, is_hdf5, log_level): fc_and_atom_types = parse_force_constants(filename) if not fc_and_atom_types: print('') print("\'%s\' dones not contain necessary information." % filename) return 1 force_constants, atom_types = fc_and_atom_types if is_hdf5: try: import h5py except ImportError: print('') print("You need to install python-h5py.") return 1 write_force_constants_to_hdf5(force_constants) if log_level > 0: print("force_constants.hdf5 has been created from vasprun.xml.") else: write_FORCE_CONSTANTS(force_constants) if log_level > 0: print("FORCE_CONSTANTS has been created from vasprun.xml.") if log_level > 0: print("Atom types: %s" % (" ".join(atom_types))) return 0
def write_force_constants_pair_sd( self, filename_write="FORCE_CONSTANTS_PAIR_SD"): for (pairtypes, force_constants_pair_sd) in self._force_constants_pair_sd.items(): filename_write_pair = "{}_{}_{}".format(filename_write, *pairtypes) write_FORCE_CONSTANTS(force_constants_pair_sd, filename_write_pair)
def write_force_constants_pair( self, filename_write="FORCE_CONSTANTS_PAIR"): force_constants_pair = self.get_force_constants_pair() for (pairtypes, force_constants_pair) in force_constants_pair.items(): filename_write_pair = "{}_{}_{}".format(filename_write, *pairtypes) write_FORCE_CONSTANTS(force_constants_pair, filename_write_pair)
def write_phonopy_force_constants(self, file_name="FORCE_CONSTANTS", cwd=None): """ Args: file_name: cwd: Returns: """ if cwd is not None: file_name = posixpath.join(cwd, file_name) write_FORCE_CONSTANTS(force_constants=self.phonopy.force_constants, filename=file_name)
def distribute_fc2(self): self._fc2 = np.zeros((self._num_atom, self._num_atom, 3, 3)) for atom1 in np.arange(self._num_atom): for atom2 in np.arange(self._num_atom): trans = np.dot(self._coeff2[atom1, atom2], self.irred_trans[self.ifc2_map[atom1, atom2]]) self._fc2[atom1, atom2] = np.dot(trans, self.irred_fc2).reshape(3,3) print "Force constants obtained from MD simulations" # show_rotational_invariance(self.force_constants, self.supercell, self.primitive) show_drift_force_constants(self.fc2) if self._is_trans_inv < 0: print "Coerced translational invariance mode, after which" set_translational_invariance(self.fc2) show_drift_force_constants(self.fc2) if self._is_hdf5: write_fc2_hdf5(self.fc2) else: write_FORCE_CONSTANTS(self._fc2, "FORCE_CONSTANTS_MDFC")
def distribute_fc2(self): self._fc2 = np.zeros((self._num_atom, self._num_atom, 3, 3)) for atom1 in np.arange(self._num_atom): for atom2 in np.arange(self._num_atom): trans = np.dot(self._coeff2[atom1, atom2], self.irred_trans[self.ifc2_map[atom1, atom2]]) self._fc2[atom1, atom2] = np.dot(trans, self.irred_fc2).reshape(3, 3) print "Force constants obtained from MD simulations" # show_rotational_invariance(self.force_constants, self.supercell, self.primitive) show_drift_force_constants(self.fc2) if self._is_trans_inv < 0: print "Coerced translational invariance mode, after which" set_translational_invariance(self.fc2) show_drift_force_constants(self.fc2) if self._is_hdf5: write_fc2_hdf5(self.fc2) else: write_FORCE_CONSTANTS(self._fc2, "FORCE_CONSTANTS_MDFC")
def load_phonopy(filename, structure, dim, symprec=0.01, primitive_matrix=None, factor=VaspToTHz, symmetrise=True, born=None, write_fc=False): """Load phonopy output and return an ``phonopy.Phonopy`` object. Args: filename (str): Path to phonopy output. Can be any of ``FORCE_SETS``, ``FORCE_CONSTANTS``, or ``force_constants.hdf5``. structure (:obj:`~pymatgen.core.structure.Structure`): The unitcell structure. dim (list): The supercell size, as a :obj:`list` of :obj:`float`. symprec (:obj:`float`, optional): The tolerance for determining the crystal symmetry. primitive_matrix (:obj:`list` or :obj:`str`, optional): The transformation matrix from the conventional to primitive cell. Only required when the conventional cell was used as the starting structure. Should be provided as a 3x3 :obj:`list` of :obj:`float`. Alternatively the str 'auto' may be provided. factor (:obj:`float`, optional): The conversion factor for phonon frequency. Defaults to :obj:`phonopy.units.VaspToTHz`. symmetrise (:obj:`bool`, optional): Symmetrise the force constants. Defaults to ``True``. born (:obj:`str`, optional): Path to file containing Born effective charges. Should be in the same format as the file produced by the ``phonopy-vasp-born`` script provided by phonopy. write_fc (:obj:`bool` or :obj:`str`, optional): Write the force constants to disk. If ``True``, a ``FORCE_CONSTANTS`` file will be written. Alternatively, if set to ``"hdf5"``, a ``force_constants.hdf5`` file will be written. Defaults to ``False`` (force constants not written). """ unitcell = get_phonopy_structure(structure) num_atom = unitcell.get_number_of_atoms() num_satom = determinant(dim) * num_atom phonon = Phonopy(unitcell, dim, primitive_matrix=primitive_matrix, factor=factor, symprec=symprec) if 'FORCE_CONSTANTS' == filename or '.hdf5' in filename: # if force constants exist, use these to avoid recalculating them if '.hdf5' in filename: fc = file_IO.read_force_constants_hdf5(filename) elif 'FORCE_CONSTANTS' == filename: fc = file_IO.parse_FORCE_CONSTANTS(filename=filename) if fc.shape[0] != num_satom: msg = ("\nNumber of atoms in supercell is not consistent with the " "matrix shape of\nforce constants read from {}.\nPlease" "carefully check --dim.") logging.error(msg.format(filename)) sys.exit() phonon.set_force_constants(fc) elif 'FORCE_SETS' == filename: # load the force sets from file and calculate force constants fs = file_IO.parse_FORCE_SETS() if fs['natom'] != num_satom: msg = ("\nNumber of atoms in supercell is not consistent with the " "the data in FORCE_SETS\nPlease carefully check --dim.") logging.error(msg.format(filename)) sys.exit() phonon.set_displacement_dataset(fs) logging.info("Calculating force constants...") phonon.produce_force_constants() if born: # load born parameters from a file nac_params = file_IO.parse_BORN(phonon._primitive, symprec=symprec, filename=born) # set the nac unit conversion factor manual, specific to VASP nac_params['factor'] = Hartree * Bohr phonon.set_nac_params(nac_params) if symmetrise: phonon.symmetrize_force_constants() if write_fc == 'hdf5': file_IO.write_force_constants_to_hdf5(phonon.get_force_constants()) logging.info("Force constants written to force_constants.hdf5.") elif write_fc: file_IO.write_FORCE_CONSTANTS(phonon.get_force_constants()) logging.info("Force constants written to FORCE_CONSTANTS.") return phonon
def save_force_constants_to_file(force_constants, filename='FORCE_CONSTANTS'): # Just a wrapper to phonopy function write_FORCE_CONSTANTS(force_constants.get_array(), filename=filename)
def calculate_phonon(atoms, calc, ndim=np.eye(3), primitive_matrix=np.eye(3), distance=0.01, factor=VaspToTHz, is_symmetry=True, symprec=1e-5, func=None, **func_args): """ """ if 'magmoms' in atoms.arrays: is_mag = True else: is_mag = False # 1. get displacements and supercells atoms.set_calculator(calc) # bulk = PhonopyAtoms(atoms=atoms) if is_mag: bulk = PhonopyAtoms( symbols=atoms.get_chemical_symbols(), scaled_positions=atoms.get_scaled_positions(), cell=atoms.get_cell(), magmoms=atoms.arrays['magmoms'], ) else: bulk = PhonopyAtoms(symbols=atoms.get_chemical_symbols(), scaled_positions=atoms.get_scaled_positions(), cell=atoms.get_cell()) phonon = Phonopy(bulk, ndim, primitive_matrix=primitive_matrix, factor=factor, symprec=symprec) phonon.generate_displacements(distance=distance) disps = phonon.get_displacements() for d in disps: print("[phonopy] %d %s" % (d[0], d[1:])) supercell0 = phonon.get_supercell() supercells = phonon.get_supercells_with_displacements() write_supercells_with_displacements(supercell0, supercells) write_disp_yaml(disps, supercell0) # 2. calculated forces. set_of_forces = [] for iscell, scell in enumerate(supercells): cell = Atoms(symbols=scell.get_chemical_symbols(), scaled_positions=scell.get_scaled_positions(), cell=scell.get_cell(), pbc=True) if is_mag: cell.set_initial_magnetic_moments( atoms.get_initial_magnetic_moments()) cell.set_calculator(calc) dir_name = "PHON_CELL%s" % iscell cur_dir = os.getcwd() if not os.path.exists(dir_name): os.mkdir(dir_name) os.chdir(dir_name) forces = cell.get_forces() #print "[Phonopy] Forces: %s" % forces # Do something other than calculating the forces with func. # func: func(atoms, calc, func_args) if func is not None: func(cell, calc, **func_args) os.chdir(cur_dir) drift_force = forces.sum(axis=0) #print "[Phonopy] Drift force:", "%11.5f" * 3 % tuple(drift_force) # Simple translational invariance for force in forces: force -= drift_force / forces.shape[0] set_of_forces.append(forces) # Phonopy post-process phonon.produce_force_constants(forces=set_of_forces) force_constants = phonon.get_force_constants() write_FORCE_CONSTANTS(force_constants, filename='FORCE_CONSTANTS') print('') print("[Phonopy] Phonon frequencies at Gamma:") for i, freq in enumerate(phonon.get_frequencies((0, 0, 0))): print("[Phonopy] %3d: %10.5f THz" % (i + 1, freq)) # THz print("[Phonopy] %3d: %10.5f cm-1" % (i + 1, freq * 33.35)) #cm-1 return phonon
def phonons(self, atoms=None, lammps_cmd="", enforce_c_size=15.0, parameters={}): """Make Phonon calculation setup.""" from phonopy import Phonopy from phonopy.file_IO import ( # parse_FORCE_CONSTANTS, write_FORCE_CONSTANTS, ) bulk = atoms.phonopy_converter() dim = get_supercell_dims(atoms, enforce_c_size=enforce_c_size) atoms = atoms.make_supercell([dim[0], dim[1], dim[2]]) Poscar(atoms).write_file("POSCAR") atoms = atoms.make_supercell_matrix([dim[0], dim[1], dim[2]]) Poscar(atoms).write_file("POSCAR-Super.vasp") phonon = Phonopy(bulk, [[dim[0], 0, 0], [0, dim[1], 0], [0, 0, dim[2]]]) print("[Phonopy] Atomic displacements1:", bulk) print("[Phonopy] Atomic displacements2:", phonon, dim[0], dim[1], dim[2]) phonon.generate_displacements(distance=0.03) disps = phonon.get_displacements() print("[Phonopy] Atomic displacements3:", disps) for d in disps: print("[Phonopy]", d[0], d[1:]) supercells = phonon.get_supercells_with_displacements() # Force calculations by calculator set_of_forces = [] disp = 0 from ase import Atoms as AseAtoms for scell in supercells: ase_atoms = AseAtoms( symbols=scell.get_chemical_symbols(), scaled_positions=scell.get_scaled_positions(), cell=scell.get_cell(), pbc=True, ) j_atoms = ase_to_atoms(ase_atoms) disp = disp + 1 parameters["control_file"] = "run0.mod" a, b, forces = LammpsJob( atoms=j_atoms, lammps_cmd=lammps_cmd, parameters=parameters, jobname="disp-" + str(disp), ).runjob() print("forces=", forces) drift_force = forces.sum(axis=0) print("drift forces=", drift_force) # Simple translational invariance for force in forces: force -= drift_force / forces.shape[0] set_of_forces.append(forces) phonon.produce_force_constants(forces=set_of_forces) write_FORCE_CONSTANTS(phonon.get_force_constants(), filename="FORCE_CONSTANTS") print() print("[Phonopy] Phonon frequencies at Gamma:")
def abinit_to_phonopy(anaddbnc, supercell_matrix, symmetrize_tensors=False, output_dir_path=None, prefix_outfiles="", symprec=1e-5, set_masses=False): """ Converts the interatomic force constants(IFC), born effective charges(BEC) and dielectric tensor obtained from anaddb to the phonopy format. Optionally writes the standard phonopy files to a selected directory: FORCE_CONSTANTS, BORN (if BECs are available) POSCAR of the unit cell, POSCAR of the supercell. The conversion is performed taking the IFC in the Wigner–Seitz supercell with weights as produced by anaddb and reorganizes them in a standard supercell multiple of the unit cell. Operations are vectorized using numpy. This may lead to the allocation of large arrays in case of very large supercells. Performs a check to verify if the two codes identify the same symmetries and it gives a warning in case of failure. Mismatching symmetries may lead to incorrect conversions. Args: anaddbnc: an instance of AnaddbNcFile. Should contain the output of the IFC analysis, the BEC and the dielectric tensor. supercell_matrix: the supercell matrix used for phonopy. Any choice is acceptable, however the best agreement between the abinit and phonopy results is obtained if this is set to a diagonal matrix with on the diagonal the ngqpt used to generate the anaddb.nc. symmetrize_tensors: if True the tensors will be symmetrized in the Phonopy object and in the output files. This will apply to IFC, BEC and dielectric tensor. output_dir_path: a path to a directory where the phonopy files will be created prefix_outfiles: a string that will be added as a prefix to the name of the written files symprec: distance tolerance in Cartesian coordinates to find crystal symmetry in phonopy. It might be that the value should be tuned so that it leads to the the same symmetries as in the abinit calculation. set_masses: if True the atomic masses used by abinit will be added to the PhonopyAtoms and will be present in the returned Phonopy object. This should improve compatibility among abinit and phonopy results if frequencies needs to be calculated. Returns: An instance of a Phonopy object that contains the IFC, BEC and dieletric tensor data. """ ifc = anaddbnc.ifc nac_params = None becs = None epsinf = None if anaddbnc.becs is not None and anaddbnc.epsinf is not None: becs = anaddbnc.becs.values epsinf = anaddbnc.epsinf # according to the phonopy website 14.399652 is not the coefficient for abinit # probably it relies on the other conventions in the output. nac_params = {"born": becs, "dielectric": epsinf, "factor": 14.399652} s = anaddbnc.structure phon_at = get_phonopy_structure(s) if set_masses: phon_at.masses = [anaddbnc.amu[n] for n in phon_at.numbers] # use phonopy to get the proper supercell given by the primitive and the matrix # and convert it to pymatgen phonon = Phonopy(phon_at, supercell_matrix, primitive_matrix=np.eye(3), nac_params=nac_params, symprec=symprec) phon_supercell = phonon.get_supercell() supercell = get_pmg_structure(phon_supercell) abi_hall_num = s.abi_spacegroup.get_spglib_hall_number() spglib_hall_num = phonon.symmetry.dataset["hall_number"] if abi_hall_num != spglib_hall_num: warnings.warn( "The hall number obtained based on the DDB symmetries differs " f"from the one calculated with spglib: {abi_hall_num} versus " f"{spglib_hall_num}. The conversion may be incorrect. Try changing symprec." ) # convert to phonopy units at_cart = ifc.atoms_cart_coord * abu.Bohr_Ang ifccc = ifc.ifc_cart_coord * abu.Ha_eV / abu.Bohr_Ang**2 weights = ifc.ifc_weights latt = supercell.lattice ifcph = np.zeros((len(s), len(supercell), 3, 3)) # loop over the atoms in the primitive cell # other operations are vectorized using numpy arrays. Some array may require large allocations for i, (site, c_list, w_list) in enumerate(zip(s, at_cart, weights)): ind_w = np.where(w_list > 0) ifccc_loc = ifccc[i, ind_w[0]] w_list = w_list[ind_w] c_list = c_list[ind_w] # align the coordinates of the first atom in the list (the site under consideration) # with the site in the primitive cell. c_list = c_list - c_list[0] + site.coords # convert to fractional coordinates as needed by the Lattice to get the distances f_list = latt.get_fractional_coords(c_list) sc_fcoords = supercell.frac_coords # construct the list of sites of the supercell that are closer to sites in # the primitive cell dist_and_img = [ latt.get_distance_and_image(f_list[0], fc) for fc in sc_fcoords ] # the function gives the translation of the image, but it should be applied to the coordinates. # Only the positions are needed nearest_sc_fcoords = [ fc + trasl for (_, trasl), fc in zip(dist_and_img, sc_fcoords) ] # divide by the corresponding weights. Elements with weights 0 were discarded above ifccc_loc = np.transpose(ifccc_loc, (0, 2, 1)) / w_list[:, None, None] # create an array with all the possible pairs # instantiating this array seems slow but seems still faster than the required loops coord_pairs = np.array( list(itertools.product(nearest_sc_fcoords, f_list))) # find the pairs that match between the coordinates of the modified supercell and the f_list ind_match = np.where( np.abs(coord_pairs[:, 0] - coord_pairs[:, 1]).sum(axis=1) < 1e-6)[0] # set the ifc for phonopy in the final array corresponding to the matching indices. n_points_f_list = len(f_list) ifcph[i, ind_match // n_points_f_list] = ifccc_loc[ind_match % n_points_f_list] phonon.set_force_constants(ifcph) if symmetrize_tensors: phonon.symmetrize_force_constants() if output_dir_path: makedirs_p(output_dir_path) fc_filepath = os.path.join(output_dir_path, prefix_outfiles + "FORCE_CONSTANTS") write_FORCE_CONSTANTS(phonon.get_force_constants(), fc_filepath) if becs is not None and epsinf is not None: born_filepath = os.path.join(output_dir_path, prefix_outfiles + "BORN") write_BORN(phon_at, borns=becs, epsilon=epsinf, filename=born_filepath, symmetrize_tensors=symmetrize_tensors) poscar_filepath = os.path.join(output_dir_path, prefix_outfiles + "POSCAR") poscar = Poscar(s) poscar.write_file(poscar_filepath, significant_figures=15) supercell_filepath = os.path.join(output_dir_path, prefix_outfiles + "supercell_POSCAR") superce_poscar = Poscar(supercell) superce_poscar.write_file(supercell_filepath, significant_figures=15) return phonon
def calculate_phonon(atoms, calc=None, forces_set_file=None, ndim=np.eye(3), primitive_matrix=np.eye(3), distance=0.01, factor=VaspToTHz, is_plusminus='auto', is_symmetry=True, symprec=1e-5, func=None, prepare_initial_wavecar=False, skip=None, restart=True, parallel=True, sc_mag=None, **func_args): """ """ if 'magmoms' in atoms.arrays or 'initial_magmoms' in atoms.arrays: is_mag = True else: is_mag = False print("is_mag: ", is_mag) # 1. get displacements and supercells if calc is not None: atoms.set_calculator(calc) # bulk = PhonopyAtoms(atoms=atoms) if is_mag: bulk = PhonopyAtoms( symbols=atoms.get_chemical_symbols(), scaled_positions=atoms.get_scaled_positions(), cell=atoms.get_cell(), magmoms=atoms.arrays['initial_magmoms'], ) else: bulk = PhonopyAtoms(symbols=atoms.get_chemical_symbols(), scaled_positions=atoms.get_scaled_positions(), cell=atoms.get_cell()) phonon = Phonopy(bulk, ndim, primitive_matrix=primitive_matrix, factor=factor, symprec=symprec) phonon.generate_displacements(distance=distance, is_plusminus=is_plusminus) disps = phonon.get_displacements() for d in disps: print(("[phonopy] %d %s" % (d[0], d[1:]))) supercell0 = phonon.get_supercell() supercells = phonon.get_supercells_with_displacements() #write_supercells_with_displacements(supercell0, supercells) write_disp_yaml(disps, supercell0) # 2. calculated forces. if forces_set_file is not None: symmetry = phonon.get_symmetry() set_of_forces = parse_FORCE_SETS( is_translational_invariance=False, filename=forces_set_file) # ['first_atoms'] # set_of_forces=np.array(set_of_forces) #set_of_forces=[np.asarray(f) for f in set_of_forces] phonon.set_displacement_dataset(set_of_forces) phonon.produce_force_constants() else: # initialize set of forces if restart and os.path.exists('forces_set.pickle'): try: with open("forces_set.pickle", 'rb') as myfile: set_of_forces = pickle.load(myfile) iskip = len(set_of_forces) - 1 except: set_of_forces = [] iskip = -1 else: set_of_forces = [] iskip = -1 if prepare_initial_wavecar and skip is None: scell = supercell0 cell = Atoms(symbols=scell.get_chemical_symbols(), scaled_positions=scell.get_scaled_positions(), cell=scell.get_cell(), pbc=True) if is_mag: cell.set_initial_magnetic_moments(sc_mag) write('Supercell.cif', cell) mcalc = copy.deepcopy(calc) mcalc.set(lwave=True, lcharg=True) cell.set_calculator(mcalc) dir_name = "SUPERCELL0" cur_dir = os.getcwd() if not os.path.exists(dir_name): os.mkdir(dir_name) os.chdir(dir_name) mcalc.scf_calculation(cell) os.chdir(cur_dir) def calc_force(iscell): scell = supercells[iscell] cell = Atoms(symbols=scell.get_chemical_symbols(), scaled_positions=scell.get_scaled_positions(), cell=scell.get_cell(), pbc=True) if is_mag: cell.set_initial_magnetic_moments(sc_mag) cell.set_calculator(copy.deepcopy(calc)) dir_name = "PHON_CELL%s" % iscell cur_dir = os.getcwd() if not os.path.exists(dir_name): os.mkdir(dir_name) if prepare_initial_wavecar: os.system('ln -s %s %s' % (os.path.abspath("SUPERCELL0/WAVECAR"), os.path.join(dir_name, 'WAVECAR'))) os.chdir(dir_name) forces = cell.get_forces() print("[Phonopy] Forces: %s" % forces) # Do something other than calculating the forces with func. # func: func(atoms, calc, func_args) if func is not None: func(cell, calc, **func_args) os.chdir(cur_dir) drift_force = forces.sum(axis=0) print("[Phonopy] Drift force:", "%11.5f" * 3 % tuple(drift_force)) # Simple translational invariance for force in forces: force -= drift_force / forces.shape[0] return forces if parallel: p = Pool() set_of_forces = p.map(calc_force, list(range(iskip, len(supercells)))) else: for iscell, scell in enumerate(supercells): if iscell > iskip: fs = calc_force(iscell) set_of_forces.append(fs) with open("forces_set.pickle", 'wb') as myfile: pickle.dump(set_of_forces, myfile) phonon.produce_force_constants(forces=np.array(set_of_forces)) force_constants = phonon.get_force_constants() write_FORCE_CONSTANTS(force_constants, filename='FORCE_CONSTANTS') #print("[Phonopy] Phonon frequencies at Gamma:") # for i, freq in enumerate(phonon.get_frequencies((0, 0, 0))): # print(("[Phonopy] %3d: %10.5f THz" % (i + 1, freq))) # THz # print(("[Phonopy] %3d: %10.5f cm-1" % (i + 1, freq * 33.35))) #cm-1 with open('phonon.pickle', 'wb') as myfile: pickle.dump(phonon, myfile) phonon.save(settings={'force_constants': True}) return phonon
def BuildForceConstants(lattice_vectors, atom_types, atom_pos, q_pts, freqs, eigs, sc_dim, freq_units = 'thz', atom_mass = None, file_path = r"FORCE_CONSTANTS"): """ Use the Phonopy API to take a set of frequencies and eigenvectors, construct a dynamical matrix, transform to a set of force constants, and write out a Phonopy FORCE_CONSTANTS file. Args: lattice_vectors -- 3x3 matrix containing the lattice vectors atom_types -- list of atomic symbols atom_pos -- list of atomic positions ** in fractional coordinates ** q_pts -- list of q-point coordinates freqs -- n_q sets of Frequencies eigs -- n_q sets of eigenvectors sc_dim -- (equivalent) supercell dimension for preparing force constants freq_units -- units of freqs ('thz' or 'inv_cm' -- default: 'thz') atom_mass -- (optional) list of atomic masses (default: taken from Phonopy internal database) file_path -- path to FORCE_CONSTANTS file to write (default: FORCE_CONSTANTS) """ # Check input. _CheckStructure(lattice_vectors, atom_types, atom_pos, atom_mass) for param in q_pts, freqs, eigs, sc_dim: assert param is not None dim_1, dim_2 = np.shape(q_pts) assert dim_1 > 0 and dim_2 == 3 n_at = len(atom_types) n_q = dim_1 n_b = 3 * n_at dim_1, dim_2 = np.shape(freqs) assert dim_1 == n_q and dim_2 == n_b dim_1, dim_2, dim_3, dim_4 = np.shape(eigs) assert dim_1 == n_q and dim_2 == n_b and dim_3 == n_at and dim_4 == 3 dim_1, = np.shape(sc_dim) assert dim_1 == 3 # Obtain a frequency conversion factor to "VASP units". freq_conv_factor = None if freq_units == 'thz': freq_conv_factor = VaspToTHz elif freq_units == 'inv_cm': freq_conv_factor = VaspToCm if freq_conv_factor is None: raise Exception("Error: Unknown freq_units '{0}'.".format(freq_units)) # Create a Phonopy-format structure. structure = PhonopyAtoms( symbols = atom_types, masses = atom_mass, scaled_positions = atom_pos, cell = lattice_vectors ) # Convert supercell expansion to a supercell matrix. dim_1, dim_2, dim_3 = sc_dim if dim_1 != dim_2 != dim_2 != 1: warnings.warn("The dynamical matrix -> force constants transform has only been tested at the Gamma point; please report issues to the developer.", RuntimeWarning) sc_matrix = [[dim_1, 0, 0], [0, dim_2, 0], [0, 0, dim_3]] # Use the main Phonopy object to obtain the primitive cell and supercell. calculator = Phonopy(structure, sc_matrix) primitive = calculator.get_primitive(); supercell = calculator.get_supercell(); # Use the DynmatToForceConstants object to convert frequencies/eigenvectors -> dynamical matrices -> force constants. dynmat_to_fc = DynmatToForceConstants(primitive, supercell) commensurate_points = dynmat_to_fc.get_commensurate_points() # If an input code does not use crystal symmetry or outputs data at all q-points in a sampling mesh, data may be provided for more q-points than there are commensurate points. # However, for most codes this would be an odd situation -> issue a warning. if len(commensurate_points) != n_q: warnings.warn("The number of entries in the q_pts list does not equal the number of commensurate points expected for the supplied supercell matrix.", RuntimeWarning) # Map commensurate points in Phonopy setup to q-points in input data. map_indices = [] for qx_1, qy_1, qz_1 in commensurate_points: map_index = None for i, (qx_2, qy_2, qz_2) in enumerate(q_pts): if math.fabs(qx_2 - qx_1) < _SymPrec and math.fabs(qy_2 - qy_1) < _SymPrec and math.fabs(qz_2 - qz_1) < _SymPrec: map_index = i break if map_index is None: raise Exception("Error: Expected q = ({0: >6.3f}, {1: >6.3f}, {2: >6.3f}) in the q_pts list (this may be a bug; please report to the developer).".format(qx_1, qy_1, qz_1)) # Sanity check. assert map_index not in map_indices map_indices.append(map_index) # Arrange the frequencies and eigenvectors to the layout required by Phonopy. freq_sets, eig_sets = [], [] for index in map_indices: freq_sets.append( [freq / freq_conv_factor for freq in freqs[index]] ) eig = eigs[index] # Eigenvectors need to be a 3Nx3N matrix in the format: # 1_x -> [ m_1, ..., m_3N ] # 1_y -> [ m_1, ..., m_3N ] # ... # N_z -> [ m_1, ..., m_3N ] eig_rows = [] for i in range(0, n_at): for j in range(0, 3): eig_row = [] for k in range(0, n_b): eig_row.append(eig[k][i][j]) eig_rows.append(eig_row) eig_sets.append(eig_rows) freq_sets = np.array(freq_sets, dtype = np.float64) eig_sets = np.array(eig_sets, dtype = np.complex128) # Use the DynmatToForceConstants object to build the dynamical matrices, reverse transform to the force constants, and write a Phonopy FORCE_CONSTANTS file. dynmat_to_fc.set_dynamical_matrices(freq_sets, eig_sets) dynmat_to_fc.run() write_FORCE_CONSTANTS( dynmat_to_fc.get_force_constants(), filename = file_path )
import phonopy from phonopy.file_IO import parse_FORCE_CONSTANTS, write_FORCE_CONSTANTS phonon = phonopy.load(supercell_matrix=[[2, 0, 0], [0, 2, 0], [0, 0, 2]], primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], unitcell_filename="POSCAR", force_sets_filename="FORCE_SETS", born_filename="BORN") write_FORCE_CONSTANTS(phonon.get_force_constants(), filename="FORCE_CONSTANTS") force_constants = parse_FORCE_CONSTANTS() phonon.force_constants = force_constants phonon.symmetrize_force_constants() write_FORCE_CONSTANTS(phonon.get_force_constants(), filename="FORCE_CONSTANTS_NEW")
def write_force_constants(self, filename_write): write_FORCE_CONSTANTS(self._force_constants, filename_write)
def write_force_constants_sd( self, filename_write="FORCE_CONSTANTS_SD"): write_FORCE_CONSTANTS(self._force_constants_sd, filename_write)
atomic_elements=unitcell.get_chemical_symbols(), primitive_matrix=primitive_mat) structure.set_force_constants( ForceConstants(force_constants, supercell=np.diag(supercell))) # generate LAMMPS MD trajectory (requires dynaphopy development) trajectory = generate_lammps_trajectory(structure, 'in.lammps', total_time=20, time_step=0.001, relaxation_time=5, silent=False, supercell=supercell, memmap=False, velocity_only=False, temperature=temperature) # Calculate renormalized force constants with dynaphopy calculation = Quasiparticle(trajectory) with silence_stdout(): calculation.select_power_spectra_algorithm(2) # 1: Max. Entrop. 2:FFT calculation.plot_renormalized_phonon_dispersion_bands() renormalized_force_constants = calculation.get_renormalized_force_constants( ) # Save renormalized force constants and unitcell to disk write_FORCE_CONSTANTS(renormalized_force_constants.get_array(), filename='FORCE_CONSTANTS_{}K'.format(temperature)) phlammps.write_unitcell_POSCAR(filename='POSCAR_unitcell')
def do_phonons(strt=None, parameters=None, c_size=15): """ Setting up phonopy job using LAMMPS Args: strt: Structure object parameters: LAMMPS input file parameters c_size: cell-size """ # spg_strt = SpacegroupAnalyzer(strt).get_conventional_standard_structure() p = get_phonopy_atoms(mat=strt) bulk = p dim1 = int((float(c_size) / float(max(abs(strt.lattice.matrix[0]))))) + 1 dim2 = int(float(c_size) / float(max(abs(strt.lattice.matrix[1])))) + 1 dim3 = int(float(c_size) / float(max(abs(strt.lattice.matrix[2])))) + 1 Poscar(strt).write_file("POSCAR") tmp = strt.copy() tmp.make_supercell([dim1, dim2, dim3]) Poscar(tmp).write_file("POSCAR-Super.vasp") # phonon = Phonopy(get_phonopy_atoms(tmp), [[dim1, 0, 0], [0, dim2, 0], [0, 0, dim3]]) # , phonon = Phonopy(bulk, [[dim1, 0, 0], [0, dim2, 0], [0, 0, dim3]]) # , print("[Phonopy] Atomic displacements1:", bulk) print("[Phonopy] Atomic displacements2:", phonon, dim1, dim2, dim3) phonon.generate_displacements(distance=0.03) disps = phonon.get_displacements() print("[Phonopy] Atomic displacements3:", disps) for d in disps: print("[Phonopy]", d[0], d[1:]) supercells = phonon.get_supercells_with_displacements() # Force calculations by calculator set_of_forces = [] disp = 0 for scell in supercells: cell = Atoms( symbols=scell.get_chemical_symbols(), scaled_positions=scell.get_scaled_positions(), cell=scell.get_cell(), pbc=True, ) disp = disp + 1 mat = Poscar(AseAtomsAdaptor().get_structure(cell)) mat.comment = str("disp-") + str(disp) parameters["min"] = "skip" parameters["control_file"] = "/users/knc6/in.phonon" # a,b,forces=run_job(mat=mat,parameters={'min':'skip','pair_coeff': '/data/knc6/JARVIS-FF-NEW/ALLOY4/Mishin-Ni-Al-2009.eam.alloy', 'control_file': '/users/knc6/in.phonon', 'pair_style': 'eam/alloy', 'atom_style': 'charge'}) a, b, forces = run_job(mat=mat, parameters=parameters) print("forces=", forces) drift_force = forces.sum(axis=0) print("drift forces=", drift_force) print("[Phonopy] Drift force:", "%11.5f" * 3 % tuple(drift_force)) # Simple translational invariance for force in forces: force -= drift_force / forces.shape[0] set_of_forces.append(forces) phonon.produce_force_constants(forces=set_of_forces) write_FORCE_CONSTANTS(phonon.get_force_constants(), filename="FORCE_CONSTANTS") print() print("[Phonopy] Phonon frequencies at Gamma:")
def save_force_constants_to_file(force_constants, filename='FORCE_CONSTANTS'): #Just a wrapper to phonopy function write_FORCE_CONSTANTS(force_constants, filename=filename)
def write_fc_enlarged(self, filename): fc = self._fc_enlarged write_FORCE_CONSTANTS(fc, filename)
def write_force_constants_sd( self, filename_write="FORCE_CONSTANTS_SD"): fc = self.get_force_constants_sd() write_FORCE_CONSTANTS(fc, filename_write)
def write_force_constants_symmetrized( self, filename_write="FORCE_CONSTANTS_SPG"): write_FORCE_CONSTANTS(self._force_constants_symmetrized, filename_write)
if settings.get_is_force_constants() == 'read': phonon.set_force_constants(fc) # Impose cutoff radius on force constants cutoff_radius = settings.get_cutoff_radius() if cutoff_radius: phonon.set_force_constants_zero_with_radius(cutoff_radius) # Enforce space group symmetry to force constants if settings.get_fc_spg_symmetry(): if log_level > 0: print('') print("Force constants are symmetrized by space group operations.") print("This may take some time...") phonon.symmetrize_force_constants_by_space_group() file_IO.write_FORCE_CONSTANTS(phonon.get_force_constants(), filename='FORCE_CONSTANTS_SPG') if log_level > 0: print( "Symmetrized force constants are written into " "FORCE_CONSTANTS_SPG.") # Imporse translational invariance and index permulation symmetry to # force constants if settings.get_fc_symmetry_iteration() > 0: phonon.symmetrize_force_constants(settings.get_fc_symmetry_iteration()) # Write FORCE_CONSTANTS if settings.get_is_force_constants() == "write": if settings.get_is_hdf5(): file_IO.write_force_constants_to_hdf5(phonon.get_force_constants()) if log_level > 0:
if True: print('Writing partial density of states') f = open('partial_density_of_states', 'w') for i, j in zip( wf.get_result('dos').get_array('frequency'), wf.get_result('dos').get_array('partial_dos').T): f.write('{} {}\n'.format(i, ' '.join(map(str, j)))) f.close() # FORCE CONSTANTS if True: print('Writing FORCE_CONSTANTS') from phonopy.file_IO import write_FORCE_CONSTANTS, write_FORCE_SETS force_constants = wf.get_result('force_constants').get_array( 'force_constants') write_FORCE_CONSTANTS(force_constants, filename='FORCE_CONSTANTS') # 2) Load a complete PHONOPY object phonopy_input = wf.get_parameters()['phonopy_input'].get_dict() structure = wf.get_result('final_structure') bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive'], distance=phonopy_input['distance']) phonon.set_force_constants(force_constants)
def write_force_constants_sd(self, filename_write="FORCE_CONSTANTS_SD"): fc = self.get_force_constants_sd() write_FORCE_CONSTANTS(fc, filename_write)
def _calc_bands(atoms, hessian, supercell=(1, 1, 1), outfile=None, grid=None): """Calculates the band structure for the given Hessian matrix. Args: atoms (matdb.atoms.Atoms): atoms object corresponding to the *primitive* cell. The specified supercell matrix should result in a number of atoms that matches the dimensionality of the Hessian. supercell (tuple): tuple of `int` supercell matrix components; can have either 3 or 9 components. hessian (numpy.ndarray): with shape `(natoms*3, natoms*3)`. grid (list): list of `int` specifying the number of divisions in k-space along each reciprocal unit vector. outfile (str): path to the output `band.yaml` file that should be created by this function. Returns: If `outfile` is None, then this method returns a dictionary that has the same format as :func:`from_yaml`. """ #Create a temporary directory in which to work. target = mkdtemp() bandfile = path.join(target, "band.yaml") if grid is None: grid = [13, 13, 13] if isinstance(supercell, np.ndarray): supercell = supercell.flatten() #First, roll up the Hessian and write it as a FORCE_CONSTANTS file. with chdir(target): HR = roll(hessian) write_FORCE_CONSTANTS(HR) atoms.write("POSCAR", format="vasp") #We need to create the band.conf file and write the special #paths in k-space at which the phonons should be calculated. atom_types = _ordered_unique(atoms.get_chemical_symbols()) settings = [("FORCE_CONSTANTS", "READ"), ("ATOM_NAME", ' '.join(atom_types)), ("DIM", ' '.join(map(str, supercell))), ("MP", ' '.join(map(str, grid)))] labels, bands = parsed_kpath(atoms) bandfmt = "{0:.3f} {1:.3f} {2:.3f}" sband = [] for Q in bands: sband.append(bandfmt.format(*Q)) settings.append(("BAND", " ".join(sband))) settings.append(("BAND_LABELS", ' '.join(labels))) with open("band.conf", 'w') as f: for k, v in settings: f.write("{} = {}\n".format(k, v)) sargs = ["phonopy", "band.conf"] xres = execute(sargs, target, venv=True) if not path.isfile(bandfile): #pragma: no cover msg.err("could not calculate phonon bands; see errors.") msg.std(''.join(xres["output"])) result = None if outfile is not None: #Move the band.yaml file to the new target location. from shutil import move move(bandfile, outfile) else: result = from_yaml(bandfile) #Remove the temporary directory that we created and return the result. rmtree(target) return result
from phonopy import Phonopy from phonopy.interface.vasp import read_vasp from phonopy.file_IO import parse_FORCE_SETS from phonopy.file_IO import parse_FORCE_CONSTANTS, write_FORCE_CONSTANTS cell = read_vasp("POSCAR") phonon = Phonopy(cell, [[2, 0, 0], [0, 2, 0], [0, 0, 2]]) force_sets = parse_FORCE_SETS() phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() write_FORCE_CONSTANTS(phonon.get_force_constants(), filename="FORCE_CONSTANTS") force_constants = parse_FORCE_CONSTANTS() phonon.set_force_constants(force_constants) phonon.symmetrize_force_constants(iteration=1) write_FORCE_CONSTANTS(phonon.get_force_constants(), filename="FORCE_CONSTANTS_NEW")
def write(self, filename): write_FORCE_CONSTANTS(self._fc_reduced, filename=filename)