def run_single_point(): logger = setup_logger(output_filename="dielectrics.log") structure = VaspReader(input_location='./POSCAR').read_POSCAR() vasp = Vasp(**single_point_pbe) vasp.set_crystal(structure) vasp.execute() if vasp.completed: logger.info("PBE self-consistent run completed properly.") else: raise Exception( "PBE self-consistent run failed to converge, will stop proceeding")
def get_total_energies(db, dir=None): all_zips = glob.glob(dir + "/*.zip") for zip in all_zips: kvp = {} data = {} kvp['uid'] = zip.replace(".zip", '').replace('/', '_') archive = zipfile.ZipFile(zip) atoms = None total_energy = None has_contcar = False for name in archive.namelist(): if 'OSZICAR' in name: oszicar = archive.read(name) oszicar_reader = VaspReader( file_content=str(oszicar).split('\\n')) total_energy = oszicar_reader.get_free_energies_from_oszicar( )[-1] kvp['total_energy'] = total_energy if 'CONTCAR' in name: with open('CONTCAR_temp', 'w') as f: for l in str(archive.read(name)).split('\\n'): f.write(l + '\n') has_contcar = True if not has_contcar: for name in archive.namelist(): if 'POSCAR' in name: with open('CONTCAR_temp', 'w') as f: for l in str(archive.read(name)).split('\\n'): f.write(l + '\n') crystal = ase.io.read('CONTCAR_temp', format='vasp') f.close() os.remove('CONTCAR_temp') if (crystal is not None) and (total_energy is not None): print(kvp['uid'], total_energy) populate_db(db, crystal, kvp, data)
folders = glob.glob('*' + halo + '*') _sigma_100K = [] _sigma_300K = [] _compositions = [] _sigma_ref_300K = [] for folder in ['../CsSnI_Pnma', '../CsPbI_Pnma']: print(folder) #if not os.path.isdir(folder): continue os.chdir(folder) # get the compositions crystal = VaspReader(input_location="./phonon/POSCAR").read_POSCAR() _d = crystal.all_atoms_count_dictionaries() scorer = AnharmonicScore(md_frames='./vasprun_md.xml', ref_frame='./phonon/POSCAR', atoms=None, unit_cell_frame='./phonon/POSCAR') __sigmas, _ = scorer.structural_sigma(return_trajectory=True) _sigma_ref_300K.append(__sigmas[2000:]) os.chdir(cwd) for folder in folders: if not os.path.isdir(folder): continue os.chdir(folder) # get the compositions
def __init__(self, ref_frame=None, unit_cell_frame=None, md_frames=None, potim=1, force_constants=None, supercell=[1, 1, 1], primitive_matrix=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], atoms=None, include_third_order=False, third_order_fc='./phono3py/fc3.hdf5', include_fourth_order=False, fourth_order_fc=None, force_sets_filename='FORCE_SETS', mode_resolved=False): self.mode_resolved = mode_resolved if isinstance(ref_frame, Crystal): self.ref_frame = ref_frame elif ('POSCAR' in ref_frame) or ('CONTCAR' in ref_frame): print(ref_frame) print("initialising reference frame from POSCAR ") self.ref_frame = VaspReader(input_location=ref_frame).read_POSCAR() self.ref_coords = np.array([[a.scaled_position.x, a.scaled_position.y, a.scaled_position.z] for a in self.ref_frame.asymmetric_unit[0].atoms]) self.atom_masks = None if atoms is not None: self.atom_masks = [id for id, atom in enumerate(self.ref_frame.asymmetric_unit[0].atoms) if atom.label in atoms] if isinstance(md_frames,list): self.md_frames = md_frames # require the vasprun.xml containing the MD data elif isinstance(md_frames,str): self.md_frames = [md_frames] self.get_dft_md_forces() self.get_all_md_atomic_displacements() if isinstance(force_constants, str): try: self.phonon = phonopy.load(supercell_matrix=supercell, # WARNING - hard coded! primitive_matrix=primitive_matrix, unitcell_filename=unit_cell_frame, force_constants_filename=force_constants) print("Use supercell " + str(supercell)) print("Use primitive matrix " + str(primitive_matrix) + " done") except: self.phonon = phonopy.load(supercell_matrix=supercell, # WARNING - hard coded! primitive_matrix='auto', unitcell_filename=unit_cell_frame, force_constants_filename=force_constants) print("INPUT PHONOPY force constant shape ", np.shape(self.phonon.force_constants)) #TODO - if the input supercell is not [1,1,1], then it will need to be expanded into the correct supercell shape here! new_shape = np.shape(self.phonon.force_constants)[0] * np.shape(self.phonon.force_constants)[2] self.force_constant = np.zeros((new_shape, new_shape)) self.force_constant = self.phonon.force_constants.transpose(0, 2, 1, 3).reshape(new_shape, new_shape) elif (force_constants is None): """ Loading directly from SPOSCAR (supercell structure) and FORCESET to avoid problem of the need for reconstructing the force constants for supercells from primitive cells """ print("Here try to use FORCE_SETS") # if we want to get the eigenvector on all atoms in the supercell, we need to specify the primitive matrix # as the identity matrix, making the phonopy to treat the supercell as the primitive, rather than generate them # automatically if not self.mode_resolved: self.phonon = phonopy.load(supercell_filename=ref_frame, log_level=1, force_sets_filename=force_sets_filename) else: self.phonon = phonopy.load(supercell_filename=ref_frame, log_level=1, force_sets_filename=force_sets_filename,primitive_matrix=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) self.phonon.produce_force_constants() print("INPUT PHONOPY force constant shape ", np.shape(self.phonon.force_constants)) new_shape = np.shape(self.phonon.force_constants)[0] * np.shape(self.phonon.force_constants)[2] self.force_constant = np.zeros((new_shape, new_shape)) self.force_constant = self.phonon.force_constants.transpose(0, 2, 1, 3).reshape(new_shape, new_shape) elif isinstance(force_constants, np.ndarray): new_shape = np.shape(force_constants)[0] * np.shape(force_constants)[2] self.force_constant = np.zeros((new_shape, new_shape)) self.force_constant = force_constants.transpose(0, 2, 1, 3).reshape(new_shape, new_shape) print("force constant reshape ", np.shape(self.force_constant)) print("Force constants ready") self.time_series = [t * potim for t in range(len(self.all_displacements))] self.force_constant_3 = None self.include_third_oder = include_third_order if self.include_third_oder: if isinstance(third_order_fc, str): if os.path.isfile(third_order_fc): print("Found third order force constants") import h5py f = h5py.File(third_order_fc) # './phono3py/fc3.hdf5' raw_force_constant_3 = np.array(f['fc3']) elif isinstance(third_order_fc, np.ndarray): raw_force_constant_3 = third_order_fc s = np.shape(raw_force_constant_3)[0] * 3 self.force_constant_3 = raw_force_constant_3.transpose([0, 3, 1, 4, 2, 5]).reshape(s, s, s) print("Reshaped 3rd order force constant is ", np.shape(self.force_constant_3)) self.force_constant_4 = None self.include_fourth_order = include_fourth_order if self.include_fourth_order: if isinstance(fourth_order_fc, np.ndarray): raw_force_constant_4 = fourth_order_fc s = np.shape(raw_force_constant_4)[0] * 3 self.force_constant_4 = raw_force_constant_4.transpose([0, 4, 1, 5, 2, 6, 3, 7]).reshape(s, s, s, s) if self.mode_resolved: self.prepare_phonon_eigs()
class AnharmonicScore(object): def __init__(self, ref_frame=None, unit_cell_frame=None, md_frames=None, potim=1, force_constants=None, supercell=[1, 1, 1], primitive_matrix=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], atoms=None, include_third_order=False, third_order_fc='./phono3py/fc3.hdf5', include_fourth_order=False, fourth_order_fc=None, force_sets_filename='FORCE_SETS', mode_resolved=False): self.mode_resolved = mode_resolved if isinstance(ref_frame, Crystal): self.ref_frame = ref_frame elif ('POSCAR' in ref_frame) or ('CONTCAR' in ref_frame): print(ref_frame) print("initialising reference frame from POSCAR ") self.ref_frame = VaspReader(input_location=ref_frame).read_POSCAR() self.ref_coords = np.array([[a.scaled_position.x, a.scaled_position.y, a.scaled_position.z] for a in self.ref_frame.asymmetric_unit[0].atoms]) self.atom_masks = None if atoms is not None: self.atom_masks = [id for id, atom in enumerate(self.ref_frame.asymmetric_unit[0].atoms) if atom.label in atoms] if isinstance(md_frames,list): self.md_frames = md_frames # require the vasprun.xml containing the MD data elif isinstance(md_frames,str): self.md_frames = [md_frames] self.get_dft_md_forces() self.get_all_md_atomic_displacements() if isinstance(force_constants, str): try: self.phonon = phonopy.load(supercell_matrix=supercell, # WARNING - hard coded! primitive_matrix=primitive_matrix, unitcell_filename=unit_cell_frame, force_constants_filename=force_constants) print("Use supercell " + str(supercell)) print("Use primitive matrix " + str(primitive_matrix) + " done") except: self.phonon = phonopy.load(supercell_matrix=supercell, # WARNING - hard coded! primitive_matrix='auto', unitcell_filename=unit_cell_frame, force_constants_filename=force_constants) print("INPUT PHONOPY force constant shape ", np.shape(self.phonon.force_constants)) #TODO - if the input supercell is not [1,1,1], then it will need to be expanded into the correct supercell shape here! new_shape = np.shape(self.phonon.force_constants)[0] * np.shape(self.phonon.force_constants)[2] self.force_constant = np.zeros((new_shape, new_shape)) self.force_constant = self.phonon.force_constants.transpose(0, 2, 1, 3).reshape(new_shape, new_shape) elif (force_constants is None): """ Loading directly from SPOSCAR (supercell structure) and FORCESET to avoid problem of the need for reconstructing the force constants for supercells from primitive cells """ print("Here try to use FORCE_SETS") # if we want to get the eigenvector on all atoms in the supercell, we need to specify the primitive matrix # as the identity matrix, making the phonopy to treat the supercell as the primitive, rather than generate them # automatically if not self.mode_resolved: self.phonon = phonopy.load(supercell_filename=ref_frame, log_level=1, force_sets_filename=force_sets_filename) else: self.phonon = phonopy.load(supercell_filename=ref_frame, log_level=1, force_sets_filename=force_sets_filename,primitive_matrix=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) self.phonon.produce_force_constants() print("INPUT PHONOPY force constant shape ", np.shape(self.phonon.force_constants)) new_shape = np.shape(self.phonon.force_constants)[0] * np.shape(self.phonon.force_constants)[2] self.force_constant = np.zeros((new_shape, new_shape)) self.force_constant = self.phonon.force_constants.transpose(0, 2, 1, 3).reshape(new_shape, new_shape) elif isinstance(force_constants, np.ndarray): new_shape = np.shape(force_constants)[0] * np.shape(force_constants)[2] self.force_constant = np.zeros((new_shape, new_shape)) self.force_constant = force_constants.transpose(0, 2, 1, 3).reshape(new_shape, new_shape) print("force constant reshape ", np.shape(self.force_constant)) print("Force constants ready") self.time_series = [t * potim for t in range(len(self.all_displacements))] self.force_constant_3 = None self.include_third_oder = include_third_order if self.include_third_oder: if isinstance(third_order_fc, str): if os.path.isfile(third_order_fc): print("Found third order force constants") import h5py f = h5py.File(third_order_fc) # './phono3py/fc3.hdf5' raw_force_constant_3 = np.array(f['fc3']) elif isinstance(third_order_fc, np.ndarray): raw_force_constant_3 = third_order_fc s = np.shape(raw_force_constant_3)[0] * 3 self.force_constant_3 = raw_force_constant_3.transpose([0, 3, 1, 4, 2, 5]).reshape(s, s, s) print("Reshaped 3rd order force constant is ", np.shape(self.force_constant_3)) self.force_constant_4 = None self.include_fourth_order = include_fourth_order if self.include_fourth_order: if isinstance(fourth_order_fc, np.ndarray): raw_force_constant_4 = fourth_order_fc s = np.shape(raw_force_constant_4)[0] * 3 self.force_constant_4 = raw_force_constant_4.transpose([0, 4, 1, 5, 2, 6, 3, 7]).reshape(s, s, s, s) if self.mode_resolved: self.prepare_phonon_eigs() def prepare_phonon_eigs(self,nqpoints=10): print("Need to calculate the mode resolved anharmonic scores, first get the phonon eigenvectors") from core.models.element import atomic_mass_dict from pymatgen.symmetry.bandstructure import HighSymmKpath from core.internal.builders.crystal import map_to_pymatgen_Structure pmg_path = HighSymmKpath(map_to_pymatgen_Structure(self.ref_frame), symprec=1e-3) self._kpath = pmg_path._kpath self.prim = pmg_path.prim self.conv = pmg_path.conventional __qpoints = [[self._kpath['kpoints'][self._kpath['path'][j][i]] for i in range(len(self._kpath['path'][j]))] for j in range(len(self._kpath['path']))] from phonopy.phonon.band_structure import get_band_qpoints_and_path_connections qpoints, connections = get_band_qpoints_and_path_connections(__qpoints, npoints=nqpoints) # now this qpoints will contain points within two high-symmetry points along the Q-path self.phonon.run_band_structure(qpoints, with_eigenvectors=True) _eigvecs = self.phonon.band_structure.__dict__['_eigenvectors'] _eigvals = self.phonon.band_structure.__dict__['_eigenvalues'] self.eigvecs = [] self.eigvals = [] import math self.atomic_masses = [] for a in self.ref_frame.all_atoms(sort=False, unique=False): for _ in range(3): self.atomic_masses.append(1.0 / math.sqrt(atomic_mass_dict[a.label.upper()])) from phonopy.units import VaspToTHz if _eigvecs is not None: for i, eigvecs_on_path in enumerate(_eigvecs): for j, eigvecs_at_q in enumerate(eigvecs_on_path): for k, vec in enumerate(eigvecs_at_q.T): #vec = np.array(vec).reshape(self.ref_frame.total_num_atoms(), 3) self.eigvecs.append(self.atomic_masses*vec) eigv=_eigvals[i][j][k] self.eigvals.append(np.sqrt(abs(eigv))*np.sign(eigv)*VaspToTHz) print('eigenvector shape ', np.shape(vec)) print('Total number of eigenstates ' + str(len(self.eigvals))) def mode_resolved_sigma(self): if self.mode_resolved is not True: raise Exception("eigenmodes not prepared for running this function") print("do dot") dft_dot = np.dot(self.dft_forces, np.array(self.eigvecs).T).std(axis=0) anh_dot = np.dot(self.anharmonic_forces, np.array(self.eigvecs).T).std(axis=0) print(np.shape(dft_dot), np.shape(anh_dot)) print("finished dot") #self.mode_sigmas = [np.dot(self.anharmonic_forces,eigvec).std()/np.dot(self.dft_forces,eigvec).std() for eigvec in self.eigvecs] self.mode_sigmas = np.divide(anh_dot,dft_dot) return self.eigvals,self.mode_sigmas def mode_resolved_sigma_band(self): from pymatgen.symmetry.bandstructure import HighSymmKpath from core.internal.builders.crystal import map_to_pymatgen_Structure pmg_path = HighSymmKpath(map_to_pymatgen_Structure(self.ref_frame), symprec=1e-3) self._kpath = pmg_path._kpath distances = self.phonon.band_structure.__dict__['_distances'] print(np.shape(distances[0])) eigvals = self.phonon.band_structure.__dict__['_frequencies'] print(np.shape(eigvals[0])) eigvecs = self.phonon.band_structure.__dict__['_eigenvectors'] sp_pts = self.phonon.band_structure.__dict__['_special_points'] print(sp_pts) sp_pt_labels = self._kpath['path'] print(sp_pt_labels) f, (a0, a1) = plt.subplots(1, 2, gridspec_kw={'width_ratios': [3, 1]}) from phonopy.units import VaspToTHz freqs=[] sigmas=[] for j in range(len(distances)): for i in range(len(eigvals[j].T)): #now need something to calculate the sigma here along each point print(str(j) + '/' + str(len(distances)-1), str(i)+'/'+str(len(eigvals[j].T)-1)) _sigmas=[] for k in range(len(distances[j])): e=eigvecs[j][k][i] _s=np.dot(self.anharmonic_forces,e).std()/np.dot(self.dft_forces,e).std() _sigmas.append(np.exp(1.5*_s)) #sigmas.append(_s) #eigv=eigvals[j][k][i] #eigv=np.sqrt(abs(eigv)) * np.sign(eigv) * VaspToTHz #freqs.append(eigv) #print(max(sigmas),min(sigmas)) #sigmas=[np.dot(self.anharmonic_forces,e).std()/np.dot(self.dft_forces,e).std() for e in eigvecs[j][:][i]] #print(sigmas) a0.plot(distances[j],eigvals[j].T[i],'-',c='#FFBB00',alpha=0.75,linewidth=0.85) a0.scatter(distances[j],eigvals[j].T[i],marker='o',s=_sigmas,fc='#3F681C',alpha=0.45) a0.set_xlim(min(distances[0]),max(distances[-1])) unique_labels = [] for i in range(len(sp_pt_labels)): for j in range(len(sp_pt_labels[i])): if sp_pt_labels[i][j]=='\\Gamma': _l = "$\\Gamma$" elif "_1" in sp_pt_labels[i][j]: _l = sp_pt_labels[i][j].replace("_1","") else: _l = sp_pt_labels[i][j] if (j==len(sp_pt_labels[i])-1) and (i!=len(sp_pt_labels)-1): unique_labels.append(_l+'$\\vert$') elif (i>0) and (j==0): unique_labels[-1] = unique_labels[-1]+_l else: unique_labels.append(_l) a0.set_xticks(sp_pts) a0.set_xticklabels(unique_labels) for i in sp_pts: a0.axvline(x=i,ls=':',c='k') a0.set_ylabel("Frequency (THz)") freqs,sigmas=self.mode_resolved_sigma() a1.scatter(sigmas,freqs,marker='o',fc='#3F681C',alpha=0.6, s=1) a1.set_ylim(a0.get_ylim()) a1.set_xlabel('$\\sigma$ (300 K)') a1.axvline(x=1, ls=':', c='k') a1.set_yticks([]) plt.tight_layout() plt.savefig('phonon_band_sigma.pdf') def plot_fc(self): plt.matshow(self.force_constant) plt.colorbar() plt.savefig('fc.pdf') @property def lattice_vectors(self): """ :return: A numpy array representation of the lattice vector """ _lv = self.ref_frame.lattice.lattice_vectors return np.array( [[_lv[0][0], _lv[0][1], _lv[0][2]], [_lv[1][0], _lv[1][1], _lv[1][2]], [_lv[2][0], _lv[2][1], _lv[2][2]]]) def get_dft_md_forces(self): all_forces = [] for frame in self.md_frames: for event, elem in etree.iterparse(frame): if elem.tag == 'varray': if elem.attrib['name'] == 'forces': this_forces = [] if not self.mode_resolved: for v in elem: this_force = [float(_v) for _v in v.text.split()] this_forces.append(this_force) else: for v in elem: for this_force in [float(_v) for _v in v.text.split()]: this_forces.append(this_force) all_forces.append(np.array(this_forces)) print('MD force vector shape ', np.shape(this_forces)) self.dft_forces = np.array(all_forces) print('All MD force vector shape ', np.shape(self.dft_forces)) print("Atomic forces along the MD trajectory loaded\n") def get_all_md_atomic_displacements(self): all_positions = [] for frame in self.md_frames: if len(self.md_frames)!=1: this = [] for event, elem in etree.iterparse(frame): if elem.tag == 'varray': if elem.attrib['name'] == 'positions': this_positions = [] for v in elem: this_position = [float(_v) for _v in v.text.split()] this_positions.append(this_position) if len(self.md_frames) != 1: this.append(this_positions) else: all_positions.append(np.array(this_positions)) if len(self.md_frames) != 1: all_positions.append(this[-1]) # only need those with forces all_positions = all_positions[-len(self.dft_forces):] all_positions = np.array(all_positions) print("Atomic positions along the MD trajectory loaded, converting to displacement, taking into account PBC") __all_displacements = np.array( [all_positions[i, :] - self.ref_coords for i in range(all_positions.shape[0])]) # periodic boundary conditions #__all_displacements = (__all_displacements + 0.5 + 1e-5) % 1 - 0.5 - 1e-5 __all_displacements = __all_displacements - np.round(__all_displacements) # this is how it's done in Pymatgen # Convert to Cartesian self.all_displacements = np.zeros(np.shape(__all_displacements)) for i in range(__all_displacements.shape[0]): np.dot(__all_displacements[i, :, :], self.lattice_vectors, out=self.all_displacements[i, :, :]) @property def harmonic_forces(self): if (not hasattr(self, '_harmonic_forces')) or ( hasattr(self, '_harmonic_forces') and self._harmonic_force is None): self._harmonic_force = np.zeros(np.shape(self.dft_forces)) for i in range(np.shape(self.all_displacements)[0]): # this loop over MD frames if not self.mode_resolved: self._harmonic_force[i, :, :] = -1.0 * ( np.dot(self.force_constant, self.all_displacements[i, :, :].flatten())).reshape( self.all_displacements[0, :, :].shape) else: self._harmonic_force[i, :] = -1.0 * (np.dot(self.force_constant, self.all_displacements[i, :, :].flatten())) return self._harmonic_force @property def third_order_forces(self): print("Do we have third_order constant " + str(self.force_constant_3.__class__)) if self.mode_resolved: raise NotImplementedError if self.force_constant_3 is not None: if (not hasattr(self, '_third_order_forces')) or ( hasattr(self, '_third_order_forces') and self._third_order_forces is None): self._third_order_forces = np.zeros(np.shape(self.dft_forces)) _a = self.force_constant_3 for i in range(np.shape(self.all_displacements)[0]): # this loop over MD frames _b = self.all_displacements[i, :, :].flatten() _A = np.einsum('ijk,k->ij', _a, _b) self._third_order_forces[i, :, :] = -1 * np.einsum('ij,j->i', _A, _b).reshape( self.all_displacements[0, :, :].shape) return self._third_order_forces / 2.0 # see https://hiphive.materialsmodeling.org/background/force_constants.html @property def fourth_order_forces(self): if self.mode_resolved: raise NotImplementedError print("Do we have fourth_order constant " + str(self.force_constant_4.__class__)) if self.force_constant_4 is not None: if (not hasattr(self, '_fourth_order_forces')) or ( hasattr(self, '_fourth_order_forces') and self._fourth_order_forces is None): self._fourth_order_forces = np.zeros(np.shape(self.dft_forces)) _a = self.force_constant_4 for i in range(np.shape(self.all_displacements)[0]): # this loop over MD frames print("fourth order forces, frame "+str(i)) _b = self.all_displacements[i, :, :].flatten() _A = np.einsum('ijkl,l->ijk', _a, _b) _B = np.einsum('ijk,k->ij', _A, _b) self._fourth_order_forces[i, :, :] = -1 * np.einsum('ij,j->i', _B, _b).reshape( self.all_displacements[0, :, :].shape) print("fourth order forces done") return self._fourth_order_forces / 6.0 @property def anharmonic_forces(self): if (not hasattr(self, '_anharmonic_forces')) or ( hasattr(self, '_anharmonic_forces') and self._anharmonic_forces is None): self._anharmonic_forces = self.dft_forces - self.harmonic_forces if self.include_third_oder: self._anharmonic_forces = self._anharmonic_forces - self.third_order_forces if self.include_fourth_order: self._anharmonic_forces = self._anharmonic_forces - self.fourth_order_forces return self._anharmonic_forces def trajectory_normalized_dft_forces(self, flat=False): all_forces_std = self.dft_forces.flatten().std() out = np.zeros(np.shape(self.dft_forces)) np.divide(self.dft_forces, all_forces_std, out=out) if flat: return out.flatten() return out def trajectory_normalized_anharmonic_forces(self, flat=False): # anharmonic_forces_std = self.anharmonic_forces.flatten().std() all_forces_std = self.dft_forces.flatten().std() out = np.zeros(np.shape(self.anharmonic_forces)) np.divide(self.anharmonic_forces, all_forces_std, out=out) if flat: return out.flatten() return out def atom_normalized_dft_forces(self, atom, flat=False): _mask = [id for id, a in enumerate(self.ref_frame.asymmetric_unit[0].atoms) if a.label == atom] dft_forces = self.dft_forces[:, _mask, :] dft_forces_std = dft_forces.flatten().std() out = np.zeros(np.shape(dft_forces)) np.divide(dft_forces, dft_forces_std, out=out) if flat: return out.flatten(), dft_forces_std return out, dft_forces_std def atom_normalized_anharmonic_forces(self, atom, flat=False): _mask = [id for id, a in enumerate(self.ref_frame.asymmetric_unit[0].atoms) if a.label == atom] print(atom, _mask) anharmonic_forces = self.anharmonic_forces[:, _mask, :] dft_forces = self.dft_forces[:, _mask, :] dft_forces_std = dft_forces.flatten().std() out = np.zeros(np.shape(anharmonic_forces)) np.divide(anharmonic_forces, dft_forces_std, out=out) if flat: return out.flatten() return out def plot_atom_joint_distributions(self): atoms = [a.label for a in self.ref_frame.asymmetric_unit[0].atoms] atoms = list(set(atoms)) atoms = sorted(atoms) print(atoms) fig, axs = plt.subplots(1, len(atoms), figsize=(4 * len(atoms), 4)) for id, atom in enumerate(atoms): divider = make_axes_locatable(axs[id]) cax1 = divider.append_axes("right", size="5%", pad=0.05) X, std = self.atom_normalized_dft_forces(atom, flat=True) Y = self.atom_normalized_anharmonic_forces(atom, flat=True) X_pred, Y_pred = np.mgrid[-2:2:50j, -2:2:50j] positions = np.vstack([X_pred.ravel(), Y_pred.ravel()]) values = np.vstack([X, Y]) print("Perform Gaussian Kernel Density Estimate") kernel = gaussian_kde(values) Z = np.reshape(kernel(positions).T, X_pred.shape) print("Making the plot") a = axs[id].imshow(np.rot90(Z), cmap=plt.get_cmap('Blues'), extent=[-1, 1, -1, 1]) axs[id].plot([-1, -0.5, 0, 0.5, 1], [std for i in range(5)], 'k--') axs[id].plot([-1, -0.5, 0, 0.5, 1], [-1.0 * std for i in range(5)], 'k--') axs[id].set_xlim([-1, 1]) axs[id].set_ylim([-1, 1]) axs[id].set_xlabel("$F_{i}/\\sigma(F_{i})$", fontsize=16) axs[id].set_ylabel("$F^{A}_{i}/\\sigma(F_{i})$", fontsize=16) axs[id].set_title(str(atom), fontsize=20) axs[id].tick_params(axis='both', which='major', labelsize=10) plt.colorbar(a, cax=cax1) # .set_label(label='probability density',size=15) plt.tight_layout() plt.savefig('atom_joint_PDF.pdf') def plot_total_joint_distribution(self, x='DFT', y='anh'): if x == 'DFT': X = self.dft_forces.flatten() / self.dft_forces.flatten().std() else: raise NotImplementedError() if y == 'anh': Y = self.anharmonic_forces.flatten() / self.dft_forces.flatten().std() elif y == 'har': Y = self.harmonic_forces.flatten() / self.dft_forces.flatten().std() else: raise NotImplementedError() X_pred, Y_pred = np.mgrid[-1:1:50j, -1:1:50j] positions = np.vstack([X_pred.ravel(), Y_pred.ravel()]) values = np.vstack([X, Y]) print("Gaussian KDE") kernel = gaussian_kde(values) print("Kernel done") Z = np.reshape(kernel(positions).T, X_pred.shape) plt.imshow(np.rot90(Z), cmap=plt.get_cmap('Blues'), extent=[-1, 1, -1, 1]) plt.xlim([-1, 1]) plt.ylim([-1, 1]) plt.xlabel("$F/\\sigma(F)$", fontsize=16) if y == 'anh': plt.ylabel("$F^{A}/\\sigma(F)$", fontsize=16) elif y == 'har': plt.ylabel("$F^{(2)}/\\sigma(F)$", fontsize=16) if y == 'anh': plt.plot([-1, -0.5, 0, 0.5, 1], [self.anharmonic_forces.flatten().std() for i in range(5)], 'k--') plt.plot([-1, -0.5, 0, 0.5, 1], [-1.0 * self.anharmonic_forces.flatten().std() for i in range(5)], 'k--') plt.tick_params(axis='both', which='major', labelsize=10) plt.colorbar().set_label(label='probability density', size=15) plt.tight_layout() plt.savefig('joint_PDF.pdf') def structure_averaged_sigma_trajectory(self): atoms = [a.label for a in self.ref_frame.asymmetric_unit[0].atoms] atoms = list(set(atoms)) self.sigma_frames = [] for atom in atoms: anh_f = self.atom_normalized_anharmonic_forces(atom) dft_f = self.atom_normalized_dft_forces(atom) for i in range(np.shape(self.all_displacements)[0]): _sigma_frame = anh_f[i, :, :].flatten().std() / dft_f[i, :, :].flatten().std() self.sigma_frames.append(_sigma_frame) def structural_sigma(self, return_trajectory=False): if self.atom_masks is None: rmse = self.anharmonic_forces std = self.dft_forces else: rmse = self.anharmonic_forces[:, self.atom_masks, :] std = self.dft_forces[:, self.atom_masks, :] if not return_trajectory: print(return_trajectory, 'calculate sigma') sigma = rmse.std(dtype=np.float64) / std.std(dtype=np.float64) print("Sigma for entire structure over the MD trajectory is ", str(sigma)) return sigma, self.time_series else: sigma = rmse.std(axis=(1,2), dtype=np.float64) / std.std(axis=(1,2), dtype=np.float64) print('sigma is ', sigma) print('averaged sigma is ',sigma.mean()) return sigma, self.time_series
'LWAVE': False, 'LCHARG': False, 'LREAL': 'Auto', 'NELM': 150, 'NSW': 0, 'NCORE': 48, 'use_gw': True, 'Gamma_centered': True, 'MP_points': [1, 1, 1], 'executable': 'vasp_gam' } pwd = os.getcwd() # get the trajectory all_frames = VaspReader(args.traj).read_XDATCAR() if not args.batch: output = 'gap_dynamics.dat' else: output = 'gap_dynamics_' + str(args.part) + '.dat' # figure out if this is a continuing calculation if not os.path.exists(pwd + '/' + output): o = open(pwd + '/' + output, 'w+') o.write('Frame\t VBM \t CBM \t E_f \t E_g\n') o.close() last = 0 else: o = open(pwd + '/' + output, 'r') for l in o.readlines():
def composition_eq_point_curve(): cwd_1 = os.getcwd() labels = { 0: 'Cs(Pb$_{x}$Sn$_{1-x})$Cl$_3$', 1: 'Cs(Pb$_{x}$Sn$_{1-x})$Br$_3$', 2: 'Cs(Pb$_{x}$Sn$_{1-x})$I$_3$' } colors = { 0: '#000000', # hex code for black 1: '#dd0000', # hex code for electric red 2: '#ffce00' # hex code for tangerine yellow } for counter, X in enumerate(['Cl', 'Br', 'I']): data_dict = {} for dir in [ r for r in glob.glob(os.getcwd() + "/*" + str(X) + "*") if '.pdf' not in r ]: os.chdir(dir) print(dir) cwd_2 = os.getcwd() data = [] directories = glob.glob(os.getcwd() + "/disp_111_*_0_05") if 'CsSnBr3_cubic' in dir: directories = glob.glob(os.getcwd() + "/disp_Sn_111_*_0_05") for sub_dir in directories: os.chdir(sub_dir) print(sub_dir) energy = VaspReader(input_location='./OSZICAR' ).get_free_energies_from_oszicar()[-1] crystal = VaspReader(input_location='./POSCAR').read_POSCAR() energy = energy / crystal.total_num_atoms() info = pickle.load(open('info.p', 'rb')) info['energy'] = energy data.append(info) os.chdir(cwd_2) os.chdir(cwd_1) displacements = list(sorted([d['displacement'] for d in data])) energy = [] for _dis in displacements: for d in data: if d['displacement'] == _dis: energy.append(d['energy']) energy = [e - energy[0] for e in energy] x = np.array(displacements) y = np.array(energy) from scipy.optimize import curve_fit popt, pcov = curve_fit(pes, x, y) x = [0 + (0.6 / 100) * i for i in range(100)] y = pes(np.array(x), *popt) a = popt[0] b = popt[1] if 2 * a / (4 * b) > 0: min_x = 0 else: min_x = math.sqrt(-2 * a / (4 * b)) concentration = data[0]['concentration'] data_dict[1.0 - concentration] = min_x os.chdir(cwd_1) x = list(sorted(data_dict.keys())) plt.plot(x, [data_dict[_x] for _x in x], 'o-', c=colors[counter], label=labels[counter], ms=12, lw=3) plt.legend() plt.xlabel('$x$ in Cs(Pb$_{x}$Sn$_{1-x}$)X$_{3}$') plt.ylabel('Minima on PES ($x_{\min}$, \AA)') plt.tight_layout() plt.savefig('xeq_comp_summary.pdf')
def make_distorted_structure(poscar, direction='111', max_displacement=0.6, number_of_points=15, lattice_deformation=0, atom_to_displace=['Sn', 'Pb']): for counter, delta_d in enumerate([ 0 + max_displacement / number_of_points * k for k in range(number_of_points) ]): crystal = VaspReader(input_location=poscar).read_POSCAR() # expand or shrink the lattice parameters crystal = deform_crystal_by_lattice_expansion_coefficients( crystal, def_fraction=[lattice_deformation for _ in range(3)]) d = [int(_d) for _d in list(direction)] displacement_vector = cVector3D(d[0], d[1], d[2]).normalise() # just displace the first atom that is the element that we want to displace _displace_vec = displacement_vector.vec_scale(delta_d) # _next = True for mol in crystal.asymmetric_unit: for atom in mol.atoms: if (atom.label in atom_to_displace): # and (_next is True): print(atom.label) atom.position = atom.position + _displace_vec # _next = False try: n = crystal.all_atoms_count_dictionaries()['Sn'] except KeyError: n = 0 folder_name = 'disp' + '_' + str(direction) + '_' + str( n) + '_str_' + str(counter) + '_a_def_' + str( lattice_deformation).replace('.', '_') cwd = os.getcwd() try: os.makedirs(cwd + '/' + folder_name) except: pass os.chdir(cwd + '/' + folder_name) _dict = crystal.all_atoms_count_dictionaries() if 'Sn' not in _dict.keys(): _dict['Sn'] = 0 if 'Pb' not in _dict.keys(): _dict['Pb'] = 0 system_dict = { 'atom_to_displace': atom_to_displace, 'concentration': _dict['Sn'] / (_dict['Sn'] + _dict['Pb']), 'displacement': delta_d, 'id': counter, 'direction': direction, 'deformation': lattice_deformation } pickle.dump(system_dict, open('info.p', 'wb')) VaspWriter().write_structure(crystal, 'POSCAR') os.chdir(cwd)
def get_this_pes(directory=os.getcwd()): cmap = matplotlib.cm.get_cmap('YlOrRd') cwd = os.getcwd() data = [] for dir in glob.glob(directory + "/disp_*"): os.chdir(dir) try: energy = VaspReader(input_location='./OSZICAR' ).get_free_energies_from_oszicar()[-1] crystal = VaspReader(input_location='./POSCAR').read_POSCAR() energy = energy / crystal.total_num_atoms() info = pickle.load(open('info.p', 'rb')) info['energy'] = energy data.append(info) except: pass os.chdir(cwd) deformations = set([d['deformation'] for d in data]) deformations = list(sorted(deformations)) max_def = max(deformations) + 0.0001 max_displacement = 0 min_x = [] min_y = [] for id, _def in enumerate(deformations): displacement = [ d['displacement'] for d in data if d['deformation'] == _def ] displacement = list(sorted(displacement)) energy = [] for dis in displacement: for d in data: if (d['deformation'] == _def) and (d['displacement'] == dis): energy.append(d['energy']) energy = [e - energy[0] for e in energy] x = np.array(displacement) y = np.array(energy) from scipy.optimize import curve_fit popt, pcov = curve_fit(pes, x, y) x = [0 + (0.6 / 100) * i for i in range(100)] y = pes(np.array(x), *popt) a = popt[0] b = popt[1] if 2 * a / (4 * b) > 0: min_x.append(0) min_y.append(0) else: min_x.append(math.sqrt(-2 * a / (4 * b))) min_y.append(pes(math.sqrt(-2 * a / (4 * b)), *popt)) if id % 2 == 0: plt.plot(x, y, '-', c=cmap((0.05 + _def) / (2 * max_def)), label=str(_def * 100) + '\%', lw=2) else: plt.plot(x, y, '-', c=cmap((0.05 + _def) / (2 * max_def)), lw=2) max_displacement = max(x) plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0)) plt.plot([0, max_displacement], [0, 0], 'k--') plt.plot(min_x, min_y, 'o-', c='k', lw=3.5) plt.legend() plt.ylim([-0.01, 0.0150]) plt.xlim([0, max_displacement]) plt.xlabel('B-Cation Displacement $\Delta d$ (\AA)') plt.ylabel('$E-E_{\Delta d=0}$ (eV/atom)') plt.tight_layout() plt.savefig('pes.pdf')
def pressure_eq_point_curve(): cwd = os.getcwd() labels = { 0: 'CsPbBr$_3$', 1: 'Cs(Pb$_{0.5}$Sn$_{0.5})$Br$_3$', 2: 'CsSnBr$_3$' } colors = { 0: '#000000', # hex code for black 1: '#dd0000', # hex code for electric red 2: '#ffce00' # hex code for tangerine yellow } for counter, sys in enumerate([ 'CsPbBr3_cubic', 'mixed_CsPbSnBr3_SC_1_1_1_CsPbSnBr3_5_str_17', 'CsSnBr3_cubic' ]): os.chdir(sys) this_dir = os.getcwd() data = [] for dir in glob.glob("./disp_*"): os.chdir(dir) try: energy = VaspReader(input_location='./OSZICAR' ).get_free_energies_from_oszicar()[-1] crystal = VaspReader(input_location='./POSCAR').read_POSCAR() energy = energy / crystal.total_num_atoms() info = pickle.load(open('info.p', 'rb')) info['energy'] = energy data.append(info) except: pass os.chdir(this_dir) deformations = set([d['deformation'] for d in data]) deformations = list(sorted(deformations)) min_x = [] for id, _def in enumerate(deformations): displacement = [ d['displacement'] for d in data if d['deformation'] == _def ] displacement = list(sorted(displacement)) energy = [] for dis in displacement: for d in data: if (d['deformation'] == _def) and (d['displacement'] == dis): energy.append(d['energy']) energy = [e - energy[0] for e in energy] x = np.array(displacement) y = np.array(energy) from scipy.optimize import curve_fit popt, pcov = curve_fit(pes, x, y) a = popt[0] b = popt[1] if 2 * a / (4 * b) > 0: min_x.append(0) else: min_x.append(math.sqrt(-2 * a / (4 * b))) plt.plot(deformations, min_x, 'o--', label=labels[counter], ms=10, c=colors[counter]) _deformations = [] _min_x = [] for t in range(len(min_x)): if (min_x[t] > 0): _deformations.append(deformations[t]) _min_x.append(min_x[t]) x = np.array(_deformations) y = np.array(_min_x) from scipy import interpolate f = interpolate.interp1d(x, y) x = np.array([ min(_deformations) + k * (max(_deformations) - min(_deformations)) / 100 for k in range(100) ]) y = f(x) from scipy.optimize import curve_fit popt, pcov = curve_fit(square, y, x, maxfev=2000) a = popt[0] b = popt[1] _y = np.array([(max(y) + 0.1 * max(y)) * k / 100 for k in range(100)]) _x = square(_y, *popt) print(_x[0]) plt.plot(_x, _y, '-', lw=2, c=colors[counter]) os.chdir(cwd) plt.xlim([0.015, 0.052]) plt.xlabel('Lattice Deformation $\delta $') plt.ylabel('Minima on PES ($x_{\min}$, \AA)') plt.legend() plt.tight_layout() plt.savefig("xeq_pressure_summary_Br.pdf")
def get_pes_across_composition(X='Cl'): cmap = matplotlib.cm.get_cmap('coolwarm') cwd_1 = os.getcwd() data_dict = {} min_x = [] min_y = [] for dir in [ r for r in glob.glob(os.getcwd() + "/*" + str(X) + "*") if '.pdf' not in r ]: os.chdir(dir) cwd_2 = os.getcwd() data = [] for sub_dir in glob.glob(os.getcwd() + "/disp_111_*_0_05"): os.chdir(sub_dir) print(sub_dir) energy = VaspReader(input_location='./OSZICAR' ).get_free_energies_from_oszicar()[-1] crystal = VaspReader(input_location='./POSCAR').read_POSCAR() energy = energy / crystal.total_num_atoms() info = pickle.load(open('info.p', 'rb')) info['energy'] = energy data.append(info) os.chdir(cwd_2) os.chdir(cwd_1) displacements = list(sorted([d['displacement'] for d in data])) energy = [] for _dis in displacements: for d in data: if d['displacement'] == _dis: energy.append(d['energy']) energy = [e - energy[0] for e in energy] x = np.array(displacements) y = np.array(energy) from scipy.optimize import curve_fit popt, pcov = curve_fit(pes, x, y) x = [0 + (0.6 / 100) * i for i in range(100)] y = pes(np.array(x), *popt) a = popt[0] b = popt[1] if 2 * a / (4 * b) > 0: min_x.append(0) min_y.append(0) else: min_x.append(math.sqrt(-2 * a / (4 * b))) min_y.append(pes(math.sqrt(-2 * a / (4 * b)), *popt)) data_dict[1.0 - data[0]['concentration']] = {'x': x, 'y': y} for k in list(sorted(data_dict.keys())): plt.plot(data_dict[k]['x'], data_dict[k]['y'], '-', c=cmap(k), label=str(k)) # plt.legend(loc=2) plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0)) plt.plot(min_x, min_y, 'o', c='#00743F') max_displacemnt = 0.6 plt.xlim([0, max_displacemnt]) plt.plot([0, max_displacemnt], [0, 0], 'k--') plt.xlabel('B-Cation Displacement $\Delta d$ (\AA)') plt.ylabel('$E-E_{\Delta d=0}$ (eV/atom)') plt.tight_layout() plt.savefig(X + "_B_111_landscape.pdf")