def phonon_run(runID, save_to_db=False, plot_bands=False): print("Running ID %d" % (runID)) db = connect(db_name) atoms = db.get_atoms(id=runID) #view(atoms) #atoms = bulk("Al") #atoms = atoms*(2,1,1) #calc = EAM(potential="/home/davidkl/Documents/EAM/Al-LEA.eam.alloy") calc = EAM(potential="/home/davidkl/Documents/EAM/mg-al-set.eam.alloy") atoms.set_calculator(calc) #calc = gp.GPAW( mode=gp.PW(600), xc="PBE", kpts=(4,4,4), nbands="120%", symmetry="off" ) #atoms.set_calculator(calc) ph = Phonons(atoms, calc, supercell=(3, 3, 3), name=wrk + "/phonon_files/phonon%d" % (runID)) ph.run() #return ph.read(acoustic=True) omega_e, dos_e = ph.dos(kpts=(30, 30, 30), npts=1000, delta=5E-4) if (plot_bands): points = ibz_points['fcc'] G = points['Gamma'] X = points['X'] W = points['W'] K = points['K'] L = points['L'] U = points['U'] point_names = ['$\Gamma$', 'X', 'U', 'L', '$\Gamma$', 'K'] path = [G, X, U, L, G, K] path_kc, q, Q = bandpath(path, atoms.cell, 100) omega_kn = 1000.0 * ph.band_structure(path_kc) figb = plt.figure() axb = figb.add_subplot(1, 1, 1) for n in range(len(omega_kn[0])): omega_n = omega_kn[:, n] axb.plot(q, omega_n) plt.show() if (save_to_db): # Store the results in the database db.update(runID, has_dos=True) manager = cpd.PhononDOS_DB(db_name) # Extract relevant information from the atoms database row = db.get(id=runID) name = row.name atID = row.id manager.save(name=name, atID=atID, omega_e=omega_e, dos_e=dos_e)
class RamanPhonons(RamanData): def __init__(self, atoms, *args, **kwargs): RamanData.__init__(self, atoms, *args, **kwargs) for key in ['txt', 'exext', 'exname']: kwargs.pop(key, None) kwargs['name'] = kwargs.get('name', self.name) self.vibrations = Phonons(atoms, *args, **kwargs) self.delta = self.vibrations.delta self.indices = self.vibrations.indices self.kpts = (1, 1, 1) @property def kpts(self): return self._kpts @kpts.setter def kpts(self, kpts): if not hasattr(self, '_kpts') or kpts != self._kpts: self._kpts = kpts self.kpts_kc = monkhorst_pack(self.kpts) if hasattr(self, 'im_r'): del self.im_r # we'll have to recalculate def calculate_energies_and_modes(self): if not self._already_read: if hasattr(self, 'im_r'): del self.im_r self.read() if not hasattr(self, 'im_r'): self.timer.start('band_structure') omega_kl, u_kl = self.vibrations.band_structure( self.kpts_kc, modes=True, verbose=self.verbose) self.im_r = self.vibrations.m_inv_x self.om_Q = omega_kl.ravel().real # energies in eV self.modes_Qq = u_kl.reshape(len(self.om_Q), 3 * len(self.atoms)) self.modes_Qq /= self.im_r self.om_v = self.om_Q # pre-factors for one vibrational excitation with np.errstate(divide='ignore', invalid='ignore'): self.vib01_Q = np.where(self.om_Q > 0, 1. / np.sqrt(2 * self.om_Q), 0) # -> sqrt(amu) * Angstrom self.vib01_Q *= np.sqrt(u.Ha * u._me / u._amu) * u.Bohr self.timer.stop('band_structure')
def ase_phonon_calc( struct, calc=None, kpoints=[1, 1, 1], ftol=0.01, force_clean=False, name="asephonon", ): """Calculate phonon modes of a molecule using ASE and a given calculator. The system will be geometry optimized before calculating the modes. A report of the phonon modes will be written to a file and arrays of the eigenvectors and eigenvalues returned. | Args: | struct (ase.Atoms): Atoms object with to calculate modes for. | calc (ase.Calculator): Calculator for energies and forces (if not | present, use the one from struct) | kpoints (np.ndarray): Kpoint grid for phonon calculation. If None, just | do a Vibration modes calculation (default is [1,1,1]) | ftol (float): Tolerance for geometry optimisation (default | is 0.01 eV/Ang) | force_clean (bool): If True, force a deletion of all phonon files | and recalculate them | Returns: | evals (float[k-points][modes]): Eigenvalues of phonon modes | evecs (float[k-points][modes][ions][3]): Eigenvectors of phonon modes | struct (ase.Atoms): Optimised structure """ N = len(struct) if calc is None: calc = struct.calc struct = struct.copy() calc.atoms = struct struct.calc = calc dyn = BFGS(struct, trajectory="geom_opt.traj") dyn.run(fmax=ftol) # Calculate phonon modes vib_pbc = kpoints is not None if vib_pbc: vib = Phonons(struct, calc, name=name) else: vib = Vibrations(struct, name=name) if force_clean: vib.clean() vib.run() if vib_pbc: vib.read(acoustic=True) path = monkhorst_pack(kpoints) evals, evecs = vib.band_structure(path, True) else: vib.read() path = np.zeros((1, 3)) # One axis added since it's like the gamma point evals = np.real(vib.get_energies()[None]) evecs = np.array([vib.get_mode(i) for i in range(3 * N)])[None] # eV to cm^-1 evals *= ((cnst.electron_volt / cnst.h) / cnst.c) / 100.0 # Normalise eigenvectors evecs /= np.linalg.norm(evecs, axis=(2, 3))[:, :, None, None] return ASEPhononData(evals, evecs, path, struct)
class ThermalProperties(object): """Class with methods to calculate thermal properties of a structure within the QHA Attributes ---------- name : str Base name for the files used for the phonon calculations. structure : ATAT2EMT ATAT structure file describing the primitive cell. atoms : ase.Atoms Atoms object with the primitive cell. calc : ase.Calculator Callable returning a Calculator to use for all energy and force calculations. n_atoms : int Number of atoms in the primitive cell. temperature : Iterable of float Temperatures at which to calculate temperature dependent properties. strains : Iterable of float Strains to apply to the atoms for volume dependent properties. strained_thermo : list of ThermalProperties List with classes for the thermal properties of strained versions of the atoms object. sjeos : SJEOS Class to fit the equation of state at constant temperature. base_dir : str Base directory for the calculations. (WARNING: not fully implemented) verbosity : int >= 0 Verbosity level. do_plotting : bool Determines if the plottings are activated. supercell_size : tuple of 3 int Size of the supercell to do the phonon calculations. thermo : ase.CrystalThermo Class to delegate the calculation of some thermodynamic properties. phonons : ase.Phonons Class to perform phonon calculations using the supercell approach. phonon_kpts_mp : (N, 3) np.ndarray Monkhorst-Pack k-point grid. phonon_energy_mp : (N,) np.ndarray Energies of the corresponding MP k-points. phonon_energy : np.ndarray Energies to calculate the Phonon density of states. phonon_dos : np.ndarray Phonon density of states at given energies. Parameters ---------- atoms : calc : supercell_size : atat_structure : plot : verbosity : name : """ def __init__(self, atoms=None, calc=None, supercell_size=5, atat_structure=None, plot=False, verbosity=0, name='thermo'): # relaxed structure self.name = name self.structure = None self.atoms = None self.calc = None self.n_atoms = 0 self.temperature = None self.strains = None self.strained_thermo = [] if atoms is not None and atat_structure is not None: print('ERROR: only atoms OR atat_structure can be specified') return if atoms is not None: self.atoms = atoms self.n_atoms = len(self.atoms) if self.atoms.calc is None: assert calc is not None self.atoms.set_calculator(calc()) self.calc = calc else: self.calc = atoms.calc elif atat_structure is not None: assert calc is not None self.calc = calc self.structure = ATAT2EMT(atat_structure, calc(), to_niggli=True, verbosity=verbosity) self.structure.atoms.wrap() self.atoms = self.structure.atoms self.n_atoms = len(self.atoms) # isgn = spglib.get_symmetry_dataset(self.atoms, symprec=1e-3)['number'] # self.symmetry = el.crystal_system(isgn) self.sjeos = SJEOS() self.base_dir = os.getcwd() self.verbosity = verbosity self.do_plotting = plot if isinstance(supercell_size, int): self.supercell_size = (supercell_size, supercell_size, supercell_size) else: assert len(supercell_size) == 3 self.supercell_size = supercell_size self.get_phonons() self.thermo = CrystalThermo( phonon_energies=self.phonon_energy, phonon_DOS=self.phonon_dos, potentialenergy=self.atoms.get_potential_energy(), formula_units=self.n_atoms) def set_temperature(self, temperature, save_at='.'): """Set the temperature grid. Parameters ---------- temperature : iterable of float Iterable containing the temperatures at which to calculate the properties. save_at : string Path (relative or absolute) in which to store the value. """ if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'T.dat') np.savetxt(save_name, temperature) self.temperature = temperature def get_phonons(self, kpts=(50, 50, 50), npts=5000): """Calculate the phonon spectrum and DOS. Parameters ---------- kpts : tuple Number of points in each directions of the k-space grid. npts : int Number of energy points to calculate the DOS at. """ self.phonons = Phonons(self.atoms, self.calc(), supercell=self.supercell_size, delta=0.05, name=self.name) self.phonons.run() # Read forces and assemble the dynamical matrix self.phonons.read(acoustic=True) self.phonon_kpts_mp = monkhorst_pack(kpts) self.phonon_energy_mp = self.phonons.band_structure( self.phonon_kpts_mp) self.phonon_energy, self.phonon_dos = \ self.phonons.dos(kpts=kpts, npts=npts, delta=5e-4) def get_volume_phonons(self, nvolumes=5, max_strain=0.02): """Calculate the volume dependent phonons. Parameters ---------- nvolumes : int > 0 Number of volumes to calculate the phonon spectrum. max_strain : float > 0 Maximum (isotropic) strain used to deform equilibrium volume. """ strains = np.linspace(-max_strain, max_strain, nvolumes) load = False if self.strains is None: self.strains = strains else: if not (strains == self.strains).all(): self.strains = strains self.strained_thermo = [] else: load = True strain_matrices = [np.eye(3) * (1 + s) for s in self.strains] atoms = self.atoms cell = atoms.cell for i, s in enumerate(strain_matrices): satoms = atoms.copy() satoms.set_cell(np.dot(cell, s.T), scale_atoms=True) if load: pass else: satoms.set_calculator(None) sthermo = ThermalProperties(satoms, self.calc, name='thermo_{:.2f}'.format( self.strains[i])) self.strained_thermo.append(sthermo) def get_volume_energy(self, temperature=None, nvolumes=5, max_strain=0.02): """Return the volume dependent (Helmholtz) energy. Parameters ---------- temperature : float > 0 Temeprature at which the volume-energy curve is calculated. nvolumes : int > 0 Number of volumes to calculate the energy at. max_strain : float > 0 Maximum (isotropic) strain used to deform equilibrium volume. save_at : string Path (relative or absolute) in which to store the value. Returns ------- volume : list of double Volumes at which the entropy was calculated. energy : list of double Helmholtz energy for each of the volumes. """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if isinstance(temperature, collections.Iterable): volume_energy = [ self.get_volume_energy(T, nvolumes, max_strain) for T in temperature ] return volume_energy self.get_volume_phonons(nvolumes, max_strain) energy = [] volume = [] for sthermo in self.strained_thermo: energy.append( sthermo.get_helmholtz_energy(temperature, save_at=None)) volume.append(sthermo.atoms.get_volume()) return volume, energy def get_volume_entropy(self, temperature=None, nvolumes=5, max_strain=0.02): """Return the volume dependent entropy. Parameters ---------- temperature : float > 0 Temeprature at which the volume-entropy curve is calculated. nvolumes : int > 0 Number of volumes to calculate the entropy at. max_strain : float > 0 Maximum (isotropic) strain used to deform equilibrium volume. save_at : string Path (relative or absolute) in which to store the value. Returns ------- volume : list of double Volumes at which the entropy was calculated. entropy : list of double Entropy for each of the volumes. """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if isinstance(temperature, collections.Iterable): volume_entropy = [ self.get_volume_entropy(T, nvolumes, max_strain) for T in temperature ] return volume_entropy self.get_volume_phonons(nvolumes, max_strain) entropy = [] volume = [] for sthermo in self.strained_thermo: entropy.append(sthermo.get_entropy(temperature, save_at=None)) volume.append(sthermo.atoms.get_volume()) return volume, entropy def get_entropy(self, temperature=None, save_at='.'): """Return entropy per atom in eV / atom. Parameters ---------- temperature : float > 0 Temeprature at which the Helmholtz energy is calculated. save_at : string Path (relative or absolute) in which to store the value. Returns ------- entropy : float Entropy in eV / atom Notes ----- To convert to SI units, divide by units.J. At the moment only vibrational entropy is included. Electronic entropy can be included if the calculator provides the electronic DOS. """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'S.dat') if isinstance(temperature, collections.Iterable): vib_entropy = [ self.get_entropy(T, save_at=None) for T in temperature ] if save_at is not None: np.savetxt(save_name, vib_entropy) return np.array(vib_entropy) if temperature == 0.: if save_at is not None: np.savetxt(save_name, np.asarray([0.])) return 0. vib_entropy = self.thermo.get_entropy(temperature, self.verbosity) if save_at is not None: np.savetxt(save_name, vib_entropy) return vib_entropy def get_helmholtz_energy(self, temperature=None, save_at='.'): """Return Helmholtz energy per atom in eV / atom. Parameters ---------- temperature : float > 0 Temeprature at which the Helmholtz energy is calculated. save_at : string Path (relative or absolute) in which to store the value. Returns ------- helmholtz_energy : float Helmholtz energy in eV / atom Notes ----- To convert to SI units, divide by units.J. """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'F.dat') if isinstance(temperature, collections.Iterable): helmholtz_energy = [ self.get_helmholtz_energy(T, save_at=None) for T in temperature ] if save_at is not None: np.savetxt(save_name, helmholtz_energy) return np.array(helmholtz_energy) if temperature == 0.: helmholtz_energy = self.get_zero_point_energy( ) + self.thermo.potentialenergy if save_at is not None: np.savetxt(save_name, helmholtz_energy) return helmholtz_energy helmholtz_energy = self.thermo.get_helmholtz_energy( temperature, self.verbosity) if save_at is not None: np.savetxt(save_name, helmholtz_energy) return helmholtz_energy def get_internal_energy(self, temperature=None, save_at='.'): """Return internal energy per atom in eV / atom. Parameters ---------- temperature : float > 0 Temeprature at which the internal energy is calculated. save_at : string Path (relative or absolute) in which to store the value. Returns ------- internal_energy : float Internal energy in eV / atom Notes ----- To convert to SI units, divide by units.J. """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'U.dat') if isinstance(temperature, collections.Iterable): internal_energy = [ self.get_internal_energy(T, save_at=None) for T in temperature ] if save_at is not None: np.savetxt(save_name, internal_energy) return np.array(internal_energy) if temperature == 0.: internal_energy = self.get_zero_point_energy( ) + self.thermo.potentialenergy if save_at is not None: np.savetxt(save_name, internal_energy) return internal_energy internal_energy = self.thermo.get_internal_energy( temperature, self.verbosity) if save_at is not None: np.savetxt(save_name, internal_energy) return internal_energy def get_zero_point_energy(self): """Return the Zero Point Energy in eV / atom. Returns ------- zpe: float Zero point energy in eV / atom. """ zpe_list = self.phonon_energy / 2. zpe = np.trapz(zpe_list * self.phonon_dos, self.phonon_energy) / self.n_atoms return zpe def get_specific_heat(self, temperature=None, save_at='.'): """Return heat capacity per atom in eV / atom K. Parameters ---------- temperature : float > 0 Temeprature at which the specific heat is calculated. save_at : string Path (relative or absolute) in which to store the value. Returns ------- C_V : float Specific heat in eV / atom K Notes ----- To convert to SI units, multiply by (units.mol / units.J). """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'Cv.dat') if isinstance(temperature, collections.Iterable): C_V = [ self.get_specific_heat(T, save_at=None) for T in temperature ] if save_at is not None: np.savetxt(save_name, C_V) return np.array(C_V) if temperature == 0.: if save_at is not None: np.savetxt(save_name, np.asarray([0.])) return 0. if self.phonon_energy[0] == 0.: self.phonon_energy = np.delete(self.phonon_energy, 0) self.phonon_dos = np.delete(self.phonon_dos, 0) i2kT = 1. / (2. * units.kB * temperature) arg = self.phonon_energy * i2kT C_v = units.kB * arg**2 / np.sinh(arg)**2 C_V = np.trapz(C_v * self.phonon_dos, self.phonon_energy) / self.n_atoms if save_at is not None: np.savetxt(save_name, np.asarray([C_V])) return C_V def get_thermal_expansion(self, temperature=None, exp_norm_temp=None, nvolumes=5, max_strain=0.02, ntemperatures=5, delta_t=1., save_at='.'): """Return the isotropic volumetric thermal expansion in K^-1. Parameters ---------- temperature : float > 0 Temeprature at which the expansion coefficient is calculated. exp_norm_temp : float > 0 Temperature for the normalization of the thermal expansion (usually to compare with experiment). nvolumes : int > 0 Number of volumes to fit the equation of state to extract equilibrium volumes. max_strain : float > 0 Maximum strain used to fit the equation of state to extract equilibrium volumes. ntemperatures : int > 0 Number of temperatures to approximate the temperature derivative of the volume. delta_t : float >0 Temperature step to approximate the temperature derivative of the volume. save_at : string Path (relative or absolute) in which to store the value. Returns ------- : double Isotropic volumetric thermal expansion in K^-1 """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'thermal_expansion.dat') if isinstance(temperature, collections.Iterable): alpha_v = [ self.get_thermal_expansion(T, exp_norm_temp, nvolumes, max_strain, ntemperatures, delta_t, save_at=None) for T in temperature ] if save_at is not None: np.savetxt(save_name, alpha_v) return np.array(alpha_v) max_delta_t = (ntemperatures - 1) * delta_t if temperature - max_delta_t / 2. > 0.: temperatures = np.linspace(temperature - max_delta_t / 2., temperature + max_delta_t / 2., ntemperatures) t0 = (ntemperatures - 1) / 2 mode = 'c' else: ntemperatures = (ntemperatures + 2) / 2 temperatures = np.linspace(temperature, temperature + max_delta_t / 2., ntemperatures) t0 = 0 mode = 'f' print(temperatures, ntemperatures) # 1.- Get V-F points Vs = [] Fs = [] for T in temperatures: V, F = self.get_volume_energy(T, nvolumes, max_strain) Vs.append(V) Fs.append(F) # 2.- Fit EOS to V-F points for each T self.sjeos.clean() for i in range(ntemperatures): V = np.asarray(Vs[i]) F = np.asarray(Fs[i]) self.sjeos.fit(V, F) # 3.- Numerical derivative dV/dT V0s = self.sjeos.get_equilibrium_volume(mean=True) fd = FD(temperatures) dV_dT = fd.derivative(1, t0, V0s, acc_order=2, mode=mode) if self.do_plotting: plt.plot(temperatures, V0s) # 4a.- Normalize by volume at temperature (same as derivative) if exp_norm_temp is None: return dV_dT / V0s[(ntemperatures - 1) / 2] # 4b.- Normalize by volume at some give reference temperature (different from derivative) V, F = self.get_volume_energy(exp_norm_temp, nvolumes, max_strain) self.sjeos.clean() self.sjeos.fit(np.asarray(V), np.asarray(F)) V_norm = self.sjeos.get_equilibrium_volume(mean=True) alpha_v = dV_dT / V_norm if save_at is not None: np.savetxt(save_name, alpha_v) return alpha_v def get_gruneisen(self, temperature=None, nvolumes=5, max_strain=0.02, save_at='.'): r"""Return the Gr\"uneisen parameter. Parameters ---------- temperature : float > 0 Temeprature at which the expansion coefficient is calculated. nvolumes : int > 0 Number of volumes to fit the equation of state to extract equilibrium volumes. max_strain : float > 0 Maximum strain used to fit the equation of state to extract equilibrium volumes. save_at : string Path (relative or absolute) in which to store the value. Returns ------- gruneisen : double Gr\"uneisen parameter. Notes ----- The Gr\"uneisen parameter is calculated as .. math :: \gamma=\frac{C_v}{V}\left.\frac{\partial S}{\partial V}\right|_T """ if temperature is None and self.temperature is None: print( 'ERROR. You nee to specify a temperature for the calculations.' ) return elif temperature is None: temperature = self.temperature if save_at is not None: if not os.path.exists(save_at): os.makedirs(save_at) save_name = os.path.join(save_at, 'gruneisen.dat') if isinstance(temperature, collections.Iterable): gruneisen = [ self.get_gruneisen(T, nvolumes, max_strain, save_at=None) for T in temperature ] if save_at is not None: np.savetxt(save_name, gruneisen) return gruneisen self.get_volume_phonons(nvolumes, max_strain) C_V = self.get_specific_heat(temperature) V, F = self.get_volume_energy(temperature, nvolumes, max_strain) self.sjeos.clean() self.sjeos.fit(np.asarray(V), np.asarray(F)) V_0 = self.sjeos.get_equilibrium_volume(mean=True)[0] V, S = self.get_volume_entropy(temperature, nvolumes, max_strain) fd = FD(V) dS_dV = fd.derivative(1, nvolumes / 2, S, acc_order=2, mode='c') gruneisen = dS_dV * V_0 / C_V """ phonon_energy_mp = [] volumes = [] hw_V = [[] for i in range(len(self.phonon_energy_mp.ravel()))] for sthermo in self.strained_thermo: volumes.append(sthermo.atoms.get_volume()) phonon_energy_mp.append(sthermo.phonon_energy_mp.ravel()) for j, hw in enumerate(phonon_energy_mp[-1]): hw_V[j].append(hw) fd = FD(volumes) gruneisen_i = np.empty_like(phonon_energy_mp[0]) for i, hw in enumerate(hw_V): dhw_dV = fd.derivative(1, nvolumes/2, hw, acc_order=2, mode='c') # print(dhw_dV, hw[nvolumes/2], dhw_dV * volumes[nvolumes/2] / hw[nvolumes/2]) gruneisen_i[i]\ = - dhw_dV * volumes[nvolumes/2] / hw[nvolumes/2] self.hw_V = hw_V self.volumes = volumes i2kT = 1. / (2. * units.kB * temperature) arg = phonon_energy_mp[nvolumes/2] * i2kT C_v = units.kB * arg ** 2 / np.sinh(arg) ** 2 # print(C_V, C_v, gruneisen_i, volumes) # gruneisen = np.trapz(C_v * gruneisen_i * self.phonon_dos, self.phonon_energy) / C_V gruneisen = np.sum(C_v * gruneisen_i) / np.sum(C_v) print(gruneisen, gruneisen_S) plt.scatter(temperature, gruneisen, color='r') plt.scatter(temperature, gruneisen_S, color='g') """ if save_at is not None: np.savetxt(save_name, gruneisen) return gruneisen
# Read forces and assemble the dynamical matrix ph.read() # High-symmetry points in the Brillouin zone points = ibz_points['bcc'] G = points['Gamma'] H = points['H'] N = points['N'] P = points['P'] point_names = ['$\Gamma$', 'H', 'P', '$\Gamma$', 'N'] path = [G, H, P, G, N] # Band structure in meV path_kc, q, Q = bandpath(path, atoms.cell, 100) omega_kn = 1000 * ph.band_structure(path_kc) # Calculate phonon DOS omega_e, dos_e = ph.dos(kpts=(50, 50, 50), npts=5000, delta=5e-4) omega_e *= 1000 save_atoms(atoms, q, Q, omega_kn, point_names, dos_e, omega_e) # Plot the band structure and DOS # import matplotlib.pyplot as plt # plt.figure(1, (8, 6)) # plt.axes([.1, .07, .67, .85]) # for n in range(len(omega_kn[0])): # omega_n = omega_kn[:, n] # plt.plot(q, omega_n, 'k-', lw=2) # # plt.xticks(Q, point_names, fontsize=18) # plt.yticks(fontsize=18)
def get_elph_elements(atoms, gpw_name, calc_fd, sc=(1, 1, 1), basename=None, phononname='phonon'): """ Evaluates the dipole transition matrix elements Input ---------- params_fd : Calculation parameters used for the phonon calculation sc (tuple): Supercell, default is (1,1,1) used for gamma phonons basename : If you want give a specific name (gqklnn_{}.pckl) Output ---------- gqklnn.pckl, the electron-phonon matrix elements """ from ase.phonons import Phonons from gpaw.elph.electronphonon import ElectronPhononCoupling calc_gs = GPAW(gpw_name) world = calc_gs.wfs.world #calc_fd = GPAW(**params_fd) calc_gs.initialize_positions(atoms) kpts = calc_gs.get_ibz_k_points() nk = len(kpts) gamma_kpt = [[0, 0, 0]] nbands = calc_gs.wfs.bd.nbands qpts = gamma_kpt # calc_fd.get_potential_energy() # XXX needed to initialize C_nM ?????? # Phonon calculation, We'll read the forces from the elph.run function # This only looks at gamma point phonons ph = Phonons(atoms=atoms, name=phononname, supercell=sc) ph.read() frequencies, modes = ph.band_structure(qpts, modes=True) if world.rank == 0: print("Phonon frequencies are loaded.") # Find el-ph matrix in the LCAO basis elph = ElectronPhononCoupling(atoms, calc=None, supercell=sc) elph.set_lcao_calculator(calc_fd) elph.load_supercell_matrix() if world.rank == 0: print("Supercell matrix is loaded") # Non-root processes on GD comm seem to be missing kpoint data. assert calc_gs.wfs.gd.comm.size == 1, "domain parallelism not supported" # not sure how to fix this, sorry gcomm = calc_gs.wfs.gd.comm kcomm = calc_gs.wfs.kd.comm if gcomm.rank == 0: # Find the bloch expansion coefficients c_kn = np.empty((nk, nbands, calc_gs.wfs.setups.nao), dtype=complex) for k in range(calc_gs.wfs.kd.nibzkpts): c_k = calc_gs.wfs.collect_array("C_nM", k, 0) if kcomm.rank == 0: c_kn[k] = c_k kcomm.broadcast(c_kn, 0) # And we finally find the electron-phonon coupling matrix elements! g_qklnn = elph.bloch_matrix(c_kn=c_kn, kpts=kpts, qpts=qpts, u_ql=modes) if world.rank == 0: print("Saving the elctron-phonon coupling matrix") np.save("gqklnn{}.npy".format(make_suffix(basename)), np.array(g_qklnn))
# Read forces and assemble the dynamical matrix ph.read(acoustic=True) # High-symmetry points in the Brillouin zone points = ibz_points['fcc'] G = points['Gamma'] X = points['X'] W = points['W'] K = points['K'] L = points['L'] U = points['U'] point_names = ['$\Gamma$', 'X', 'U', 'L', '$\Gamma$', 'K'] path = [G, X, U, L, G, K] path_kc, q, Q = get_bandpath(path, atoms.cell, 100) omega_kn = 1000 * ph.band_structure(path_kc) # DOS omega_e, dos_e = ph.dos(kpts=(50, 50, 50), npts=5000, delta=1e-4) omega_e *= 1000 # Plot phonon dispersion import matplotlib #matplotlib.use('Agg') import pylab as plt plt.figure(1, (8, 6)) plt.axes([.1, .07, .67, .85]) for n in range(len(omega_kn[0])): omega_n = omega_kn[:, n] plt.plot(q, omega_n, 'k-', lw=2)
def main(): ''' Read in parameters for EAM calc ''' with open('HA4/results/fit_potential_output_full.txt', 'r') as textfile: line = next(textfile) line = line.split(',') A = float(line[0]) lmbd = float(line[1]) D = float(line[2]) mu2 = float(line[3]) # with open('HAlea') # ''' Optimization parameters ''' # A = 1000 # eV # lmbd = 3 # Å^(-1) # D = 5 # Å # mu2 = 1 # 2 # Å^(-1) # param_0 = [A, lmbd, D, mu2] ''' Strains and stuff ''' eV_to_J = 1.60217662 * 10**(-19) angstrom_to_meter = 1e-10 calc = get_calc((A, lmbd, D, mu2)) # calc = EMT() e1 = 0.01 e6 = 0.01 energies = [] C11_vec = [] C12_vec = [] B_vec = [] x = np.linspace(0, 0.5, 100) for i in x: e1 = i e6 = i # C11-C12 al_bulk = bulk('Al', 'fcc', a=4.032, cubic=True) al_bulk.set_calculator(calc) ep_mat_1 = np.array([[e1, 0, 0], [0, -e1, 0], [0, 0, e1**2 / (1 - e1**2)]]) energies.append(al_bulk.get_potential_energy()) cell_0 = al_bulk.get_cell() # al_bulk.set_cell(np.dot((np.eye(3) + ep_mat_1), np.transpose(cell_0))) al_bulk.set_cell(np.dot((np.eye(3) + ep_mat_1), cell_0)) # Yields same result energies.append(al_bulk.get_potential_energy()) # print(cell_0 / al_bulk.get_cell()) # print(cell_0) # print(al_bulk.get_cell()) V = al_bulk.get_volume() #4 * al_bulk.get_volume() delta_E = energies[-1] - energies[0] C11_minus_C12 = delta_E / (V * e1**2) # print('Hola', C11_minus_C12 * eV_to_J / (angstrom_to_meter**3 * 1e9)) # C11+C12 al_bulk = bulk('Al', 'fcc', a=4.032, cubic=True) al_bulk.set_calculator(calc) e2 = e1 ep_mat_12 = np.array([[e1, 0, 0], [0, e2, 0], [0, 0, 0]]) energies.append(al_bulk.get_potential_energy()) V = al_bulk.get_volume( ) #4 * al_bulk.get_volume() # Equilibrium cell volume cell_0 = al_bulk.get_cell() al_bulk.set_cell(np.dot((np.eye(3) + ep_mat_12), cell_0)) energies.append(al_bulk.get_potential_energy()) delta_E = energies[-1] - energies[0] C11_plus_C12 = delta_E / (V * e1**2) # print(C11_plus_C12) # C11 and C12 C11 = (C11_minus_C12 + C11_plus_C12) / 2 C12 = C11_plus_C12 - C11 C11_vec.append(C11 * eV_to_J / (angstrom_to_meter**3 * 1e9)) C12_vec.append(C12 * eV_to_J / (angstrom_to_meter**3 * 1e9)) B_vec.append( ((C11 + 2 * C12) / 3) * eV_to_J / (angstrom_to_meter**3 * 1e9)) plt.figure() plt.plot(x, C11_vec) plt.plot(x, C12_vec) plt.plot(x, B_vec) plt.set_xlabel( r'Displacement factor $\varepsilon_1 = \varepsilon_2 \varepsilon_6 $') plt.set_ylabel('Elastic constants/Bulk modulus [GPa]') plt.show() # C44 al_bulk = bulk('Al', 'fcc', a=4.032, cubic=True) al_bulk.set_calculator(calc) cell_0 = al_bulk.get_cell() ep_mat_6 = np.array([[0, 0.5 * e6, 0], [0.5 * e6, 0, 0], [0, 0, e6**2 / (4 - e6**2)]]) al_bulk.set_cell(np.dot((np.eye(3) + ep_mat_6), cell_0)) energies.append(al_bulk.get_potential_energy()) V = 4 * al_bulk.get_volume() delta_E = energies[-1] - energies[0] C44 = 2 * delta_E / (V * e6**2) # print(C44) B = (C11 + 2 * C12) / 3 # print('C11: ', C11 * eV_to_J / (angstrom_to_meter**3 * 1e9)) # print('C12: ', C12 * eV_to_J / (angstrom_to_meter**3 * 1e9)) B_SI = B * eV_to_J / (angstrom_to_meter)**3 B_GPa = B_SI / 1e9 # print('B', B_GPa) c_prim = (C11 * C12) / 2 ''' Phonon calculator ''' al_bulk = bulk('Al', 'fcc', a=4.032) N = 7 ph = Phonons(al_bulk, calc, supercell=(N, N, N), delta=0.05) ph.run() # Read forces and assemble the dynamical matrix ph.read(acoustic=True, banana=True) # High-symmetry points in the Brillouin zone points = ibz_points['fcc'] G = points['Gamma'] X = points['X'] W = points['W'] K = points['K'] L = points['L'] U = points['U'] point_names = ['$\Gamma$', 'X', 'U', 'L', '$\Gamma$', 'K'] path = [G, X, U, L, G, K] # Band structure in meV path_kc, q, Q = bandpath(path, al_bulk.cell, 100) omega_kn = 1000 * ph.band_structure(path_kc) # # Check band path # fig = plt.figure() # ax = fig.add_subplot(111, projection='3d') # ax.plot(path_kc[:,0], path_kc[:,1], path_kc[:,2]) # plt.show() # Calculate phonon DOS # omega_e, dos_e = ph.dos(kpts=(50, 50, 50), npts=5000, delta=5e-4) # omega_e *= 1000 # # # Plot the band structure and DOS # plt.figure(1, (8, 6)) # plt.axes([.1, .07, .67, .85]) # for n in range(len(omega_kn[0])): # omega_n = omega_kn[:, n] # plt.plot(q, omega_n, 'k-', lw=2) # # plt.xticks(Q, point_names, fontsize=18) # plt.yticks(fontsize=18) # plt.xlim(q[0], q[-1]) # plt.ylabel("Frequency ($\mathrm{meV}$)", fontsize=22) # plt.grid('on') # # plt.axes([.8, .07, .17, .85]) # plt.fill_between(dos_e, omega_e, y2=0, color='lightgrey', edgecolor='k', lw=1) # plt.ylim(0, 35) # plt.xticks([], []) # plt.yticks([], []) # plt.xlabel("DOS", fontsize=18) # plt.show() ''' Sound velocity ''' # point_names = ['$\Gamma$', 'X'] path_100 = [G, X] # Band structure in meV # Return list of k-points, list of x-coordinates and list of x-coordinates of special points. path_kc_100, q_100, Q_100 = bandpath(path_100, al_bulk.cell, 100) omega_kn_100 = 1000 * ph.band_structure(path_kc_100) # # Find the longitudinal curve (the one that is not initially overlapping) # print(omega_kn_100[0:10,0]) # print(omega_kn_100[0:10,1]) # print(omega_kn_100[0:10,2]) # <-- This one! k = np.sqrt(path_kc_100[:, 0]**2 + path_kc_100[:, 1]**2 + path_kc_100[:, 2]**2) # [Å^-1] convert_meV_to_1_over_s = (1 / 1000) * (1 / (6.582119514 * 10**(-16))) # print(omega_kn_100[1,2]) omega_long_at_q_to_0 = omega_kn_100[1, 2] * convert_meV_to_1_over_s # omega_long_at_q_to_1 = omega_kn_100[2,2] * convert_meV_to_1_over_s c_s = omega_long_at_q_to_0 * 10**(-10) / k[1] # Speed of sound, [m/s] # c_s = 10**(-10) * ((omega_long_at_q_to_1 - omega_long_at_q_to_0) / (k[2] - k[1])) # Speed of sound, [m/s] print(c_s) # # convert_u_to_kg = 1.66054 * 10**(-27) # convert_kg_to_eV_c2 = (2.99792 * 10**8)**2 # m_Al = al_bulk.get_masses()[0] * convert_u_to_kg * convert_kg_to_eV_c2 # [eV * (s^2/m^2)] # nbr_of_atoms_UC = 4 # Number of atoms per unit cell for fcc # V_Al = nbr_of_atoms_UC * al_bulk.get_volume() # [Å^3] # rho_Al = m_Al * nbr_of_atoms_UC / V_Al # [eV * (s^2/m^2) / Å^3] # young = c_s**2 * rho_Al # # print(C11) # print(young) plt.figure() plt.plot(q_100, omega_kn_100) plt.show()
'basis': 'dzp', 'symmetry': { 'point_group': False }, 'xc': 'PBE' } elph_calc = GPAW(**parameters) atoms.set_calculator(elph_calc) atoms.get_potential_energy() gamma_bands = elph_calc.wfs.kpt_u[0].C_nM elph = ElectronPhononCoupling(atoms, elph_calc, supercell=supercell, calculate_forces=True) elph.run() parameters['parallel'] = {'domain': 1} elph_calc = GPAW(**parameters) elph = ElectronPhononCoupling(atoms, calc=None, supercell=supercell) elph.set_lcao_calculator(elph_calc) elph.calculate_supercell_matrix(dump=1) ph = Phonons(atoms=atoms, name='phonons', supercell=supercell, calc=None) ph.read() kpts = [[0, 0, 0]] frequencies, modes = ph.band_structure(kpts, modes=True) c_kn = np.array([[gamma_bands[0]]]) g_qklnn = elph.bloch_matrix(c_kn=c_kn, kpts=kpts, qpts=kpts, u_ql=modes)