def gen_perturbations(casfile, supercell='Gamma'): """ Genterate input .cell files for CASTEP singlepoint energy simulations as part of a phonopy phonon calculation of a solid solution. str casfile : filename (incl. path) to .castep file to compute phonons of np.array(3, 3) supercell : size of real space supercell dictates k-points If supercell is 'Gamma', only unit cell displacements are created """ # Load the mixed data chem = casfile.replace('.castep', '') cas = rc.readcas(casfile) mixatoms = cas.extract_struc() spins = cas.get_final_spin() press = cas.get_ext_press() # Create pure cell mixkey = cas.get_mixkey() mapping = mixmap.mixmap(mixatoms, mixkey) pureatoms = mapping.mix2pure(mixatoms) mapping.setcellparams(spins=spins, pressure=press) # Generate perturbed cells (displacements) of pure cell if supercell == 'Gamma': # Gamma-point means only single unit cell supercell = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) GammaPhonon = phonopy.Phonopy(pureatoms, supercell, symprec=1e-4, factor=phonopy.units.VaspToCm) GammaPhonon.generate_displacements(distance=0.05) displcells = GammaPhonon.get_supercells_with_displacements() # Convert displaced pure cells to mixtures and write files for d, displcell in enumerate(displcells): mixdispl = mapping.pure2mix(displcells[d]) cellfile = chem + '_' + str(d) + '.cell' mapping.casprint(mixdispl, cellfile)
def __init__(self, phonopy_disp, force_sets, pa='auto', symprec=1e-4, escale='meV'): self.cell = phonopy.load(phonopy_disp, primitive_matrix=pa) # can we get the primitive matrix here? self.phonon = phonopy.Phonopy( self.cell.unitcell, self.cell.supercell_matrix, primitive_matrix=self.cell.primitive_matrix, symprec=symprec) self.phonon.dataset = phonopy.file_IO.parse_FORCE_SETS( filename=force_sets) self.phonon.produce_force_constants() if escale == 'meV': self.scale = THzToEv * 1000 elif escale == 'THz': self.scale = 1 self.path = None self.labels = None self.are_bands_computed = False self.are_neutron_bands_computed = False self.nions = len(self.phonon.primitive.symbols)
def get_minimum_displacements( unitcell: phonopy.structure.atoms.PhonopyAtoms, supercell_matrix: np.ndarray, displacement_distance: float, phonopy_kw: dict = {}, ): # note: applying phonopy_kw on load is necessary because phonopy will recompute symmetry parprint(f'Getting displacements... ()') phonon = phonopy.Phonopy(unitcell, supercell_matrix, factor=phonopy.units.VaspToTHz, **phonopy_kw) phonon.generate_displacements(distance=displacement_distance) return phonon
def _build_phonopy_object_fhi_aims(self): cwd = os.getcwd() os.chdir(os.path.dirname(os.path.dirname(self.mainfile))) try: cell_obj = read_aims('geometry.in') self.control_parser.mainfile = 'control.in' supercell_matrix = self.control_parser.get('supercell') displacement = self.control_parser.get('displacement', 0.001) sym = self.control_parser.get('symmetry_thresh', 1e-6) try: phonopy_obj = phonopy.Phonopy(cell_obj, supercell_matrix, symprec=sym) phonopy_obj.generate_displacements(distance=displacement) supercells = phonopy_obj.get_supercells_with_displacements() set_of_forces, relative_paths = read_forces_aims( supercells, logger=self.logger) except Exception: self.logger.error("Error generating phonopy object.") set_of_forces = [] phonopy_obj = None relative_paths = [] prep_path = self.mainfile.split("phonopy-FHI-aims-displacement-") # Try to resolve references as paths relative to the upload root. try: for path in relative_paths: abs_path = "%s%s" % (prep_path[0], path) rel_path = abs_path.split(nomad.config.fs.staging + "/")[1].split("/", 3)[3] self.references.append(rel_path) except Exception: self.logger.warn( "Could not resolve path to a referenced calculation within the upload." ) finally: os.chdir(cwd) if set_of_forces: phonopy_obj.set_forces(set_of_forces) phonopy_obj.produce_force_constants() self._phonopy_obj = phonopy_obj
def get_deperm_from_phonopy_sc_to_ase_sc(natoms, repeats): """ Get permutation that maps data for a phonopy supercell into data for an ASE supercell. I.e. at index ``ase_index``, the output will hold ``phonopy_index``. """ import phonopy from phonopy.structure.atoms import PhonopyAtoms assert np.array(repeats).shape == (3, ) # Generate a supercell with a bunch of simple integer positions unitcell_symbols = ['C'] * natoms unitcell_positions = np.outer(np.arange(natoms), [1, 1, 1]) # [0,0,0], [1,1,1], [2,2,2], ... unitcell_lattice = np.eye(3) * natoms p_atoms = PhonopyAtoms(symbols=unitcell_symbols, positions=unitcell_positions, cell=unitcell_lattice) a_atoms = ase.Atoms(symbols=unitcell_symbols, positions=unitcell_positions, cell=unitcell_lattice) # get supercells. (is_symmetry=False in phonopy is because otherwise it may warn about the reduced symmetry of the supercell) a_sc_positions = (a_atoms * repeats).get_positions() p_sc_positions = phonopy.Phonopy( p_atoms, supercell_matrix=np.diag(repeats), is_symmetry=False).supercell.get_positions() # Positions had better be integers a_sc_positions_int = np.rint(a_sc_positions) p_sc_positions_int = np.rint(p_sc_positions) np.testing.assert_allclose(a_sc_positions, a_sc_positions_int, atol=1e-8) np.testing.assert_allclose(p_sc_positions, p_sc_positions_int, atol=1e-8) # Make sure nothing funny happened to atoms within the cell assert (a_sc_positions_int[0] == [0, 0, 0]).all() assert (p_sc_positions_int[0] == [0, 0, 0]).all() # Find how each can be converted into a simple intermediate order. (lexically sorted) deperm_ase_to_lexical = _lexsort_rows(a_sc_positions_int) deperm_phonopy_to_lexical = _lexsort_rows(p_sc_positions_int) assert (a_sc_positions_int[deperm_ase_to_lexical] == p_sc_positions_int[deperm_phonopy_to_lexical]).all() # Compose deperm_lexical_to_ase = np.argsort(deperm_ase_to_lexical) return deperm_phonopy_to_lexical[deperm_lexical_to_ase]
def run_task(self, fw_spec): unitcell = read_vasp("POSCAR-unitcell") phonon = phonopy.Phonopy(unitcell, self.get("supercell")) supercell = phonon.get_supercell() phonon.generate_displacements() supercells = phonon.supercells_with_displacements ids = np.arange(len(supercells)) + 1 write_supercells_with_displacements(supercell, supercells, ids) units = get_default_physical_units("vasp") phpy_yaml = PhonopyYaml(physical_units=units, settings={ 'force_sets': False, 'born_effective_charge': False, 'dielectric_constant': False, 'displacements': True }) phpy_yaml.set_phonon_info(phonon) with open("phonopy_disp.yaml", 'w') as w: w.write(str(phpy_yaml))
def get_minimum_displacements( cachepath: str, unitcell: phonopy.structure.atoms.PhonopyAtoms, supercell_matrix: np.ndarray, displacement_distance: float, phonopy_kw: dict = {}, ): # note: applying phonopy_kw on load is necessary because phonopy will recompute symmetry load = lambda: phonopy.load(cachepath, produce_fc=False, **phonopy_kw) if os.path.exists(cachepath): parprint(f'Found existing {cachepath}') return load() world.barrier() # avoid race condition where rank 0 creates file before others enter parprint(f'Getting displacements... ({cachepath})') if world.rank == 0: phonon = phonopy.Phonopy(unitcell, supercell_matrix, factor=phonopy.units.VaspToTHz, **phonopy_kw) phonon.generate_displacements(distance=displacement_distance) parprint(f'Saving displacements...') phonon.save(cachepath) world.barrier() parprint(f'Loading displacements...') return load()
def phonon(lammps_command, ucell, potential, mpi_command=None, a_mult=3, b_mult=3, c_mult=3, distance=0.01, symprec=1e-5): try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Generate pair_info pair_info = potential.pair_info(ucell.symbols) # Use spglib to find primitive unit cell of ucell convcell = ucell.dump('spglib_cell') primcell = spglib.find_primitive(convcell, symprec=symprec) primucell = am.load('spglib_cell', primcell, symbols=ucell.symbols).normalize() # Initialize Phonopy object phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms'), [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]]) phonon.generate_displacements(distance=distance) # Loop over displaced supercells to compute forces forcearrays = [] for supercell in phonon.supercells_with_displacements: # Save to LAMMPS data file system = am.load('phonopy_Atoms', supercell) system_info = system.dump('atom_data', f='disp.dat') # Define lammps variables lammps_variables = {} lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = pair_info # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = Path(script_dir, 'phonon.template') lammps_script = 'phonon.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write( iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS lmp.run(lammps_command, 'phonon.in', mpi_command=mpi_command) # Extract forces from dump file results = am.load('atom_dump', 'forces.dump') forces = uc.set_in_units(results.atoms.force, lammps_units['force']) forcearrays.append(forces) # Set computed forces phonon.set_forces(forcearrays) # Save to yaml file phonon.save('phonopy_params.yaml') # Compute band structure phonon.produce_force_constants() phonon.auto_band_structure(plot=True) plt.savefig(Path('.', 'band.png'), dpi=400) plt.close() # Compute total density of states phonon.auto_total_dos(plot=True) plt.savefig('total_dos.png', dpi=400) plt.close() # Compute partial density of states phonon.auto_projected_dos(plot=True) plt.savefig('projected_dos.png', dpi=400) plt.close() # Compute thermal properties phonon.run_thermal_properties() phonon.plot_thermal_properties() plt.savefig('thermal.png', dpi=400) plt.close() return {}
def analyze_phonons(m_data): THz_per_invcm = ase.units._c * 1.0e-10 at0 = ase.atoms.Atoms(symbols = m_data['symb'], scaled_positions = np.asarray(m_data['scaled_pos']), masses = np.asarray(m_data['m']), cell = np.asarray(m_data['c']), pbc=[True]*3) phonopy_atoms = phonopy.structure.atoms.PhonopyAtoms(symbols = m_data['symb'], scaled_positions = np.asarray(m_data['scaled_pos']), masses = np.asarray(m_data['m']), cell = np.asarray(m_data['c'])) phonons = phonopy.Phonopy(phonopy_atoms, m_data['n_cell'], factor=m_data['unit_factor']) phonons.generate_displacements(distance=m_data['dx']) phonons.produce_force_constants(forces=np.asarray(m_data['all_forces']), calculate_full_force_constants=True ) phonons.symmetrize_force_constants() # DOS n_dos_mesh = 16 phonons.run_mesh( [n_dos_mesh]*3 ) phonons.run_total_dos() frequencies = phonons.get_total_dos_dict()['frequency_points'] PHdos = phonons.get_total_dos_dict()['total_dos']/len(at0) #contains by columns the frequencies in cm^{-1} and the vDOS #the vDOS ins in units of "number of states/(unit cell x frequency[cm^{-1}])" # i.e. if you integrate the vDOS throughout frequency, it will be 3N, where N is the number of atoms in the unit cell m_data['DOS'] = { 'freq' : frequencies/THz_per_invcm, 'val' : PHdos*THz_per_invcm } # band path if 'band_path' in m_data: band_path = m_data['band_path'] band_n = [20] lat = at0.get_cell().get_bravais_lattice() special_points = lat.get_special_points() path_pts = [] path_labels = [] pt = [] for s in " ".join(band_path).split(): try: qi = float(s) pt.append(qi) except: if len(pt) > 0: raise RuntimeError("got non-float after 1 or more floats") for p in s: try: path_pts.append(np.asarray(special_points[p])) path_labels.append(p) except KeyError: raise RuntimeError("Failed to find special point {}, known {}".format(p, special_points.keys())) if len(pt) == 3: path_pts.append(np.asarray(pt)) path_labels.append("{:.2f}_{:.2f}_{:.2f}".format(*pt)) pt = [] if len(band_n) == 1: band_n *= len(path_pts)-1 assert len(band_n) == len(path_pts)-1 q_pts = [] q_pt_labels = [] for (seg_i, (label, n, p0, p1)) in enumerate(zip(path_labels[0:-1], band_n, path_pts[0:-1], path_pts[1:])): for p_i in range(n): x = float(p_i)/float(n) q_pts.append ( (1.0-x) * p0 + x*p1 ) if p_i == 0: q_pt_labels.append(label) else: q_pt_labels.append(".") q_pts.append(path_pts[-1]) q_pt_labels.append(path_labels[-1]) phonons.run_band_structure([q_pts]) bs = phonons.get_band_structure_dict() m_data['BAND_PATH'] = { 'positions' : bs['distances'][0], 'frequencies' : bs['frequencies'][0]/THz_per_invcm, 'labels' : q_pt_labels }
def calc_phonons(casfile, supercell='Gamma', method=2, postol=0.001, bands=None, verbose=False): """ Compute phonons from completed singlepoint CASTEP calculations of perturbations about a relaxed cell. Note that this function assumes that the perturbed files are in the same directory and use the same naming convention as the parent. str casfile : filename (incl. path) to .castep file to compute phonons of np.array(3, 3) supercell : size of real space supercell dictates k-points If supercell is 'Gamma', only unit cell displacements are created. int method : can be 1 (regenerate displacements and assume the same) or 2 (process all castep files that follow displacement naming convention) float postol : Tolerance (Ang) when ion considered displaced (2 only) np.array() bands : k-points to compute frequencies at bool verbose : function can take a while to run -> prints checkpoints. returns tuple (q_points, distances, frequencies, eigvecs) : output of phonopy_instance.get_band_structure() command. These are all lists of lists of np.arrays, where the relavent entry of frequencies is a list of freqs (in cm-1) of the form np.array(3*phonions) and the relevant entry of eigvecs is a matrix of phonon eigenvectors of the form np.array(3*phonions, 3*phonions). """ # Set up the PHONOPY object for the relaxed cell chem = casfile.replace('.castep', '') cas = rc.readcas(casfile) print("\nreading " + casfile + "\n") mixatoms = cas.extract_struc() mixkey = cas.get_mixkey() mapping = mixmap.mixmap(mixatoms, mixkey) pureatoms = mapping.mix2pure(mixatoms) pureions = pureatoms.get_number_of_atoms() if supercell == 'Gamma': # Gamma-point means only single unit cell supercell = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) bands = [np.array([0.0])] PhononObj = phonopy.Phonopy(pureatoms, supercell, factor=phonopy.units.VaspToCm) if method == 1: """ Generate more displs and ASSUME that they are the same as before. Less stable than method 2 and should only be used as a check. """ # Generate perturbed pure cells PhononObj.generate_displacements(distance=0.05) displcells = PhononObj.get_supercells_with_displacements() Ndispls = len(displcells) sets_of_forces = np.zeros((Ndispls, pureions, 3)) for d in range(Ndispls): displcasfile = chem + '_' + str(d) + '.castep' if verbose: print("reading " + displcasfile) displmix = rc.readcas(displcasfile) displmixforces = displmix.get_forces() displpureforces = mapping.mix2pure_forces(displmixforces) sets_of_forces[d] = displpureforces PhononObj.set_forces(sets_of_forces) elif method == 2: """ Load all the displaced structures and create a displacement_dataset object to load to the PHONOPY instance. """ # Detect displacement files allcasfiles = glob.glob(chem + '_*.castep') displcasfiles = [] for casfile in allcasfiles: n = casfile.replace(chem + '_', '').replace('.castep', '') try: int(n) displcasfiles.append(casfile) except ValueError: pass # file doesn't follow displacement naming convention Ndispls = len(displcasfiles) first_atoms_list = [] pureposns = pureatoms.get_positions() for d in range(Ndispls): # Load displacement files and compute displ relative to parent displcasfile = chem + '_' + str(d) + '.castep' if verbose: print("reading " + displcasfile) displmix = rc.readcas(displcasfile) displmixatoms = displmix.extract_struc() displpureatoms = mapping.mix2pure(displmixatoms) displpureatoms = pinpos.reorder_atoms(pureatoms, displpureatoms) puretotaldispl = displpureatoms.get_positions() - pureposns # Check that exactly one ion is displaced per perturbed cell displions = [] for i in range(pureions): displmagnitude = np.linalg.norm(puretotaldispl[i, :]) if displmagnitude > postol: displions.append(i) if len(displions) > 1: raise IndexError('The following ions were all displaced by ' + 'more than ' + str(postol) + ': ' + ' '.join([str(i) for i in displions])) elif not displions: raise IndexError('No ions were found to be displaced by ' + 'more than ' + str(postol)) displion = displions[0] # Extract (pure) forces and construct first_atoms_list # this list is later fed to construct displacement_dataset displmixforces = displmix.get_forces() displpureforces = mapping.mix2pure_forces(displmixforces) first_atoms_dict = {} first_atoms_dict['number'] = displion first_atoms_dict['displacement'] = puretotaldispl[displion, :] first_atoms_dict['forces'] = displpureforces first_atoms_list += [first_atoms_dict] # Now construct displacement_dataset (for all perturbations) displacement_dataset = {} displacement_dataset['natom'] = pureions displacement_dataset['first_atoms'] = first_atoms_list PhononObj.set_displacement_dataset(displacement_dataset) # End of Method 2 # Forces & displacements are now set so may compute the force constants if verbose: print("\nConstructing force constant matrix") PhononObj.produce_force_constants() phonopy.harmonic.force_constants.set_translational_invariance( PhononObj.force_constants) FCs = PhononObj.force_constants (FC1, FC2, FC3, FC4) = FCs.shape PhononObj.set_force_constants(FCs) # Force constants at this point ALREADY obey the ASR near perfectly # (i.e. columns/rows sum to zero) # Compute frequencies from dynamical matrix if verbose: print("\nDiagonalising dynamical matrix") PhononObj.set_band_structure(bands, is_eigenvectors=True) return PhononObj.get_band_structure()
def phononcalc(lammps_command, ucell, potential, mpi_command=None, a_mult=3, b_mult=3, c_mult=3, distance=0.01, symprec=1e-5, savefile='phonopy_params.yaml', plot=True, lammps_date=None): """ Uses phonopy to compute the phonons for a unit cell structure using a LAMMPS interatomic potential. Parameters ---------- lammps_command :str Command for running LAMMPS. ucell : atomman.System The unit cell system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. a_mult : int, optional The a size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. b_mult : int, optional The b size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. c_mult : int, optional The c size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. distance : float, optional The atomic displacement distance used for computing the phonons. Default value is 0.01. symprec : float, optional Absolute length tolerance to use in identifying symmetry of atomic sites and system boundaries. Default value is 1e-5. savefile: str, optional The name of the phonopy yaml backup file. Default value is 'phonopy_params.yaml'. plot : bool, optional Flag indicating if band structure and DOS figures are to be generated. Default value is True. lammps_date : datetime.date, optional The version date associated with lammps_command. If not given, the version will be identified. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date if lammps_date is None: lammps_date = lmp.checkversion(lammps_command)['date'] # Use spglib to find primitive unit cell of ucell convcell = ucell.dump('spglib_cell') primcell = spglib.find_primitive(convcell, symprec=symprec) primucell = am.load('spglib_cell', primcell, symbols=ucell.symbols).normalize() # Initialize Phonopy object phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms', symbols=potential.elements( primucell.symbols)), [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]], factor=phonopy.units.VaspToTHz) phonon.generate_displacements(distance=distance) # Loop over displaced supercells to compute forces forcearrays = [] for supercell in phonon.supercells_with_displacements: # Save to LAMMPS data file system = am.load('phonopy_Atoms', supercell, symbols=primucell.symbols) system_info = system.dump('atom_data', f='disp.dat', potential=potential) # Define lammps variables lammps_variables = {} lammps_variables['atomman_system_pair_info'] = system_info # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script lammps_script = 'phonon.in' template = read_calc_file('iprPy.calculation.phonon', 'phonon.template') with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS lmp.run(lammps_command, script_name=lammps_script, mpi_command=mpi_command) # Extract forces from dump file forcestructure = am.load('atom_dump', 'forces.dump') forces = uc.set_in_units(forcestructure.atoms.force, lammps_units['force']) forcearrays.append(forces) results = {} # Set computed forces phonon.set_forces(forcearrays) # Save to yaml file phonon.save(savefile) # Compute band structure phonon.produce_force_constants() phonon.auto_band_structure(plot=plot) results['band_structure'] = phonon.get_band_structure_dict() if plot: plt.ylabel('Frequency (THz)') plt.savefig(Path('.', 'band.png'), dpi=400) plt.close() # Compute density of states phonon.auto_total_dos(plot=False) phonon.auto_projected_dos(plot=False) dos = phonon.get_total_dos_dict() dos['frequency'] = uc.set_in_units(dos.pop('frequency_points'), 'THz') dos['projected_dos'] = phonon.get_projected_dos_dict()['projected_dos'] results['dos'] = dos # Compute thermal properties phonon.run_thermal_properties() results['thermal_properties'] = phonon.get_thermal_properties_dict() results['phonon'] = phonon return results
def write_displacement_matrix(poscar_file, fcs_file, T, n, use_smalldisp, imaginary_freq, grid): """ Write the displacement matrix depending of T, force constants and structure, integrated over the Brillouin zone. """ SYMPREC = 1e-5 NQ = grid if not os.path.isfile(poscar_file): sys.exit("The specified POSCAR file does not exist.") na, nb, nc = [int(i) for i in n] if min(na, nb, nc) < 1: sys.exit("All dimensions must be positive integers") poscar = generate_conf.read_POSCAR(poscar_file) natoms = poscar["numbers"].sum() ntot = na * nb * nc * natoms nmodes = 3 * natoms ncells = na * nb * nc matrix = np.zeros((ncells * nmodes, ncells * nmodes), dtype=np.complex128) local_matrix = np.zeros((ncells * nmodes, ncells * nmodes), dtype=np.complex128) if use_smalldisp: for idiag in range(ncells * nmodes): matrix[idiag, idiag] = 0.000001 return matrix if not os.path.isfile(fcs_file): sys.exit("The specified FORCE_CONSTANTS file does not exist.") supercell_matrix = np.diag([na, nb, nc]) structure = phonopy.interface.read_crystal_structure(poscar_file, "vasp")[0] fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file) phonon = phonopy.Phonopy( structure, supercell_matrix, primitive_matrix=None, factor=phonopy.units.VaspToTHz, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=SYMPREC, is_symmetry=True, log_level=0) if os.path.isfile("BORN"): nac_params = phonopy.file_IO.get_born_parameters( open("BORN"), phonon.get_primitive(), phonon.get_primitive_symmetry()) phonon.set_nac_params(nac_params=nac_params) phonon.set_force_constants(fc) masses = phonon.get_supercell().get_masses( ) * codata.physical_constants["atomic mass constant"][0] qpoints = create_mesh(NQ) nqpoints = qpoints.shape[0] # Initializations and preliminaries comm = MPI.COMM_WORLD # get MPI communicator object size = comm.size # total number of processes rank = comm.rank # rank of this process full_qlist = list(range(nqpoints)) qlist = full_qlist[rank::size] for qpt in qlist: local_matrix += qpoint_worker((qpoints[qpt, :], phonon, T, na, nb, nc, imaginary_freq, poscar["positions"])) # Reduce all qpoints comm.Reduce(local_matrix, matrix, op=MPI.SUM, root=0) if rank == 0: matrix *= codata.hbar / 2. / nqpoints / 2. / np.pi / 1e12 # m**2 for i, j in itertools.product( range(ncells * nmodes), range(ncells * nmodes)): matrix[i, j] /= np.sqrt(masses[i // 3] * masses[j // 3]) matrix = 1e18 * matrix.real # nm**2 comm.Barrier() return matrix
def write_displacement_matrix_gamma(sposcar_file, fcs_file, T, n, use_smalldisp, imaginary_freq): """ Writes the displacement matrix depending of T, force constants and structure, using only the gamma-point of the supercell. """ SYMPREC = 1e-5 if not os.path.isfile(sposcar_file): sys.exit("The specified POSCAR file does not exist.") na, nb, nc = [1, 1, 1] poscar = generate_conf.read_POSCAR(sposcar_file) natoms = poscar["numbers"].sum() ntot = na * nb * nc * natoms nmodes = 3 * natoms ncells = na * nb * nc matrix = np.zeros((ncells * nmodes, ncells * nmodes), dtype=np.complex128) if use_smalldisp: for idiag in range(ncells * nmodes): matrix[idiag, idiag] = 0.000001 return matrix if not os.path.isfile(fcs_file): sys.exit("The specified FORCE_CONSTANTS file does not exist.") supercell_matrix = np.diag([na, nb, nc]) structure = phonopy.interface.read_crystal_structure(sposcar_file, "vasp")[0] fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file) phonon = phonopy.Phonopy( structure, supercell_matrix, primitive_matrix=None, factor=phonopy.units.VaspToTHz, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=SYMPREC, is_symmetry=True, log_level=0) if os.path.isfile("BORN"): nac_params = phonopy.file_IO.get_born_parameters( open("BORN"), phonon.get_primitive(), phonon.get_primitive_symmetry()) phonon.set_nac_params(nac_params=nac_params) phonon.set_force_constants(fc) masses = phonon.get_supercell().get_masses( ) * codata.physical_constants["atomic mass constant"][0] nqpoints = 1 print("nqpoints: " + repr(nqpoints)) matrix = qpoint_worker(([0., 0., 0.], phonon, T, na, nb, nc, imaginary_freq, poscar["positions"])) matrix *= codata.hbar / 2. / nqpoints / 2. / np.pi / 1e12 # m**2 for i, j in itertools.product( range(ncells * nmodes), range(ncells * nmodes)): matrix[i, j] /= np.sqrt(masses[i // 3] * masses[j // 3]) matrix = 1e18 * matrix.real # nm**2 return matrix
def write_mode_gruneisen_gamma(poscar_file, sposcar_file, n, fcs_file, fcs_3rd_file, imaginary_freq, filename): """ Write the gruneisen parameters depending of T, force constants and structure, using only the gamma point of the supercell. """ SYMPREC = 1e-5 NQ = 1 if not os.path.isfile(poscar_file): sys.exit("Gruneisen: the specified POSCAR file does not exist.") poscar = generate_conf.read_POSCAR(poscar_file) sposcar = generate_conf.read_POSCAR(sposcar_file) corresp = symmetry.calc_corresp(poscar, sposcar, n) ncells = n[0] * n[1] * n[2] natoms = poscar["numbers"].sum() nmodes = 3 * natoms * ncells supercell_matrix = np.diag([1, 1, 1]) if not os.path.isfile(fcs_file): sys.exit("The specified FORCE_CONSTANTS file does not exist.") if not os.path.isfile(fcs_3rd_file): sys.exit("The specified FORCE_CONSTANTS_3RD file does not exist.") fcs_3rd, atom_info, R_j, R_k, abc = read_3rd_fcs_asinfile( fcs_3rd_file, poscar_file) nblocks = len(fcs_3rd) poscar_positions = sp.dot(poscar["lattvec"], poscar["positions"]).T * 10. cartesian_positions = np.array([ sp.dot(poscar["lattvec"], R_k[iblock])[abc[iblock, 2]] * 10. + poscar_positions[atom_info[iblock, 2], abc[iblock, 2]] for iblock in range(nblocks) ]) structure = phonopy.interface.read_crystal_structure(sposcar_file, "vasp")[0] fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file) phonon = phonopy.Phonopy(structure, supercell_matrix, primitive_matrix=None, factor=phonopy.units.VaspToTHz, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=SYMPREC, is_symmetry=True, log_level=0) if os.path.isfile("BORN"): nac_params = phonopy.file_IO.get_born_parameters( open("BORN"), phonon.get_primitive(), phonon.get_primitive_symmetry()) phonon.set_nac_params(nac_params=nac_params) phonon.set_force_constants(fc) masses = phonon.get_unitcell().get_masses() massesi = np.array([ masses[corresp.index([atom_info[iblock, 0], 0, 0, 0])] for iblock in range(nblocks) ]) massesj = np.array([ masses[corresp.index([atom_info[iblock, 1], 0, 0, 0])] for iblock in range(nblocks) ]) print("nqpoints: " + str(NQ)) mesh = [[0., 0., 0.]] w_grun = [] m_grun = [] f_grun = [] tot_cv = 0. r = range(nmodes) for q in mesh: f, psi = phonon.get_frequencies_with_eigenvectors(q) factorj = 1.0 for im in r: if (f[im] < -1.e-4): print("ATTENTION: IMAGINARY FREQUENCIES ->" " CONVERTED TO POSITIVE VALUE") if imaginary_freq == -1: f[im] = abs(f[im]) else: f[im] = imaginary_freq if (f[im] > 1.e-4): lpsi = psi[:, im].reshape(-1, 3) psii = np.conj( np.array([ lpsi[corresp.index([atom_info[iblock, 0], 0, 0, 0]), abc[iblock, 0]] for iblock in range(nblocks) ])) psij = np.array([ lpsi[corresp.index([ atom_info[iblock, 1], R_j[iblock, 0] % n[0], R_j[iblock, 1] % n[1], R_j[iblock, 2] % n[2] ]), abc[iblock, 1]] for iblock in range(nblocks) ]) m_gruni = mode_gruneisen(f[im], psii, psij, massesi, massesj, cartesian_positions, fcs_3rd, factorj).real * ncells m_grun.append(m_gruni) f_grun.append(f[im]) else: m_grun.append(np.array([0., 0., 0.])) f_grun.append(f[im]) with open(filename, 'w') as f: for i in range(len(f_grun)): f.write( str(f_grun[i]) + ' ' + str(m_grun[i][0]) + ' ' + str(m_grun[i][1]) + ' ' + str(m_grun[i][2]) + '\n') m_grun = np.array(m_grun) f_grun = np.array(f_grun) return f_grun, m_grun
def write_mode_gruneisen(poscar_file, n, fcs_file, fcs_3rd_file, imaginary_freq, grid, filename): """ Write the gruneisen parameters depending of T, force constants and structure, in the whole Brillouin zone. """ SYMPREC = 1e-5 if not os.path.isfile(poscar_file): sys.exit("Gruneisen: the specified POSCAR file does not exist.") poscar = generate_conf.read_POSCAR(poscar_file) ncells = n[0] * n[1] * n[2] natoms = poscar["numbers"].sum() nmodes = 3 * natoms supercell_matrix = np.diag([n[0], n[1], n[2]]) if not os.path.isfile(fcs_file): sys.exit("The specified FORCE_CONSTANTS file does not exist.") if not os.path.isfile(fcs_3rd_file): sys.exit("The specified FORCE_CONSTANTS_3RD file does not exist.") fcs_3rd, atom_info, R_j, R_k, abc = read_3rd_fcs_asinfile( fcs_3rd_file, poscar_file) nblocks = len(fcs_3rd) poscar_positions = sp.dot(poscar["lattvec"], poscar["positions"]).T * 10. cartesian_positions = np.array([ sp.dot(poscar["lattvec"], R_k[iblock])[abc[iblock, 2]] * 10. + poscar_positions[atom_info[iblock, 2], abc[iblock, 2]] for iblock in range(nblocks) ]) structure = phonopy.interface.read_crystal_structure(poscar_file, "vasp")[0] fc = phonopy.file_IO.parse_FORCE_CONSTANTS(fcs_file) phonon = phonopy.Phonopy(structure, supercell_matrix, primitive_matrix=None, factor=phonopy.units.VaspToTHz, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=SYMPREC, is_symmetry=True, log_level=0) if os.path.isfile("BORN"): nac_params = phonopy.file_IO.get_born_parameters( open("BORN"), phonon.get_primitive(), phonon.get_primitive_symmetry()) phonon.set_nac_params(nac_params=nac_params) phonon.set_force_constants(fc) masses = phonon.get_unitcell().get_masses() massesi = masses[atom_info[:, 0]] massesj = masses[atom_info[:, 1]] NQ = grid mesh = create_mesh(NQ) nqpoints = mesh.shape[0] m_grun = [] f_grun = [] r = range(nmodes) # Initializations and preliminaries comm = MPI.COMM_WORLD # get MPI communicator object size = comm.size # total number of processes rank = comm.rank # rank of this process # Scatter qpoints across cores. full_qlist = list(range(nqpoints)) qlist = full_qlist[rank::size] for qpt in qlist: q = mesh[qpt, :] f, psi = phonon.get_frequencies_with_eigenvectors(q) factor = np.exp(2j * np.pi * np.dot(q, poscar["positions"])) factorj = np.exp(2j * np.pi * np.dot(R_j, q)) for im in r: if (f[im] < -1.e-4): print("ATTENTION: IMAGINARY FREQUENCIES ->" " CONVERTED TO POSITIVE VALUE") if imaginary_freq == -1: f[im] = abs(f[im]) else: f[im] = imaginary_freq if (f[im] > 1.e-4): lpsi = psi[:, im].reshape(-1, 3) * factor[:, np.newaxis] psii = np.conj( np.array([ lpsi[atom_info[iblock, 0], abc[iblock, 0]] for iblock in range(nblocks) ])) psij = np.array([ lpsi[atom_info[iblock, 1], abc[iblock, 1]] for iblock in range(nblocks) ]) m_gruni = mode_gruneisen(f[im], psii, psij, massesi, massesj, cartesian_positions, fcs_3rd, factorj).real m_grun.append(m_gruni) f_grun.append(f[im]) else: m_grun.append(np.array([0., 0., 0.])) f_grun.append(f[im]) m_grun = np.array(m_grun) f_grun = np.array(f_grun) # Gather all qpoints m_grun = comm.gather(m_grun, root=0) f_grun = comm.gather(f_grun, root=0) if rank == 0: m_grun = np.concatenate(m_grun) f_grun = np.concatenate(f_grun) with open(filename, 'w') as f: for i in range(f_grun.shape[0]): f.write( str(f_grun[i]) + ' ' + str(m_grun[i][0]) + ' ' + str(m_grun[i][1]) + ' ' + str(m_grun[i][2]) + '\n') m_grun = comm.bcast(m_grun, root=0) f_grun = comm.bcast(f_grun, root=0) return f_grun, m_grun
def do_phonons(bulk_struct_tests, n_supercell, band_paths=None, dx=0.01): if band_paths is not None and len(band_paths) != len(bulk_struct_tests): raise RuntimeError( "got {} bulk structs but different {} band paths".format( len(bulk_struct_tests), len(band_paths))) properties = {} for bulk_i, bulk_struct_test in enumerate(bulk_struct_tests): at0 = get_relaxed_bulk(bulk_struct_test) # magnetic moments could change the symmetry, ignored here for now phonopy_atoms = phonopy.structure.atoms.PhonopyAtoms( symbols=at0.get_chemical_symbols(), scaled_positions=at0.get_scaled_positions(), masses=at0.get_masses(), cell=at0.get_cell()) phonons = phonopy.Phonopy(phonopy_atoms, np.diag([n_supercell] * 3), factor=phonopy.units.VaspToTHz) phonons.generate_displacements(distance=dx) # convert from chosen Phonopy units (THz) to cm^-1 THz_per_invcm = ase.units._c * 1.0e-10 #################################################################################################### # if args.SETUP: sys.stderr.write("SETUP\n") displ_supercells = phonons.get_supercells_with_displacements() at0_sc = at0 * n_supercell # reorder at0_sc to match order from phonopy at = ase.Atoms(pbc=True, cell=displ_supercells[0].get_cell(), positions=displ_supercells[0].get_positions(), numbers=displ_supercells[0].get_atomic_numbers()) matched_pos = np.zeros(at.positions.shape) mapping = [-1] * len(at) at0_sc_scaled_pos = at0_sc.get_scaled_positions() at_scaled_pos = at.get_scaled_positions() for at0_sc_i in range(len(at)): scaled_dists = at0_sc_scaled_pos[at0_sc_i] - at_scaled_pos scaled_dists -= np.round(scaled_dists) closest_i = np.argmin(np.linalg.norm(scaled_dists, axis=1)) matched_pos[at0_sc_i] = at.positions[closest_i] mapping[closest_i] = at0_sc_i if -1 in mapping: raise RuntimeError( "Failed to map orig and displaced atom positions") at0_sc = at0_sc[mapping] # ase.io.write("UNDISPL.{}".format(file_label), at0_sc) # create displaced cells sys.stderr.write("Creating {} displacements\n".format( len(displ_supercells))) at_sets = [] for (displ_i, displ_config) in enumerate(displ_supercells): at = ase.Atoms(pbc=True, cell=displ_config.get_cell(), positions=displ_config.get_positions(), numbers=displ_config.get_atomic_numbers()) at_sets.append(at) # ase.io.write("DISPL_{}.{}".format(displ_i, file_label), at) #################################################################################################### all_forces = [] # if args.CALCULATE: sys.stderr.write("CALCULATE\n") sys.stderr.write("Calculating for {} displacements\n".format( len(at_sets))) evaluate(at0_sc) f0 = at0_sc.get_forces() # np.savetxt("FORCES.UNDISPL.{}".format(file_label), f0) for (displ_i, at) in enumerate(at_sets): at0_sc.set_positions(at.positions) at0_sc.set_cell(at.get_cell()) sys.stderr.write( "start evaluate displacement {}\n".format(displ_i)) evaluate(at0_sc) f = at0_sc.get_forces() all_forces.append(f) # np.savetxt("FORCES.DISPL_{}.{}".format(displ_i, file_label), all_forces[-1]) #################################################################################################### # if args.PROCESS: sys.stderr.write("PROCESS\n") Nat = all_forces[0].shape[0] for f in all_forces: f -= f0 f -= np.outer(np.ones(Nat), np.sum(f, axis=0) / Nat) all_forces = np.asarray(all_forces) properties[bulk_struct_test] = { 'dx': dx, 'all_forces': all_forces.tolist(), 'symb': at0.get_chemical_symbols(), 'scaled_pos': at0.get_scaled_positions().tolist(), 'm': at0.get_masses().tolist(), 'c': at0.get_cell().tolist(), 'n_cell': np.diag([n_supercell] * 3).tolist(), 'unit_factor': phonopy.units.VaspToTHz, 'band_path': band_paths[bulk_i] } #################################################################################################### return properties