def get_displaced_structures(pmg_structure, atom_disp=0.01, supercell_matrix=None, yaml_fname=None, **kwargs): r""" Generate a set of symmetrically inequivalent displaced structures for phonon calculations. Args: pmg_structure (Structure): A pymatgen structure object. atom_disp (float): Atomic displacement. Default is 0.01 $\\AA$. supercell_matrix (3x3 array): Scaling matrix for supercell. yaml_fname (string): If not None, it represents the full path to the outputting displacement yaml file, e.g. disp.yaml. **kwargs: Parameters used in Phonopy.generate_displacement method. Return: A list of symmetrically inequivalent structures with displacements, in which the first element is the perfect supercell structure. """ is_plusminus = kwargs.get("is_plusminus", "auto") is_diagonal = kwargs.get("is_diagonal", True) is_trigonal = kwargs.get("is_trigonal", False) ph_structure = get_phonopy_structure(pmg_structure) if supercell_matrix is None: supercell_matrix = np.eye(3) * np.array((1, 1, 1)) phonon = Phonopy(unitcell=ph_structure, supercell_matrix=supercell_matrix) phonon.generate_displacements( distance=atom_disp, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, ) if yaml_fname is not None: displacements = phonon.get_displacements() write_disp_yaml( displacements=displacements, supercell=phonon.get_supercell(), filename=yaml_fname, ) # Supercell structures with displacement disp_supercells = phonon.get_supercells_with_displacements() # Perfect supercell structure init_supercell = phonon.get_supercell() # Structure list to be returned structure_list = [get_pmg_structure(init_supercell)] for c in disp_supercells: if c is not None: structure_list.append(get_pmg_structure(c)) return structure_list
def get_displaced_structures(pmg_structure, atom_disp=0.01, supercell_matrix=None, yaml_fname=None, **kwargs): """ Generate a set of symmetrically inequivalent displaced structures for phonon calculations. Args: pmg_structure (Structure): A pymatgen structure object. atom_disp (float): Atomic displacement. Default is 0.01 $\\AA$. supercell_matrix (3x3 array): Scaling matrix for supercell. yaml_fname (string): If not None, it represents the full path to the outputting displacement yaml file, e.g. disp.yaml. **kwargs: Parameters used in Phonopy.generate_displacement method. Return: A list of symmetrically inequivalent structures with displacements, in which the first element is the perfect supercell structure. """ is_plusminus = kwargs.get("is_plusminus", "auto") is_diagonal = kwargs.get("is_diagonal", True) is_trigonal = kwargs.get("is_trigonal", False) ph_structure = get_phonopy_structure(pmg_structure) if supercell_matrix is None: supercell_matrix = np.eye(3) * np.array((1, 1, 1)) phonon = Phonopy(unitcell=ph_structure, supercell_matrix=supercell_matrix) phonon.generate_displacements(distance=atom_disp, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal) if yaml_fname is not None: displacements = phonon.get_displacements() directions = phonon.get_displacement_directions() write_disp_yaml(displacements=displacements, supercell=phonon.get_supercell(), directions=directions, filename=yaml_fname) # Supercell structures with displacement disp_supercells = phonon.get_supercells_with_displacements() # Perfect supercell structure init_supercell = phonon.get_supercell() # Structure list to be returned structure_list = [get_pmg_structure(init_supercell)] for c in disp_supercells: if c is not None: structure_list.append(get_pmg_structure(c)) return structure_list
def get_data_from_dir(directory, i_volume): data_sets = file_IO.parse_FORCE_SETS( filename=directory + '/phonon-{0:02d}/FORCE_SETS'.format(i_volume)) yaml_file = open( directory + '/phonon-{0:02d}/phonon.yaml'.format(i_volume), 'r') data = yaml.load_all(yaml_file).next() unit_cell = PhonopyAtoms( symbols=[item['symbol'] for item in data['points']], scaled_positions=[item['coordinates'] for item in data['points']], cell=data['lattice']) phonon = Phonopy(unit_cell, data['supercell_matrix']) phonon.set_displacement_dataset(data_sets) phonon.produce_force_constants() force_constants = phonon.get_force_constants() supercell = phonon.get_supercell() volume = unit_cell.get_volume() energy = data['electric_total_energy'] return supercell, volume, energy, force_constants, data['supercell_matrix']
def _get_phonon(self, cell): phonon = Phonopy(cell, np.diag([2, 2, 2]), primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) force_sets = parse_FORCE_SETS(filename=os.path.join(data_dir,"FORCE_SETS_moment")) phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() supercell = phonon.get_supercell() born_elems = {'Na': [[1.08703, 0, 0], [0, 1.08703, 0], [0, 0, 1.08703]], 'Cl': [[-1.08672, 0, 0], [0, -1.08672, 0], [0, 0, -1.08672]]} born = [born_elems[s] for s in ['Na', 'Cl']] epsilon = [[2.43533967, 0, 0], [0, 2.43533967, 0], [0, 0, 2.43533967]] factors = 14.400 phonon.set_nac_params({'born': born, 'factor': factors, 'dielectric': epsilon}) return phonon
def _get_phonon(self, cell): phonon = Phonopy(cell, np.diag([2, 2, 2]), primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) force_sets = parse_FORCE_SETS(filename="FORCE_SETS_moment") phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() supercell = phonon.get_supercell() born_elems = {'Na': [[1.08703, 0, 0], [0, 1.08703, 0], [0, 0, 1.08703]], 'Cl': [[-1.08672, 0, 0], [0, -1.08672, 0], [0, 0, -1.08672]]} born = [born_elems[s] for s in ['Na', 'Cl']] epsilon = [[2.43533967, 0, 0], [0, 2.43533967, 0], [0, 0, 2.43533967]] factors = 14.400 phonon.set_nac_params({'born': born, 'factor': factors, 'dielectric': epsilon}) return phonon
def phonon_dos(fcp_file): if 'fcc2x2x2' in fcp_file: prim = read(ref_fcc_conv2x2x2) else: prim = read(ref_fcc) fcp = ForceConstantPotential.read(fcp_file) mesh = [33, 33, 33] atoms_phonopy = PhonopyAtoms(symbols=prim.get_chemical_symbols(), scaled_positions=prim.get_scaled_positions(), cell=prim.cell) phonopy = Phonopy(atoms_phonopy, supercell_matrix=5 * np.eye(3), primitive_matrix=None) supercell = phonopy.get_supercell() supercell = Atoms(cell=supercell.cell, numbers=supercell.numbers, pbc=True, scaled_positions=supercell.get_scaled_positions()) fcs = fcp.get_force_constants(supercell) phonopy.set_force_constants(fcs.get_fc_array(order=2)) phonopy.set_mesh(mesh, is_eigenvectors=True, is_mesh_symmetry=False) phonopy.run_total_dos() phonopy.plot_total_DOS() plt.savefig("phononDOS.png", dpi=200) Nq = 51 G2X = get_band(np.array([0, 0, 0]), np.array([0.5, 0.5, 0]), Nq) X2K2G = get_band(np.array([0.5, 0.5, 1.0]), np.array([0, 0, 0]), Nq) G2L = get_band(np.array([0, 0, 0]), np.array([0.5, 0.5, 0.5]), Nq) bands = [G2X, X2K2G, G2L] phonopy.set_band_structure(bands) phonopy.plot_band_structure() xticks = plt.gca().get_xticks() xticks = [x * hbar * 1e15 for x in xticks] # Convert THz to meV # plt.gca().set_xticks(xticks) plt.gca().set_xlabel("Frequency (THz)") plt.savefig("phononBand.png", dpi=200) phonopy.run_thermal_properties(t_step=10, t_max=800, t_min=100) tp_dict = phonopy.get_thermal_properties_dict() temperatures = tp_dict['temperatures'] free_energy = tp_dict['free_energy'] fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.plot(temperatures, free_energy) plt.show()
def get_rand_FCM(self, asum=15, force=10): """ Generate a symmeterized force constant matrix from an unsymmeterized matrix that has no unstable modes and also obeys the acoustic sum rule through an iterative procedure Args: force (float): maximum force constant asum (int): number of iterations to attempt to obey the acoustic sum rule Return: NxNx3x3 np.array representing the force constant matrix """ numsites = len(self.structure.sites) structure = pymatgen.io.phonopy.get_phonopy_structure(self.structure) pnstruc = Phonopy(structure, np.eye(3), np.eye(3)) dyn = self.get_unstable_FCM(force) dyn = self.get_stable_FCM(dyn) dyn = np.reshape(dyn, (numsites, 3, numsites, 3)).swapaxes(1, 2) dynmass = np.zeros([len(self.structure), len(self.structure), 3, 3]) masses = [] for j in range(numsites): masses.append(self.structure.sites[j].specie.atomic_mass) dynmass = np.zeros([numsites, numsites, 3, 3]) for m in range(numsites): for n in range(numsites): dynmass[m][n] = dyn[m][n] * np.sqrt(masses[m]) * np.sqrt( masses[n]) supercell = pnstruc.get_supercell() primitive = pnstruc.get_primitive() converter = dyntofc.DynmatToForceConstants(primitive, supercell) dyn = np.reshape(np.swapaxes(dynmass, 1, 2), (numsites * 3, numsites * 3)) converter.set_dynamical_matrices(dynmat=[dyn]) converter.run() fc = converter.get_force_constants() return fc
def phonopy_commensurate_shifts_inline(**kwargs): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy from phonopy.harmonic.dynmat_to_fc import get_commensurate_points, DynmatToForceConstants structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() force_constants = kwargs.pop('force_constants').get_array('force_constants') r_force_constants = kwargs.pop('r_force_constants').get_array('force_constants') # Generate phonopy phonon object 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']) primitive = phonon.get_primitive() supercell = phonon.get_supercell() phonon.set_force_constants(force_constants) dynmat2fc = DynmatToForceConstants(primitive, supercell) com_points = dynmat2fc.get_commensurate_points() phonon.set_qpoints_phonon(com_points, is_eigenvectors=True) frequencies_h = phonon.get_qpoints_phonon()[0] phonon.set_force_constants(r_force_constants) phonon.set_qpoints_phonon(com_points, is_eigenvectors=True) frequencies_r = phonon.get_qpoints_phonon()[0] shifts = frequencies_r - frequencies_h # Stores DOS data in DB as a workflow result commensurate = ArrayData() commensurate.set_array('qpoints', com_points) commensurate.set_array('shifts', shifts) return {'commensurate': commensurate}
def get_commensurate(structure, ph_settings): from phonopy import Phonopy from phonopy.harmonic.dynmat_to_fc import DynmatToForceConstants from aiida_phonopy.workchains.phonon import phonopy_bulk_from_structure phonon = Phonopy(phonopy_bulk_from_structure(structure), ph_settings.dict.supercell, primitive_matrix=ph_settings.dict.primitive, symprec=ph_settings.dict.symmetry_precision) primitive = phonon.get_primitive() supercell = phonon.get_supercell() dynmat2fc = DynmatToForceConstants(primitive, supercell) commensurate = dynmat2fc.get_commensurate_points() return dynmat2fc, commensurate
def _get_phonon(self): cell = read_vasp(os.path.join(data_dir, "../POSCAR_NaCl")) phonon = Phonopy(cell, np.diag([2, 2, 2]), primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) filename = os.path.join(data_dir, "FORCE_SETS_NaCl") force_sets = parse_FORCE_SETS(filename=filename) phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() supercell = phonon.get_supercell() born_elems = { "Na": [[1.08703, 0, 0], [0, 1.08703, 0], [0, 0, 1.08703]], "Cl": [[-1.08672, 0, 0], [0, -1.08672, 0], [0, 0, -1.08672]], } born = [born_elems[s] for s in ["Na", "Cl"]] epsilon = [[2.43533967, 0, 0], [0, 2.43533967, 0], [0, 0, 2.43533967]] factors = 14.400 phonon.set_nac_params({"born": born, "factor": factors, "dielectric": epsilon}) return phonon
def _get_phonon(self, cell): phonon = Phonopy(cell, np.diag([1, 1, 1])) force_sets = parse_FORCE_SETS( filename=os.path.join(data_dir, "FORCE_SETS")) phonon.dataset = force_sets phonon.produce_force_constants() supercell = phonon.get_supercell() born_elems = { 'Na': [[1.08703, 0, 0], [0, 1.08703, 0], [0, 0, 1.08703]], 'Cl': [[-1.08672, 0, 0], [0, -1.08672, 0], [0, 0, -1.08672]] } born = [born_elems[s] for s in supercell.get_chemical_symbols()] epsilon = [[2.43533967, 0, 0], [0, 2.43533967, 0], [0, 0, 2.43533967]] factors = 14.400 phonon.set_nac_params({ 'born': born, 'factor': factors, 'dielectric': epsilon }) return phonon
def _get_phonon(self, cell): phonon = Phonopy(cell, np.diag([1, 1, 1])) force_sets = parse_FORCE_SETS( filename=os.path.join(data_dir, "FORCE_SETS")) phonon.dataset = force_sets phonon.produce_force_constants() supercell = phonon.get_supercell() born_elems = {'Na': [[1.08703, 0, 0], [0, 1.08703, 0], [0, 0, 1.08703]], 'Cl': [[-1.08672, 0, 0], [0, -1.08672, 0], [0, 0, -1.08672]]} born = [born_elems[s] for s in supercell.get_chemical_symbols()] epsilon = [[2.43533967, 0, 0], [0, 2.43533967, 0], [0, 0, 2.43533967]] factors = 14.400 phonon.set_nac_params({'born': born, 'factor': factors, 'dielectric': epsilon}) return phonon
def get_commensurate_points(structure, phonopy_input): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy.harmonic.dynmat_to_fc import DynmatToForceConstants from phonopy import Phonopy # Generate phonopy phonon object 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'], symprec=phonopy_input['symmetry_precision']) primitive = phonon.get_primitive() supercell = phonon.get_supercell() dynmat2fc = DynmatToForceConstants(primitive, supercell) com_points = dynmat2fc.get_commensurate_points() return com_points
class PhononBase(TaskElement): """PhononBase class This is an interface to phonopy. """ def __init__(self, directory=None, name=None, supercell_matrix=None, primitive_matrix=None, distance=None, displace_plusminus='auto', displace_diagonal=False, lattice_tolerance=None, force_tolerance=None, pressure_target=None, stress_tolerance=None, max_increase=None, max_iteration=None, min_iteration=None, is_cell_relaxed=False, stop_condition=None, traverse=False): TaskElement.__init__(self) self._directory = directory if not name: self._name = directory else: self._name = name self._task_type = "phonon" self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._distance = distance self._displace_plusminus = displace_plusminus self._displace_diagonal = displace_diagonal self._lattice_tolerance = lattice_tolerance self._pressure_target = pressure_target self._stress_tolerance = stress_tolerance self._force_tolerance = force_tolerance self._max_increase = max_increase self._max_iteration = max_iteration self._min_iteration = min_iteration self._is_cell_relaxed = is_cell_relaxed self._stop_condition = stop_condition self._traverse = traverse self._stage = 0 self._tasks = [] self._energy = None self._space_group = None self._cell = None self._phonon = None # Phonopy object self._phonon_tasks = None # Phonopy object def get_phonon(self): return self._phonon def get_cell(self): if self._is_cell_relaxed: return self._cell else: return self._phonon_tasks[0].get_cell() def get_energy(self): """Return energies at geometry optimization steps""" return self._energy def get_space_group(self): return self._space_group def set_status(self): if self._stage == 0: task = self._tasks[0] if task.done(): self._space_group = task.get_space_group() status = task.get_status() if status == "done": if not self._evaluate_stop_condition(): self._status = "next" else: self._status = status else: done = True terminate = True for task in self._tasks: done &= task.done() terminate &= (task.get_status() == "terminate") if done: if terminate: self._status = "terminate" else: self._status = "next" self._write_yaml() def begin(self): if not self._job: print "set_job has to be executed." raise self._overwrite_settings() if self._is_cell_relaxed: self._phonon_tasks = [None] self._set_stage1() else: self._set_stage0() def done(self): return (self._status == "terminate" or self._status == "done" or self._status == "max_iteration" or self._status == "low_symmetry" or self._status == "next") def next(self): if self._stage == 0: if self._status == "next": self._energy = self._tasks[0].get_energy() num_atom = len(self._tasks[0].get_cell().get_symbols()) self._comment = self._space_group['international_standard'] self._comment += "\\n%f/%d" % (self._energy, num_atom) self._set_stage1() return self._tasks elif (self._status == "terminate" and self._traverse == "restart"): self._traverse = False self._set_stage0() return self._tasks else: # task 1..n: displaced supercells if self._status == "next": self._status = "done" forces = [] for task in self._tasks: forces.append(task.get_properties()['forces'][-1]) self._phonon.produce_force_constants(forces) write_FORCE_SETS(self._phonon.get_displacement_dataset()) self._tasks = [] elif self._status == "terminate" and self._traverse == "restart": self._traverse = False disp_terminated = [] for i, task in enumerate(self._tasks): if task.get_status() == "terminate": disp_terminated.append(i) tasks = self._get_displacement_tasks() self._tasks = [] for i in disp_terminated: self._tasks.append(tasks[i]) self._phonon_tasks[i + 1] = tasks[i] self._status = "displacements" return self._tasks self._write_yaml() raise StopIteration def _set_stage0(self): self._status = "equilibrium" task = self._get_equilibrium_task() self._phonon_tasks = [task] self._tasks = [task] def _set_stage1(self): self._stage = 1 self._status = "displacements" self._set_phonon() self._tasks = self._get_displacement_tasks() self._phonon_tasks += self._tasks def _evaluate_stop_condition(self): if self._stop_condition: if "symmetry_operations" in self._stop_condition: num_ops = len(self._space_group['rotations']) if (num_ops < self._stop_condition['symmetry_operations']): self._status = "low_symmetry" return True return False def _set_phonon(self): cell = self.get_cell() phonopy_cell = cell2atoms(cell) self._phonon = Phonopy(phonopy_cell, self._supercell_matrix, primitive_matrix=self._primitive_matrix, is_auto_displacements=False, dynamical_matrix_decimals=14, force_constants_decimals=14) self._phonon.generate_displacements( distance=self._distance, is_plusminus=self._displace_plusminus, is_diagonal=self._displace_diagonal) supercell = self._phonon.get_supercell() displacements = self._phonon.get_displacements() write_poscar(cell, "POSCAR-unitcell") write_disp_yaml(displacements, supercell) def _write_yaml(self): w = open("%s.yaml" % self._directory, 'w') w.write("supercell_matrix:\n") for row in self._supercell_matrix: w.write("- [ %3d, %3d, %3d ]\n" % tuple(row)) w.write("primitive_matrix:\n") for row in self._primitive_matrix: w.write("- [ %6.3f, %6.3f, %6.3f ]\n" % tuple(row)) w.write("distance: %f\n" % self._distance) if self._phonon_tasks[0]: if self._lattice_tolerance is not None: w.write("lattice_tolerance: %f\n" % self._lattice_tolerance) if self._stress_tolerance is not None: w.write("stress_tolerance: %f\n" % self._stress_tolerance) w.write("pressure_target: %f\n" % self._pressure_target) w.write("force_tolerance: %f\n" % self._force_tolerance) if self._max_increase is None: w.write("max_increase: unset\n") else: w.write("max_increase: %f\n" % self._max_increase) w.write("max_iteration: %d\n" % self._max_iteration) w.write("min_iteration: %d\n" % self._min_iteration) w.write("iteration: %d\n" % self._phonon_tasks[0].get_stage()) if self._energy: w.write("electric_total_energy: %20.10f\n" % self._energy) w.write("status: %s\n" % self._status) w.write("tasks:\n") for task in self._phonon_tasks: if task and task.get_status(): w.write("- name: %s\n" % task.get_name()) w.write(" status: %s\n" % task.get_status()) w.close()
def phonopy_run(phonon, single=True, filename='FORCE_SETS'): """Run the phonon calculations, using PhonoPy. The force constants are then stored in the Phonon ASE object. input: phonon: ASE Phonon object with Atoms and Calculator single: when True, the forces are computed only for a single step, and then exit. This allows to split the loop in independent iterations. When calling again the 'run' method, already computed steps are ignored, missing steps are completed, until no more are needed. When set to False, all steps are done in a row. output: True when a calculation step was performed, False otherwise or no more is needed. nb_of_iterations: the number of steps remaining """ from phonopy import Phonopy from phonopy.structure.atoms import Atoms as PAtoms from phonopy.structure.atoms import PhonopyAtoms import phonopy.file_IO as file_IO # we first look if an existing phonon pickle exists. This is the case if we # are running with iterative calls while return value is True. The first call # will then create the objects, which are subsequently updated until False. # Set calculator if provided # assert phonon.calc is not None, "Provide calculator in Phonon __init__ method" # Atoms in the supercell -- repeated in the lattice vector directions # beginning with the last supercell = phonon.atoms * phonon.N_c # create a PhonopyAtoms object cell = PhonopyAtoms(phonon.atoms.get_chemical_symbols(), positions=phonon.atoms.get_positions(), cell=phonon.atoms.get_cell(), magmoms=None) # is there an existing PhonoPy calculation ? # using factor=6.46541380e-2=VaspToeV if os.path.exists('FORCE_SETS'): phonpy = Phonopy(cell, numpy.diag(phonon.N_c), primitive_matrix=None, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-05, is_symmetry=True, use_lapack_solver=False, log_level=1) force_sets = file_IO.parse_FORCE_SETS(filename='FORCE_SETS') phonpy.set_displacement_dataset(force_sets) # inactivate magmoms in supercell as some calculators do not provide that phonpy._supercell.magmoms = None phonpy.produce_force_constants(calculate_full_force_constants=False) else: # create a PhonoPy Phonon object. phonpy = Phonopy(cell, numpy.diag(phonon.N_c)) # generate displacements (minimal set) phonpy.generate_displacements(distance=0.01) # iterative call for all displacements set_of_forces, flag, nb_of_iterations = phonopy_run_calculate( phonon, phonpy, supercell, single) if flag is True: return nb_of_iterations # some more work is required sys.stdout.write('[ASE/Phonopy] Computing force constants\n') # use symmetry to derive forces in equivalent displacements phonpy.produce_force_constants(forces=set_of_forces) # generate disp.yaml and FORCE_SETS (for later use) displacements = phonpy.get_displacements() directions = phonpy.get_displacement_directions() file_IO.write_disp_yaml(displacements, phonpy.get_supercell(), directions=directions) file_IO.write_FORCE_SETS(phonpy.get_displacement_dataset()) # store as additional data in atoms 'info' phonon.atoms.info["phonopy"] = phonpy # save the PhonoPy object fid = opencew("phonopy.pkl") if fid is not None and rank == 0: print("[ASE/Phonopy] Writing %s" % "phonopy.pkl") pickle.dump(phonpy, fid, protocol=2) fid.close() # transfer results to the ASE phonon object # Number of atoms (primitive cell) natoms = len(phonon.indices) # Number of unit cells (supercell) N = numpy.prod(phonon.N_c) # Phonopy: force_constants size is [N*natoms,N*natoms,3,3] # Phi[i,j,a,b] with [i,j = atom in supercell] and [a,b=xyz] force_constants = phonpy.get_force_constants() # the atoms [i] which are moved are in the first cell of the supercell, i.e.Ni=0 # the forces are then stored for all atoms [Nj,j] as [3,3] matrices # we compute the sum on all supercells, which all contain n atoms. C_N = numpy.zeros((N, 3 * natoms, 3 * natoms), dtype=complex) Ni = 0 for Nj in range(N): for ni in range(natoms): Nni = ni for nj in range(natoms): # compute Nn indices Nnj = Nj * natoms + nj # get fc 3x3 matrix C_N[Nj, (3 * ni):(3 * ni + 3), (3 * nj):(3 * nj + 3)] += force_constants[Nni][Nnj] # convert to ASE storage # ASE: phonon.C_N size is be [N, 3*natoms, 3*natoms] # Phi[i,j] = Phi[j,i] phonon.C_N = C_N # fill dynamical matrix (mass prefactor) phonon.D_N = phonon.C_N.copy() # Add mass prefactor m_a = phonon.atoms.get_masses() phonon.m_inv_x = numpy.repeat(m_a[phonon.indices]**-0.5, 3) M_inv = numpy.outer(phonon.m_inv_x, phonon.m_inv_x) for D in phonon.D_N: D *= M_inv return 0 # nothing left to do
def get_phonon_tb( # phonopy_atoms=[], atoms=[], fc=[], out_file="phonopyTB_hr.dat", distance_to_A=1.0, scell=np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]), factor=VaspToTHz, symprec=1e-05, displacement_distance=0.01, ): """Generate phonon TB Hamiltonia, along with WannierHamn.""" # Forked from Wannier-tools unitcell = atoms.phonopy_converter() # unitcell = phonopy_atoms prim_mat = np.array(PhonopyInputs(atoms).prim_axis().split("=")[1].split(), dtype="float").reshape(3, 3) # print("cell", unitcell.cell) num_atom = unitcell.get_number_of_atoms() num_satom = determinant(scell) * num_atom if fc.shape[0] != num_satom: print("Check Force constant matrix.") phonon = Phonopy( unitcell, scell, primitive_matrix=prim_mat, factor=factor, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=symprec, is_symmetry=True, use_lapack_solver=False, log_level=1, ) supercell = phonon.get_supercell() primitive = phonon.get_primitive() # Set force constants phonon.set_force_constants(fc) phonon._set_dynamical_matrix() dmat = phonon._dynamical_matrix # rescale fcmat by THZ**2 fcmat = dmat._force_constants * factor**2 # FORCE_CONSTANTS # fcmat = dmat._force_constants * factor ** 2 # FORCE_CONSTANTS smallest_vectors = dmat._smallest_vectors # mass = dmat._mass mass = dmat._pcell.get_masses() print("mass=", mass) multi = dmat._multiplicity reduced_bases = get_reduced_bases(supercell.get_cell(), symprec) positions = np.dot(supercell.get_positions(), np.linalg.inv(reduced_bases)) # for pos in positions: pos -= np.rint(pos) relative_scale = np.dot(reduced_bases, np.linalg.inv(primitive.get_cell())) super_pos = np.zeros((num_satom, 3), dtype=np.float64) for i in range(num_satom): super_pos[i] = np.dot(positions[i], relative_scale) p2s_map = dmat._p2s_map = primitive.get_primitive_to_supercell_map() s2p_map = dmat._s2p_map = primitive.get_supercell_to_primitive_map() num_satom = supercell.get_number_of_atoms() num_patom = primitive.get_number_of_atoms() get_phonon_hr( fcmat, smallest_vectors, mass, multi, super_pos, p2s_map, s2p_map, num_satom, num_patom, out_file, ) print("phonopy_TB.dat generated! ")
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 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
class PhononBase(TaskElement): """PhononBase class This is an interface to phonopy. """ def __init__(self, directory=None, name=None, supercell_matrix=None, primitive_matrix=None, distance=None, lattice_tolerance=None, force_tolerance=None, pressure_target=None, stress_tolerance=None, max_increase=None, max_iteration=None, min_iteration=None, traverse=False, is_cell_relaxed=False): TaskElement.__init__(self) self._directory = directory if not name: self._name = directory else: self._name = name self._task_type = "phonon" self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._distance = distance self._lattice_tolerance = lattice_tolerance self._pressure_target = pressure_target self._stress_tolerance = stress_tolerance self._force_tolerance = force_tolerance self._max_increase = max_increase self._max_iteration = max_iteration self._min_iteration = min_iteration self._traverse = traverse self._is_cell_relaxed = is_cell_relaxed self._stage = 0 self._tasks = [] self._energy = None self._cell = None self._phonon = None # Phonopy object def get_phonon(self): return self._phonon def get_cell(self): if self._is_cell_relaxed: return self._cell else: return self._phonon_tasks[0].get_cell() def get_energy(self): """Return energies at geometry optimization steps""" return self._energy def set_status(self): done = True terminate = False for task in self._tasks: done &= task.done() if task.get_status() == "terminate": terminate = True if done: if terminate: self._status = "terminate" else: self._status = "next" self._write_yaml() def begin(self): if not self._job: print "set_job has to be executed." raise self._overwrite_settings() if self._is_cell_relaxed: self._phonon_tasks = [None] self._set_stage1() else: self._set_stage0() def end(self): pass def done(self): return ("terminate" in self._status or "done" in self._status or "next" in self._status) def next(self): if self._stage == 0: if "next" in self._status: self._energy = self._tasks[0].get_energy() self._comment = "%s\\n%f" % (self._tasks[0].get_space_group( )['international_standard'], self._energy) self._set_stage1() return self._tasks elif "terminate" in self._status and self._traverse == "restart": self._traverse = False self._set_stage0() return self._tasks else: raise StopIteration else: # task 1..n: displaced supercells if "next" in self._status: self._status = "done" forces = [] for task in self._phonon_tasks[1:]: forces.append(task.get_properties()['forces'][-1]) self._write_FORCE_SETS(forces) self._phonon.set_post_process(self._primitive_matrix, forces, force_constants_decimals=14) self._tasks = [] raise StopIteration elif "terminate" in self._status and self._traverse == "restart": self._traverse = False disp_terminated = [] for i, task in enumerate(self._tasks): if task.get_status() == "terminate": disp_terminated.append(i) tasks = self._get_displacement_tasks()[1:] self._tasks = [] for i in disp_terminated: self._tasks.append(tasks[i]) self._phonon_tasks[i + 1] = tasks[i] self._status = "displacements" return self._tasks else: raise StopIteration def _set_stage0(self): self._status = "equilibrium" task = self._get_equilibrium_task() self._phonon_tasks = [task] self._tasks = [task] def _set_stage1(self): self._stage = 1 self._status = "displacements" self._set_phonon() self._tasks = self._get_displacement_tasks()[1:] self._phonon_tasks += self._tasks def _set_phonon(self): cell = self.get_cell() phonopy_cell = Atoms(cell=cell.get_lattice().T, scaled_positions=cell.get_points().T, symbols=cell.get_symbols()) self._phonon = Phonopy(phonopy_cell, self._supercell_matrix, is_auto_displacements=False) self._phonon.generate_displacements(distance=self._distance, is_diagonal=False) supercell = self._phonon.get_supercell() displacements = self._phonon.get_displacements() write_poscar(cell, "POSCAR-unitcell") write_disp_yaml(displacements, supercell) def _write_FORCE_SETS(self, forces): displacements = [[x[0], x[1:4]] for x in self._phonon.get_displacements()] natom = self._phonon.get_supercell().get_number_of_atoms() write_FORCE_SETS("FORCE_SETS", natom, displacements, forces, verbose=False) def _write_yaml(self): w = open("%s.yaml" % self._directory, 'w') if self._phonon_tasks[0]: if self._lattice_tolerance is not None: w.write("lattice_tolerance: %f\n" % self._lattice_tolerance) if self._stress_tolerance is not None: w.write("stress_tolerance: %f\n" % self._stress_tolerance) w.write("pressure_target: %f\n" % self._pressure_target) w.write("force_tolerance: %f\n" % self._force_tolerance) w.write("max_increase: %f\n" % self._max_increase) w.write("max_iteration: %d\n" % self._max_iteration) w.write("min_iteration: %d\n" % self._min_iteration) w.write("supercell_matrix:\n") for row in self._supercell_matrix: w.write("- [ %3d, %3d, %3d ]\n" % tuple(row)) w.write("primitive_matrix:\n") for row in self._primitive_matrix: w.write("- [ %6.3f, %6.3f, %6.3f ]\n" % tuple(row)) w.write("distance: %f\n" % self._distance) w.write("iteration: %d\n" % self._phonon_tasks[0].get_stage()) if self._energy: w.write("electric_total_energy: %20.10f\n" % self._energy) w.write("status: %s\n" % self._status) w.write("tasks:\n") for task in self._phonon_tasks: if task and task.get_status(): w.write("- name: %s\n" % task.get_name()) w.write(" status: %s\n" % task.get_status()) w.close()
NNN, # primitive_matrix = [[0.5,0.5,0],[0,0.5,0.5],[0.5,0,0.5]], primitive_matrix=primitive_matrix, nac_params=nac_params, factor=factor, is_symmetry=is_symmetry, ) phonon.generate_displacements( distance=delta, is_plusminus=is_plusminus, ) pho_disp = phonon.get_supercells_with_displacements() vasp.write_supercells_with_displacements( phonon.get_supercell(), pho_disp, ) ######### get new phonon object ########## # import ss_phonopy as ssp phonon = ssp.calc_phonon( calc, phonon, acoustic_sum_rule=acou_sum_rule, rot_sum_rule=rot_sum_rule, r_cut=r_cut, # F_0_correction=True, # verbose=True, # ase_calc=ase_calc,
else: file_exists("FORCE_SETS", log_level) phonon = Phonopy(unitcell, settings.get_supercell_matrix(), primitive_matrix=settings.get_primitive_matrix(), factor=factor, is_auto_displacements=False, dynamical_matrix_decimals=settings.get_dm_decimals(), force_constants_decimals=settings.get_fc_decimals(), symprec=options.symprec, is_symmetry=settings.get_is_symmetry(), use_lapack_solver=settings.get_lapack_solver(), log_level=log_level) supercell = phonon.get_supercell() primitive = phonon.get_primitive() # Set atomic masses of primitive cell if settings.get_masses() is not None: phonon.set_masses(settings.get_masses()) # Print cells if log_level > 1: print_cells(phonon, unitcell_filename) # Set force constants if settings.get_is_force_constants() == 'read': phonon.set_force_constants(fc) # Impose cutoff radius on force constants
def get_phonon_distrib_alist( prim_cell, NNN, T, seed_range, calc, cp_files=None, plus_minus=True, delta=0.01, ): """ prim_cell (str) ASE readable primitive unitcell structure file. NNN (list of int, shape: (3,3)) Supercell matrix used to calculate force constants. T (float) Temperature in Kelvin unit. seed_range (range or list of int) Seeds for random number generation. calc (str, choices: ('lmp', 'vasp')) Calculator for force calculation. cp_files (list of str) List of input files for force calculation. plus_minus (bool) Option handed to ASE function. delta (float) Displacement in Angstrom for finite displacement method. """ # Main from ase.io import read, write atoms = read(prim_cell) from phonopy import Phonopy phonon = Phonopy( atoms, NNN, ) phonon.generate_displacements( delta, ) pho_super = phonon.get_supercell() pho_disp = phonon.get_supercells_with_displacements() from phonopy.interface import vasp vasp.write_supercells_with_displacements( pho_super, pho_disp, ) import ss_phonopy as ssp phonon = ssp.calc_phonon( calc, phonon, cp_files=cp_files, ) masses = pho_super.masses job_name = 'eig_x{}{}{}_d{:5.3f}_sym{}'.format(NNN[0][0], NNN[1][1], NNN[2][2], delta, phonon._is_symmetry) try: e_s = np.load('{}.npz'.format(job_name)) except: print('Failed to load {}.npz file. Start to solve eigen problem.'.format(job_name)) eigen_set = get_eigen_set( phonon.get_force_constants(), masses, ) np.savez('{}.npz'.format(job_name), w2=eigen_set[0], D=eigen_set[1]) else: print('Successfully loaded {}.npz file!'.format(job_name)) eigen_set = (e_s['w2'], e_s['D']) from ase.md.velocitydistribution import phonon_harmonics from ase import Atoms, units alist = [] for i in seed_range: d_ac, v_ac = phonon_harmonics( None, masses, T *units.kB, eigen_set=eigen_set, # rng=np.random.rand, seed=i, quantum=True, plus_minus=plus_minus, # return_eigensolution=False, # failfast=True, ) new_super = Atoms( cell = pho_super.get_cell(), symbols = pho_super.get_chemical_symbols(), positions = pho_super.get_positions() + d_ac, velocities = v_ac, pbc = True, ) alist.append(new_super) return(alist)
def phonopy_merge(**kwargs): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy from phonopy.units import VaspToTHz from phonopy.harmonic.dynmat_to_fc import get_commensurate_points, DynmatToForceConstants structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() harmonic = kwargs.pop('harmonic') renormalized = kwargs.pop('renormalized') eigenvectors = harmonic.get_array('eigenvectors') frequencies = harmonic.get_array('frequencies') shifts = renormalized.get_array('shifts') # Generate phonopy phonon object 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']) primitive = phonon.get_primitive() supercell = phonon.get_supercell() total_frequencies = frequencies + shifts dynmat2fc = DynmatToForceConstants(primitive, supercell) dynmat2fc.set_dynamical_matrices(total_frequencies / VaspToTHz, eigenvectors) dynmat2fc.run() total_force_constants = dynmat2fc.get_force_constants() # Stores DOS data in DB as a workflow result total_data = ArrayData() total_data.set_array('force_constants', total_force_constants) return {'final_results': total_data} # Start script here # Workflow phonon (at given volume) wf = load_workflow(431) parameters = wf.get_parameters() results = wf.get_results() inline_params = {'structure': results['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'force_constants': results['force_constants']} harmonic = phonopy_commensurate_inline(**inline_params) # At reference volume (at T = 0) wf = load_workflow(432) parameters = wf.get_parameters() results_r = wf.get_results() results_h = wf.get_results() inline_params = {'structure': results_h['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'force_constants': results_h['force_constants'], 'r_force_constants': results_r['r_force_constants']} renormalized = phonopy_commensurate_shifts_inline(**inline_params) inline_params = {'structure': results_h['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'harmonic': harmonic, 'renormalized': renormalized} total = phonopy_merge(**inline_params) print total inline_params = {'structure': results_h['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'force_constants': total['force_constants']} results = phonopy_calculation_inline(**inline_params)[1] band = results['band_structure'] # Phonon Band structure plot plot_data(results['band_structure'])
class PhononBase(TaskElement, PhononYaml): """PhononBase class This is an interface to phonopy. """ def __init__(self, directory=None, name=None, supercell_matrix=None, primitive_matrix=None, nac=False, distance=None, displace_plusminus='auto', displace_diagonal=False, lattice_tolerance=None, force_tolerance=None, pressure_target=None, stress_tolerance=None, max_increase=None, max_iteration=None, min_iteration=None, is_cell_relaxed=False, max_num_atoms=None, stop_condition=None, symmetry_tolerance=None, traverse=False): TaskElement.__init__(self) self._directory = directory if not name: self._name = directory else: self._name = name self._task_type = "phonon" self._supercell_matrix = supercell_matrix if self._supercell_matrix is None: self._primitive_matrix = np.eye(3, dtype='double') else: self._primitive_matrix = primitive_matrix self._nac = nac self._distance = distance self._displace_plusminus = displace_plusminus self._displace_diagonal = displace_diagonal self._lattice_tolerance = lattice_tolerance self._pressure_target = pressure_target self._stress_tolerance = stress_tolerance self._force_tolerance = force_tolerance self._max_increase = max_increase self._max_iteration = max_iteration self._min_iteration = min_iteration self._is_cell_relaxed = is_cell_relaxed self._max_num_atoms = max_num_atoms self._stop_condition = stop_condition self._symmetry_tolerance = symmetry_tolerance self._traverse = traverse self._stage = 0 self._tasks = [] self._energy = None self._born = None self._epsilon = None self._space_group = None self._cell = None self._phonon = None # Phonopy object self._all_tasks = None self._try_collect_forces = True def get_phonon(self): return self._phonon def get_cell(self): if self._is_cell_relaxed: return self._cell else: return self._all_tasks[0].get_cell() def get_energy(self): """Return energies at geometry optimization steps""" return self._energy def get_space_group(self): return self._space_group def set_status(self): if self._stage == 0: task = self._tasks[0] if task.done(): self._space_group = task.get_space_group() status = task.get_status() if status == "done": if not self._evaluate_stop_condition(): self._status = "next" else: self._status = status else: done = True terminate = False for i, task in enumerate(self._tasks): done &= task.done() if (task.get_status() == "terminate" or task.get_status() == "max_iteration"): terminate = True if done: if terminate: self._status = "terminate" else: self._status = "next" self._write_yaml() def begin(self): if not self._job: print("set_job has to be executed.") raise RuntimeError if self._is_cell_relaxed: self._all_tasks = [None] self._set_stage1() else: self._set_stage0() def done(self): return (self._status == "terminate" or self._status == "done" or self._status == "max_iteration" or self._status == "low_symmetry" or self._status == "force_collection_failure" or self._status == "next") def __next__(self): return self.next() def next(self): if self._stage == 0: if self._status == "next": self._energy = self._tasks[0].get_energy() num_atom = len(self._tasks[0].get_cell().get_symbols()) self._comment = self._space_group['international'] self._comment += "\\n%f/%d" % (self._energy, num_atom) self._set_stage1() return self._tasks elif (self._status == "terminate" and self._traverse == "restart"): self._traverse = False self._set_stage0() return self._tasks elif self._stage == 1: # task 1..n: displaced supercells if self._status == "next": if self._collect_forces(): if self._nac: self._set_stage2() return self._tasks else: self._status = "done" else: if self._try_collect_forces: self._status = "displacements" self._log += ("Collection of forces failed. " "Try once more.\n") self._try_collect_forces = False raise StopIteration else: self._status = "force_collection_failure" elif self._status == "terminate" and self._traverse == "restart": self._traverse = False disp_terminated = [] for i, task in enumerate(self._tasks): if task.get_status() == "terminate": disp_terminated.append(i) tasks = self._get_displacement_tasks() self._tasks = [] for i in disp_terminated: self._tasks.append(tasks[i]) self._all_tasks[i + 1] = tasks[i] self._status = "displacements" return self._tasks elif self._stage == 2: if self._status == "next": self._status = "done" self._set_born_and_epsilon() elif self._status == "terminate" and self._traverse == "restart": self._traverse = False self._all_tasks.pop() self._set_stage2() else: pass self._tasks = [] self._write_yaml() raise StopIteration def _set_stage0(self): self._status = "equilibrium" task = self._get_equilibrium_task() self._all_tasks = [task] self._tasks = [task] def _set_stage1(self): self._stage = 1 self._status = "displacements" self._set_phonon() self._tasks = self._get_displacement_tasks() self._all_tasks += self._tasks def _set_stage2(self): self._stage = 2 self._status = "nac" if self._nac == "relax": nac_task = self._get_nac_task(is_cell_relaxed=False) else: nac_task = self._get_nac_task() self._tasks = [nac_task] self._all_tasks += self._tasks def _collect_forces(self): forces = [] for task in self._tasks: forces.append(task.get_properties()['forces'][-1]) if self._phonon.produce_force_constants(forces=forces): write_FORCE_SETS(self._phonon.get_displacement_dataset()) return True else: # This can be due to delay of writting file to file system. return False def _set_born_and_epsilon(self): nac_task = self._tasks[0] born = nac_task.get_born_effective_charge() epsilon = nac_task.get_dielectric_constant() indep_atoms = self._phonon.get_symmetry().get_independent_atoms() supercell = self._phonon.get_supercell() s2u = supercell.get_supercell_to_unitcell_map() u2u = supercell.get_unitcell_to_unitcell_map() indep_atoms_u = [u2u[i] for i in s2u[indep_atoms]] if born is not None and epsilon is not None: self._born = born self._epsilon = epsilon header = "# epsilon and Z* of atoms " header += ' '.join(["%d" % (n + 1) for n in indep_atoms_u]) lines = [header] lines.append(("%13.8f" * 9) % tuple(epsilon.flatten())) for z in born[indep_atoms_u]: lines.append(("%13.8f" * 9) % tuple(z.flatten())) with open("BORN", 'w') as w: w.write('\n'.join(lines)) def _evaluate_stop_condition(self): if self._stop_condition: if "symmetry_operations" in self._stop_condition: num_ops = len(self._space_group['rotations']) if (num_ops < self._stop_condition['symmetry_operations']): self._status = "low_symmetry" return True return False def _set_phonon(self): if self._supercell_matrix is None: cell = sort_cell_by_symbols( get_crystallographic_cell(self.get_cell())) self._supercell_matrix = estimate_supercell_matrix( cell, max_num_atoms=self._max_num_atoms) else: cell = self.get_cell() phonopy_cell = cell2atoms(cell) self._phonon = Phonopy(phonopy_cell, self._supercell_matrix, primitive_matrix=self._primitive_matrix, dynamical_matrix_decimals=14, force_constants_decimals=14, symprec=self._symmetry_tolerance) self._phonon.generate_displacements( distance=self._distance, is_plusminus=self._displace_plusminus, is_diagonal=self._displace_diagonal) supercell = self._phonon.get_supercell() displacements = self._phonon.get_displacements() write_poscar(cell, filename="POSCAR-unitcell") write_poscar_yaml(cell, filename="POSCAR-unitcell.yaml") write_disp_yaml(displacements, supercell) def get_yaml_lines(self): lines = TaskElement.get_yaml_lines(self) if self._is_cell_relaxed: cell = self._cell else: cell = self.get_cell() lines += self._get_phonon_yaml_lines(cell) if self._all_tasks[0] is not None: if self._energy: lines.append("electric_total_energy: %20.10f" % self._energy) return lines
band.append(np.array(q_start) + (np.array(q_end) - np.array(q_start)) / 50 * i) bands.append(band) bulk = read_vasp("POSCAR") phonon = Phonopy(bulk, [[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]], is_auto_displacements=False) primitive = phonon.get_primitive() supercell = phonon.get_supercell() dynmat2fc = DynmatToForceConstants(primitive, supercell) com_points = dynmat2fc.get_commensurate_points() print "Commensurate points" for i, q in enumerate(com_points): print i + 1, q force_sets = parse_FORCE_SETS() phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() phonon.set_qpoints_phonon(com_points, is_eigenvectors=True) frequencies, eigenvectors = phonon.get_qpoints_phonon() dynmat2fc.set_dynamical_matrices(frequencies / VaspToTHz, eigenvectors) dynmat2fc.run()
class PhononFromModel(object): """ This class uses frozen phonon method to calculate the phonon dispersion curves and thermodynamic properties of a structure. The steps are as follows: 1. Displaced structures are obtained from pristine structure via phonopy. 2. The forces of each displaced structures are calculated by model 3. The forces are passed to phonopy object and the force constant are produced 4. Phonon is calculated once the force constant matrix is available. Args: model (model object): a model object that has a method "calculate_forces" that takes in a list of structures and return a list of N*3 forces, where N are the number of atoms in each structures structure (pymatgen structure): a pristine pymatgen structure atom_disp (float): small displacements in Angstrom """ def __init__(self, model=None, structure=None, atom_disp=0.015, **kwargs): self.model = model self.structure = structure self.atom_disp = atom_disp self.qpoints, self.vertices = get_qpoints_and_vertices(self.structure) is_plusminus = kwargs.get('is_plusminus', True) is_diagonal = kwargs.get('is_diagonal', True) is_trigonal = kwargs.get('is_diagonal', False) supercell_matrix = kwargs.get('is_diagonal', None) ph_structure = get_phonopy_structure(self.structure) if supercell_matrix is None: supercell_matrix = np.eye(3) * np.array((1, 1, 1)) self.phonon = Phonopy(unitcell=ph_structure, supercell_matrix=supercell_matrix) self.phonon.generate_displacements(distance=self.atom_disp, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal) disp_supercells = self.phonon.get_supercells_with_displacements() # Perfect supercell structure init_supercell = self.phonon.get_supercell() # Structure list to be returned self.structure_list = [get_pmg_structure(init_supercell)] for c in disp_supercells: if c is not None: self.structure_list.append(get_pmg_structure(c)) forces = self.model.calculate_forces(self.structure_list) self.phonon.set_forces(forces[1:]) self.phonon.produce_force_constants() logging.info("Force constant produced") def get_thermo_properties(self, mesh=[8, 8, 8], t_step=10, t_max=3000, t_min=0): self.phonon.set_mesh(mesh=mesh) self.phonon.set_thermal_properties(t_step=t_step, t_max=t_max, t_min=t_min) plt = self.phonon.plot_thermal_properties() return plt def get_bs_plot(self, points_per_line=50, figsize=(6, 4), ylim=[-1, 5]): bands = [] bands = append_bands(bands, self.qpoints, points_per_line) self.phonon.set_band_structure(bands) q_points, self.distances, self.frequencies, eigvecs = self.phonon.get_band_structure() special_points = self.phonon._band_structure._special_points distance = self.phonon._band_structure._distance plt.figure(figsize=figsize) for j, (d, f) in enumerate(zip(self.distances, self.frequencies)): for i, freqs in enumerate(f.T): if i == 0 and j == 0: plt.plot(d, freqs, "b-", lw=1) else: plt.plot(d, freqs, "b-", lw=1) for sp in special_points: plt.axvline(x=sp, linestyle=':', linewidth=1, color='k') plt.axhline(y=0, linestyle=':', linewidth=1, color='k') plt.xticks(special_points, ["$\mathrm{%s}$" % i for i in self.vertices]) plt.xlabel("Wave vector") plt.ylabel("Frequency (THz)") plt.xlim(0, distance) plt.ylim(ylim) plt.yticks(list(range(ylim[0], ylim[-1]+1))) plt.tight_layout() return plt
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
class PhononBase(TaskElement): """PhononBase class This is an interface to phonopy. """ def __init__(self, directory=None, name=None, supercell_matrix=None, primitive_matrix=None, distance=None, lattice_tolerance=None, force_tolerance=None, pressure_target=None, stress_tolerance=None, max_increase=None, max_iteration=None, min_iteration=None, traverse=False, is_cell_relaxed=False): TaskElement.__init__(self) self._directory = directory if not name: self._name = directory else: self._name = name self._task_type = "phonon" self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._distance = distance self._lattice_tolerance = lattice_tolerance self._pressure_target = pressure_target self._stress_tolerance = stress_tolerance self._force_tolerance = force_tolerance self._max_increase = max_increase self._max_iteration = max_iteration self._min_iteration = min_iteration self._traverse = traverse self._is_cell_relaxed = is_cell_relaxed self._stage = 0 self._tasks = [] self._energy = None self._cell = None self._phonon = None # Phonopy object def get_phonon(self): return self._phonon def get_cell(self): if self._is_cell_relaxed: return self._cell else: return self._phonon_tasks[0].get_cell() def get_energy(self): """Return energies at geometry optimization steps""" return self._energy def set_status(self): done = True terminate = False for task in self._tasks: done &= task.done() if task.get_status() == "terminate": terminate = True if done: if terminate: self._status = "terminate" else: self._status = "next" self._write_yaml() def begin(self): if not self._job: print "set_job has to be executed." raise self._overwrite_settings() if self._is_cell_relaxed: self._phonon_tasks = [None] self._set_stage1() else: self._set_stage0() def end(self): pass def done(self): return ("terminate" in self._status or "done" in self._status or "next" in self._status) def next(self): if self._stage == 0: if "next" in self._status: self._energy = self._tasks[0].get_energy() self._comment = "%s\\n%f" % ( self._tasks[0].get_space_group()['international_standard'], self._energy) self._set_stage1() return self._tasks elif "terminate" in self._status and self._traverse == "restart": self._traverse = False self._set_stage0() return self._tasks else: raise StopIteration else: # task 1..n: displaced supercells if "next" in self._status: self._status = "done" forces = [] for task in self._phonon_tasks[1:]: forces.append(task.get_properties()['forces'][-1]) self._write_FORCE_SETS(forces) self._phonon.set_post_process(self._primitive_matrix, forces, force_constants_decimals=14) self._tasks = [] raise StopIteration elif "terminate" in self._status and self._traverse == "restart": self._traverse = False disp_terminated = [] for i, task in enumerate(self._tasks): if task.get_status() == "terminate": disp_terminated.append(i) tasks = self._get_displacement_tasks()[1:] self._tasks = [] for i in disp_terminated: self._tasks.append(tasks[i]) self._phonon_tasks[i + 1] = tasks[i] self._status = "displacements" return self._tasks else: raise StopIteration def _set_stage0(self): self._status = "equilibrium" task = self._get_equilibrium_task() self._phonon_tasks = [task] self._tasks = [task] def _set_stage1(self): self._stage = 1 self._status = "displacements" self._set_phonon() self._tasks = self._get_displacement_tasks()[1:] self._phonon_tasks += self._tasks def _set_phonon(self): cell = self.get_cell() phonopy_cell = Atoms( cell=cell.get_lattice().T, scaled_positions=cell.get_points().T, symbols=cell.get_symbols()) self._phonon = Phonopy(phonopy_cell, self._supercell_matrix, is_auto_displacements=False) self._phonon.generate_displacements(distance=self._distance, is_diagonal=False) supercell = self._phonon.get_supercell() displacements = self._phonon.get_displacements() write_poscar(cell, "POSCAR-unitcell") write_disp_yaml(displacements, supercell) def _write_FORCE_SETS(self, forces): displacements = [[x[0], x[1:4]] for x in self._phonon.get_displacements()] natom = self._phonon.get_supercell().get_number_of_atoms() write_FORCE_SETS("FORCE_SETS", natom, displacements, forces, verbose=False) def _write_yaml(self): w = open("%s.yaml" % self._directory, 'w') if self._phonon_tasks[0]: if self._lattice_tolerance is not None: w.write("lattice_tolerance: %f\n" % self._lattice_tolerance) if self._stress_tolerance is not None: w.write("stress_tolerance: %f\n" % self._stress_tolerance) w.write("pressure_target: %f\n" % self._pressure_target) w.write("force_tolerance: %f\n" % self._force_tolerance) w.write("max_increase: %f\n" % self._max_increase) w.write("max_iteration: %d\n" % self._max_iteration) w.write("min_iteration: %d\n" % self._min_iteration) w.write("supercell_matrix:\n") for row in self._supercell_matrix: w.write("- [ %3d, %3d, %3d ]\n" % tuple(row)) w.write("primitive_matrix:\n") for row in self._primitive_matrix: w.write("- [ %6.3f, %6.3f, %6.3f ]\n" % tuple(row)) w.write("distance: %f\n" % self._distance) w.write("iteration: %d\n" % self._phonon_tasks[0].get_stage()) if self._energy: w.write("electric_total_energy: %20.10f\n" % self._energy) w.write("status: %s\n" % self._status) w.write("tasks:\n") for task in self._phonon_tasks: if task and task.get_status(): w.write("- name: %s\n" % task.get_name()) w.write(" status: %s\n" % task.get_status()) w.close()
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 )