def double_unit_cell(struc): """ doubling the size of the unit cell use case: given a perovskite structure, return the corresponding double perovskite structure Args: struc: the input structure Returns: the doubled new structure """ lattice = struc.lattice.matrix vector_a = lattice[0] + lattice[1] vector_b = lattice[1] + lattice[2] vector_c = lattice[0] + lattice[2] new_lattice = Lattice([vector_a, vector_b, vector_c]) #the atoms new_sites = [] for site in struc.sites: new_sites.append(PeriodicSite(site.species_and_occu, site.coords, new_lattice, coords_are_cartesian=True)) new_sites.append(PeriodicSite(site.species_and_occu, site.coords+lattice[0], new_lattice, coords_are_cartesian=True)) species = [] coords = [] for site in new_sites: species.append(site.specie) coords.append(site.coords) new_struc = Structure(new_lattice, species, coords, to_unit_cell=True, coords_are_cartesian=True) return new_struc
def test_substitution(self): struc = PymatgenTest.get_structure("VO2") V_index = struc.indices_from_symbol("V")[0] sub_site = PeriodicSite("Sr", struc[V_index].coords, struc.lattice, coords_are_cartesian=True) substitution = Substitution(struc, sub_site) # test generation and super cell sub_struc = substitution.generate_defect_structure(1) self.assertEqual(sub_struc.composition.as_dict(), {"V": 1, "Sr": 1, "O": 4}) sub_struc = substitution.generate_defect_structure(2) self.assertEqual(sub_struc.composition.as_dict(), {"V": 15, "Sr": 1, "O": 32}) sub_struc = substitution.generate_defect_structure(3) self.assertEqual(sub_struc.composition.as_dict(), {"V": 53, "Sr": 1, "O": 108}) sub_struc = substitution.generate_defect_structure([[2., 0, 0], [0, 0, -3.], [0, 2., 0]]) self.assertEqual(sub_struc.composition.as_dict(), {"V": 23, "O": 48, "Sr": 1}) # test charge substitution = Substitution(struc, sub_site) sub_struc = substitution.generate_defect_structure(1) self.assertEqual(sub_struc.charge, 0.0) substitution = Substitution(struc, sub_site, charge=1.0) sub_struc = substitution.generate_defect_structure(1) self.assertEqual(sub_struc.charge, 1.0) substitution = Substitution(struc, sub_site, charge=-1.0) sub_struc = substitution.generate_defect_structure(1) self.assertEqual(sub_struc.charge, -1.0) # test multiplicity substitution = Substitution(struc, sub_site) self.assertEqual(substitution.multiplicity, 2.0) O_index = struc.indices_from_symbol("O")[0] sub_site = PeriodicSite("Sr", struc[O_index].coords, struc.lattice, coords_are_cartesian=True) substitution = Substitution(struc, sub_site) self.assertEqual(substitution.multiplicity, 4) # Test composition self.assertEqual(dict(substitution.defect_composition.as_dict()), {"V": 2, "Sr": 1, "O": 3}) # test that structure generation doesn't break if velocities existed previously # (previously caused failures for structure printing) vel_struc = Structure( struc.lattice, struc.species, struc.frac_coords, site_properties= {'velocities': [[0., 0., 0.]]*len(struc) } ) substitution = Substitution(vel_struc, sub_site) sub_struc = substitution.generate_defect_structure(1) self.assertTrue( 'velocities' not in sub_struc.site_properties) # test value error raised for site not in the structure non_site = PeriodicSite( "Sr", struc[V_index].frac_coords - [0., 0., .1], struc.lattice) self.assertRaises( ValueError, Substitution, struc, non_site)
def get_period_dis(self, p1, p2): """ 在考虑周期性的情况下,计算两点之间的距离 :param p1: 分数坐标,例如[0.5,0.5,0.5] :param p2: 分数坐标 :return: 两点之间的距离,考虑周期性 """ temp_site1 = PeriodicSite('Ar', p1, self.__struc.lattice) temp_site2 = PeriodicSite('Ar', p2, self.__struc.lattice) dis = temp_site1.distance(temp_site2) return dis
def test_to_unit_cell(self): site = PeriodicSite("Fe", np.array([1.25, 2.35, 4.46]), self.lattice) site.to_unit_cell(in_place=True) val = [0.25, 0.35, 0.46] self.assertArrayAlmostEqual(site.frac_coords, val) lattice_pbc = Lattice(self.lattice.matrix, pbc=(True, True, False)) site = PeriodicSite("Fe", np.array([1.25, 2.35, 4.46]), lattice_pbc) site.to_unit_cell(in_place=True) val = [0.25, 0.35, 4.46] self.assertArrayAlmostEqual(site.frac_coords, val)
def test_is_periodic_image(self): other = PeriodicSite("Fe", np.array([1.25, 2.35, 4.45]), self.lattice) self.assertTrue(self.site.is_periodic_image(other), "This other site should be a periodic image.") other = PeriodicSite("Fe", np.array([1.25, 2.35, 4.46]), self.lattice) self.assertFalse(self.site.is_periodic_image(other), "This other site should not be a periodic image.") other = PeriodicSite("Fe", np.array([1.25, 2.35, 4.45]), Lattice.rhombohedral(2, 60)) self.assertFalse(self.site.is_periodic_image(other), "Different lattices should not be periodic images.")
def rediscovery(migrate,vorosites,stru): labels = [] recover_labels = [] recover_state = {} true_recover_dis = {} #点类型,分别表示间隙、瓶颈、面心 points_type = ["It","Bn","Fc"] for k in range(len(stru.sites)): site = stru.sites[k] label = site._atom_site_label if migrate not in label: continue #labels记录所有的label if label not in labels: labels.append(label) #recover_labels记录已恢复的label if label in recover_labels: continue for pts_idx, pts in enumerate (vorosites): cp_tag = np.ones((len(pts), ), dtype=int) for pt_idx, pt in enumerate (pts): if cp_tag[pt_idx] != -1: print("mobile:",site,"label",label) print("void:",pt) #以Ar作为临时当前空隙的表示符号 tmp_site = PeriodicSite("Ar",pt,stru.lattice) print(site.distance(tmp_site)) if site.distance(tmp_site) < 0.5: #当某空隙位已与结构中的晶格位配对时,将该空隙位以及与该空隙位0.25A半径范围内的所有空隙位移除,后续不再判断。 recover_labels.append(label) true_recover_dis[str(label)] = (points_type[pts_idx]+str(pt_idx),site.distance(tmp_site)) cp_tag[pt_idx] = -1 for pt_idx2, pt2 in enumerate (pts): tmp_site2 = PeriodicSite("Ar",list(pt2),stru.lattice) if tmp_site.distance(tmp_site2) < 0.25: cp_tag[pt_idx2] = -1 break #统计当前结构的恢复率 recover_rate = len(recover_labels)/len(labels) for la in labels: if la in recover_labels: recover_state[str(la)] = True else: recover_state[str(la)] = False return recover_rate, recover_state, true_recover_dis
def test_distance_and_image(self): other_site = PeriodicSite("Fe", np.array([1, 1, 1]), self.lattice) (distance, image) = self.site.distance_and_image(other_site) self.assertAlmostEqual(distance, 6.22494979899, 5) self.assertTrue(([-1, -1, -1] == image).all()) (distance, image) = self.site.distance_and_image(other_site, [1, 0, 0]) self.assertAlmostEqual(distance, 19.461500456028563, 5) # Test that old and new distance algo give the same ans for # "standard lattices" lattice = Lattice(np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])) site1 = PeriodicSite("Fe", np.array([0.01, 0.02, 0.03]), lattice) site2 = PeriodicSite("Fe", np.array([0.99, 0.98, 0.97]), lattice) self.assertAlmostEqual( get_distance_and_image_old(site1, site2)[0], site1.distance_and_image(site2)[0]) lattice = Lattice.from_parameters(1, 0.01, 1, 10, 10, 10) site1 = PeriodicSite("Fe", np.array([0.01, 0.02, 0.03]), lattice) site2 = PeriodicSite("Fe", np.array([0.99, 0.98, 0.97]), lattice) self.assertTrue( get_distance_and_image_old(site1, site2)[0] > site1.distance_and_image(site2)[0]) site2 = PeriodicSite("Fe", np.random.rand(3), lattice) (dist_old, jimage_old) = get_distance_and_image_old(site1, site2) (dist_new, jimage_new) = site1.distance_and_image(site2) self.assertTrue(dist_old - dist_new > -1e-8, "New distance algo should give smaller answers!") self.assertFalse((abs(dist_old - dist_new) < 1e-8) ^ (jimage_old == jimage_new).all(), "If old dist == new dist, images must be the same!") latt = Lattice.from_parameters(3.0, 4.0, 10.0, 3.0, 1.0, 2.0) site = PeriodicSite("Fe", [0.1, 0.1, 0.1], latt) site2 = PeriodicSite("Fe", [0.99, 0.99, 0.99], latt) (dist, img) = site.distance_and_image(site2) self.assertAlmostEqual(dist, 1.1304420998572722) self.assertEqual(list(img), [0, -1, -1])
def setUp(self): self.lattice = Lattice.cubic(10.0) self.si = Element("Si") self.site = PeriodicSite("Fe", [0.25, 0.35, 0.45], self.lattice) self.site2 = PeriodicSite({"Si": 0.5}, [0, 0, 0], self.lattice) self.assertEqual(self.site2.species, Composition({Element('Si'): 0.5}), "Inconsistent site created!") self.propertied_site = PeriodicSite(Specie("Fe", 2), [0.25, 0.35, 0.45], self.lattice, properties={ 'magmom': 5.1, 'charge': 4.2 }) self.dummy_site = PeriodicSite("X", [0, 0, 0], self.lattice)
def test_standardization(self): import numpy as np from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.core.structure import Structure from pymatgen.core.lattice import Lattice from pymatgen.core.sites import PeriodicSite from supercellor.supercell import standardize_cell import itertools np.random.seed(10) for i in range(20): lattice = Lattice(1 * np.eye(3) + 0.5 * (np.random.random((3, 3)) - 0.5)) sites = [] for pos in itertools.product([-0.5, 0.5], repeat=3): sites.append( PeriodicSite("H", pos, lattice, coords_are_cartesian=True)) structure = Structure.from_sites(sites) sga = SpacegroupAnalyzer(structure) standardized_pymatgen = sga.get_primitive_standard_structure() standardized_supercellor = standardize_cell(structure, False) sga2 = SpacegroupAnalyzer(standardized_supercellor) standardized_pymatgen2 = sga.get_primitive_standard_structure() # Checking if I get to the same lattice when standardizing the structure standardize # by the supercellor. Note: standardized_pymatgen != standardized_supercellor # because they do not use the same algorithm self.assertTrue( ((standardized_pymatgen._lattice.matrix - standardized_pymatgen2._lattice.matrix)**2).sum() < 1e-6) self.assertTrue(standardized_pymatgen2 == standardized_pymatgen)
def assign_single(s_ori, prop, cutoffs, v_species): remade_sites = [] for site, site_prop in zip(s_ori, prop): site_element = make_element_string(str(site.specie)) if site_element in Fix_Charges_dict: # No bayers process required. oxi = Fix_Charges_dict[site_element] site_sp = make_specie_string(site_element, oxi) #print('site_sp:',site_sp) else: site_v_sps = [] site_cutoffs = [] for cutoff, v_sp in zip(cutoffs, v_species): if make_element_string(v_sp) == site_element: site_cutoffs.append(cutoff) site_v_sps.append(v_sp) if site_prop > cutoffs[-1]: site_sp = None #site_property overflowing! We assume this is not very frequent. return None elif site_prop < cutoffs[0]: site_sp = v_species[0] else: for idx in range(1, len(cutoffs)): if site_prop > cutoffs[idx - 1] and site_prop < cutoffs[idx]: site_sp = v_species[idx] break remade_site = PeriodicSite(site_sp, site.frac_coords, site.lattice) #print(remade_site) remade_sites.append(remade_site) return Structure.from_sites(remade_sites)
def operate_site(site): new_cart = symmop.operate(site.coords) new_frac = self._lattice.get_fractional_coords(new_cart) return PeriodicSite(site.species_and_occu, new_frac, self._lattice, properties=site.properties)
def test_methods_compatibility(self): import json, numpy as np, itertools, os from pymatgen.core.structure import Structure from pymatgen.core.lattice import Lattice from pymatgen.core.sites import PeriodicSite from supercellor.supercell import make_supercell create_new = False if create_new: lattice = Lattice(1.1 * np.eye(3) + (np.random.random((3, 3)) - 0.5)) sites = [] for pos in itertools.product([-0.5, 0.5], repeat=3): sites.append( PeriodicSite("H", pos, lattice, coords_are_cartesian=True)) structure = Structure.from_sites(sites) counter = 1 while True: fname = 'data/structure-{}.json'.format(counter) if os.path.exists(fname): counter += 1 else: break if counter > 100: raise Exception("Reached 100 files") with open(fname, 'w') as f: json.dump(structure.as_dict(), f) else: with open('data/structure-1.json', 'r') as f: d = json.load(f) structure = Structure.from_dict(d) for radius in np.linspace(1.0, 5.0, 5): supercell1, scale1 = make_supercell(structure, distance=radius, method='hnf', implementation='pyth', verbosity=0, wrap=True, standardize=True, do_niggli_first=False) # make fortran work and test: supercell2, scale2 = make_supercell(structure, distance=radius, method='hnf', implementation='fort', verbosity=0, wrap=True, standardize=True, do_niggli_first=False) for idim in range(3): self.assertTrue( np.linalg.norm(supercell1._lattice.matrix[idim]) >= radius) self.assertTrue( np.linalg.norm(supercell2._lattice.matrix[idim]) >= radius) self.assertTrue( abs(supercell1._lattice.volume - supercell2._lattice.volume) < 1e-6)
def test_get_symmetry_operations(self): for sg, structure in [(self.sg, self.structure), (self.sg4, self.structure4)]: pgops = sg.get_point_group_operations() fracsymmops = sg.get_symmetry_operations() symmops = sg.get_symmetry_operations(True) latt = structure.lattice for fop, op, pgop in zip(fracsymmops, symmops, pgops): # translation vector values should all be 0 or 0.5 t = fop.translation_vector * 2 self.assertArrayAlmostEqual(t - np.round(t), 0) self.assertArrayAlmostEqual(fop.rotation_matrix, pgop.rotation_matrix) for site in structure: newfrac = fop.operate(site.frac_coords) newcart = op.operate(site.coords) self.assertTrue(np.allclose(latt.get_fractional_coords(newcart), newfrac)) found = False newsite = PeriodicSite(site.species, newcart, latt, coords_are_cartesian=True) for testsite in structure: if newsite.is_periodic_image(testsite, 1e-3): found = True break self.assertTrue(found) # Make sure this works for any position, not just the atomic # ones. random_fcoord = np.random.uniform(size=(3)) random_ccoord = latt.get_cartesian_coords(random_fcoord) newfrac = fop.operate(random_fcoord) newcart = op.operate(random_ccoord) self.assertTrue(np.allclose(latt.get_fractional_coords(newcart), newfrac))
def get_bone_config(self): """ :return: a configuration that has only terminated backbones a pmg structure that has only terminated backbones a list of pmg molecules """ terminated_backbone_hmols = [ conformer_addhmol(mc.backbone, joints=mc.backbone_graph.joints, original=mc) for mc in self.molconformers ] backbone_sites = [] for b in terminated_backbone_hmols: backbone_sites += b.sites boneonly_psites = [ PeriodicSite(s.species_string, s.coords, self.pstructure.lattice, to_unit_cell=False, coords_are_cartesian=True, properties=s.properties) for s in backbone_sites ] boneonly_pstructure = Structure.from_sites(boneonly_psites) boneonly_molconformers = [ MolConformer.from_pmgmol(m) for m in terminated_backbone_hmols ] return Config( boneonly_molconformers, boneonly_pstructure, occu=self.occu), boneonly_pstructure, terminated_backbone_hmols
def add_oxidation_state_by_site(self, oxidation_states): """ Add oxidation states to a structure by site. Args: oxidation_states: List of oxidation states. E.g., [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2] """ try: for i, site in enumerate(self._sites): new_sp = {} for el, occu in site.species_and_occu.items(): sym = el.symbol new_sp[Specie(sym, oxidation_states[i])] = occu new_site = PeriodicSite(new_sp, site.frac_coords, self._lattice, coords_are_cartesian=False, properties=site.properties) self._sites[i] = new_site except IndexError: raise ValueError("Oxidation state of all sites must be " "specified in the dictionary.")
def from_bson_voronoi_list2(bson_nb_voro_list2, structure): """ Returns the voronoi_list needed for the VoronoiContainer object from a bson-encoded voronoi_list. Args: bson_nb_voro_list2: List of periodic sites involved in the Voronoi. structure: Structure object. Returns: The voronoi_list needed for the VoronoiContainer (with PeriodicSites as keys of the dictionary - not allowed in the BSON format). """ voronoi_list = [None] * len(bson_nb_voro_list2) for isite, voro in enumerate(bson_nb_voro_list2): if voro is None or voro == "None": continue voronoi_list[isite] = [] for psd, dd in voro: struct_site = structure[dd["index"]] periodic_site = PeriodicSite( struct_site._species, struct_site.frac_coords + psd[1], struct_site._lattice, properties=struct_site.properties, ) dd["site"] = periodic_site voronoi_list[isite].append(dd) return voronoi_list
def graft_coordinates_onto_structure(self, coordstruc): """Graft coordinates from mast_coordinates Structure objects onto the appropriate structure Args: coordstruc <Structure>: Structure object with the coordinates for grafting self.keywords['struc_work1'] will contain the elements and lattice parameter, which will not be touched. Returns: modified Structure object <Structure> """ goodstruc = self.keywords['struc_work1'].copy() lengoodsites = len(goodstruc.sites) lencoordsites = len(coordstruc.sites) if not (lengoodsites == lencoordsites): raise MASTError( self.__class__.__name__, "Original and coordinate structures do not have the same amount of sites in %s" % self.keywords['name']) cct = 0 newsites = list() mylattice = goodstruc.lattice while cct < lengoodsites: newcoords = coordstruc.sites[cct].frac_coords oldspecie = goodstruc.sites[cct].specie newsite = PeriodicSite(oldspecie, newcoords, mylattice) newsites.append(newsite) cct = cct + 1 goodstruc.remove_sites(range(0, lengoodsites)) for cct in range(0, lengoodsites): goodstruc.append(newsites[cct].specie, newsites[cct].frac_coords) return goodstruc
def add_oxidation_state_by_element(self, oxidation_states): """ Add oxidation states to a structure. Args: structure: pymatgen.core.structure Structure object. oxidation_states: dict of oxidation states. E.g., {"Li":1, "Fe":2, "P":5, "O":-2} """ try: for i, site in enumerate(self._sites): new_sp = {} for el, occu in site.species_and_occu.items(): sym = el.symbol new_sp[Specie(sym, oxidation_states[sym])] = occu new_site = PeriodicSite(new_sp, site.frac_coords, self._lattice, coords_are_cartesian=False, properties=site.properties) self._sites[i] = new_site except KeyError: raise ValueError("Oxidation state of all elements must be " "specified in the dictionary.")
def translate_sites(self, indices, vector, frac_coords=True): """ Translate specific sites by some vector, keeping the sites within the unit cell. Args: sites: List of site indices on which to perform the translation. vector: Translation vector for sites. frac_coords: Boolean stating whether the vector corresponds to fractional or cartesian coordinates. """ for i in indices: site = self._sites[i] if frac_coords: fcoords = site.frac_coords + vector else: fcoords = self._lattice.get_fractional_coords(site.coords + vector) new_site = PeriodicSite(site.species_and_occu, fcoords, self._lattice, to_unit_cell=True, coords_are_cartesian=False, properties=site.properties) self._sites[i] = new_site
def setUp(self): self.struc = PymatgenTest.get_structure("VO2") V_index = self.struc.indices_from_symbol("V")[0] sub_site = PeriodicSite("Sr", self.struc[V_index].coords, self.struc.lattice, coords_are_cartesian=True) self.substitution = Substitution(self.struc, sub_site)
def test_vacancy(self): struc = PymatgenTest.get_structure("VO2") V_index = struc.indices_from_symbol("V")[0] vac = Vacancy(struc, struc[V_index]) # test generation and super cell vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.composition.as_dict(), {"V": 1, "O": 4}) vac_struc = vac.generate_defect_structure(2) self.assertEqual(vac_struc.composition.as_dict(), {"V": 15, "O": 32}) vac_struc = vac.generate_defect_structure(3) self.assertEqual(vac_struc.composition.as_dict(), {"V": 53, "O": 108}) vac_struc = vac.generate_defect_structure([[2.0, 0, 0], [0, 0, -3.0], [0, 2.0, 0]]) self.assertEqual(vac_struc.composition.as_dict(), {"V": 23, "O": 48}) # test charge vac = Vacancy(struc, struc[V_index]) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 0.0) vac = Vacancy(struc, struc[V_index], charge=1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 1.0) vac = Vacancy(struc, struc[V_index], charge=-1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, -1.0) # test multiplicity vac = Vacancy(struc, struc[V_index]) self.assertEqual(vac.multiplicity, 2) O_index = struc.indices_from_symbol("O")[0] vac = Vacancy(struc, struc[O_index]) self.assertEqual(vac.multiplicity, 4) # Test composition self.assertEqual(dict(vac.defect_composition.as_dict()), { "V": 2, "O": 3 }) # test lattice value error occurs for different lattices sc_scaled_struc = struc.copy() sc_scaled_struc.make_supercell(2) self.assertRaises(ValueError, Vacancy, struc, sc_scaled_struc[V_index]) self.assertRaises(ValueError, Vacancy, sc_scaled_struc, struc[V_index]) # test value error raised for site not in the structure non_site = PeriodicSite("V", struc[V_index].frac_coords + [0.0, 0.0, 0.1], struc.lattice) self.assertRaises(ValueError, Vacancy, struc, non_site)
def test_interstitial(self): struc = PymatgenTest.get_structure("VO2") V_index = struc.indices_from_symbol("V")[0] int_site = PeriodicSite("V", struc[V_index].coords + [0.1, 0.1, 0.1], struc.lattice) interstitial = Interstitial(struc, int_site) # test generation and super cell int_struc = interstitial.generate_defect_structure(1) self.assertEqual(int_struc.composition.as_dict(), {"V": 3, "O": 4}) # Ensure the site is in the right place self.assertEqual(int_site, int_struc.get_sites_in_sphere(int_site.coords, 0.1)[0][0]) int_struc = interstitial.generate_defect_structure(2) self.assertEqual(int_struc.composition.as_dict(), {"V": 17, "O": 32}) int_struc = interstitial.generate_defect_structure(3) self.assertEqual(int_struc.composition.as_dict(), {"V": 55, "O": 108}) int_struc = interstitial.generate_defect_structure([[2.0, 0, 0], [0, 0, -3.0], [0, 2.0, 0]]) self.assertEqual(int_struc.composition.as_dict(), {"V": 25, "O": 48}) # test charge interstitial = Interstitial(struc, int_site) int_struc = interstitial.generate_defect_structure(1) self.assertEqual(int_struc.charge, 0.0) interstitial = Interstitial(struc, int_site, charge=1.0) int_struc = interstitial.generate_defect_structure(1) self.assertEqual(int_struc.charge, 1.0) interstitial = Interstitial(struc, int_site, charge=-1.0) int_struc = interstitial.generate_defect_structure(1) self.assertEqual(int_struc.charge, -1.0) # test multiplicity interstitial = Interstitial(struc, int_site) self.assertEqual(interstitial.multiplicity, 8.0) # test manual setting of multiplicity interstitial = Interstitial(struc, int_site, multiplicity=4.0) self.assertEqual(interstitial.multiplicity, 4.0) # Test composition self.assertEqual(dict(interstitial.defect_composition.as_dict()), {"V": 3, "O": 4}) # test that structure generation doesn't break if velocities existed previously # (previously caused failures for structure printing) vel_struc = Structure( struc.lattice, struc.species, struc.frac_coords, site_properties={"velocities": [[0.0, 0.0, 0.0]] * len(struc)}, ) interstitial = Interstitial(vel_struc, int_site, charge=-1.0) int_struc = interstitial.generate_defect_structure(1) self.assertTrue("velocities" not in int_struc.site_properties)
def reduceScale(structure, scale, dim): """ Attempt to reduce the structure from a supercell inputs -------- structure (Structure): Pymatgen structure object to reduce scale (float): How much to scale the periodic lattice vectors by dim (int): The number of periodic directions in "structure" outputs -------- new_struct (Structure): Structure object for low-dimensional material with non-periodic directions being orthogonal to the periodic directions """ structure.translate_sites(indices=range(structure.num_sites), vector=-1 * structure.sites[0].frac_coords + [0, 0, .5]) specs = structure.species cart = [x.coords for x in structure.sites] lat = np.array(structure.lattice.as_dict()['matrix']) lat[0] *= scale if dim == 2 or dim == 3: lat[1] *= scale if dim == 3: lat[2] *= scale fracs = np.array(np.linalg.solve(lat.T, np.array(cart).T).T) specs = [] u_cart = [] i = 0 for frac in fracs: if frac[0] < 1 and frac[1] < 1 and frac[2] < 1: specs.append(structure.species[i]) u_cart.append(cart[i]) i += 1 new_sites = [] i = 0 for site in u_cart: p = PeriodicSite(atoms_n_occu=Element(specs[i]), lattice=Lattice(lat), coords=site, coords_are_cartesian=True) new_sites.append(p) i += 1 new_struct = Structure.from_sites(new_sites) return (new_struct)
def _get_structures(self, num_structs): structs = [] rs = subprocess.Popen( ["makestr.x", "struct_enum.out", str(0), str(num_structs - 1)], stdout=subprocess.PIPE, stdin=subprocess.PIPE, close_fds=True) rs.communicate() if len(self.ordered_sites) > 0: original_latt = self.ordered_sites[0].lattice # Need to strip sites of site_properties, which would otherwise # result in an index error. Hence Structure is reconstructed in # the next step. ordered_structure = Structure( original_latt, [site.species_and_occu for site in self.ordered_sites], [site.frac_coords for site in self.ordered_sites]) inv_org_latt = np.linalg.inv(original_latt.matrix) for n in range(1, num_structs + 1): with open("vasp.{:06d}".format(n)) as f: data = f.read() data = re.sub("scale factor", "1", data) data = re.sub("(\d+)-(\d+)", r"\1 -\2", data) poscar = Poscar.from_string(data, self.index_species) sub_structure = poscar.structure #Enumeration may have resulted in a super lattice. We need to #find the mapping from the new lattice to the old lattice, and #perform supercell construction if necessary. new_latt = sub_structure.lattice sites = [] if len(self.ordered_sites) > 0: transformation = np.dot(new_latt.matrix, inv_org_latt) transformation = [[int(round(cell)) for cell in row] for row in transformation] logger.debug("Supercell matrix: {}".format(transformation)) s = Structure.from_sites(ordered_structure) s.make_supercell(transformation) sites.extend([site.to_unit_cell for site in s]) super_latt = sites[-1].lattice else: super_latt = new_latt for site in sub_structure: if site.specie.symbol != "X": # We exclude vacancies. sites.append( PeriodicSite(site.species_and_occu, site.frac_coords, super_latt).to_unit_cell) structs.append(Structure.from_sites(sorted(sites))) logger.debug("Read in a total of {} structures.".format(num_structs)) return structs
def get_defect_site(self, initdef): siteinds = [] ## this is relatively simple for vacancies and substitutionals ## which by definition are a pre-existing site in the structure if initdef["type"][0] == "v" or initdef["type"][0] == "s": siteind = self.get_site_ind(initdef["species"], initdef["index"], initdef["index_offset_basis"], initdef["index_offset_a"], initdef["index_offset_b"]) siteinds.append(siteind) defect_site = self.structure_bulk[siteind] ## for interstitials/adatoms, the defect site coords are defined ## by the center (average) of the user-provided list of sites elif initdef["type"][0] == "i" or initdef["type"][0] == "a": siteind = [ self.get_site_ind(sp, ind, offset_basis, offset_a, offset_b) for sp, ind, offset_basis, offset_a, offset_b in zip( initdef["species"], initdef["index"], initdef["index_offset_basis"], initdef["index_offset_a"], initdef["index_offset_b"]) ] siteinds.append(siteind) ## get the averaged position coords_ref = self.structure_bulk[siteind[0]].frac_coords # print (coords_ref) defect_coords = coords_ref.copy() for i in siteind[1:]: coords = self.structure_bulk[i].frac_coords # print (coords,coords_ref,np.array(coords[0:3])-np.array(coords_ref[0:3])) for j in [0, 1, 2]: ## dealing with pbc if (coords[j] - coords_ref[j]) > 0.501: coords[j] -= 1.0 elif (coords_ref[j] - coords[j]) > 0.501: coords[j] += 1.0 # print (coords) defect_coords += coords defect_coords = defect_coords / len(siteind) # print (defect_coords) ## we may want to further shift the position manually M = self.structure.lattice.matrix defect_coords += np.dot( np.linalg.inv(M).T, [initdef["shift_x"], initdef["shift_y"], initdef["shift_z"]]) ## create the defect_site as a PeriodicSite object defect_site = PeriodicSite(initdef["species_new"], defect_coords, lattice=self.structure.lattice, coords_are_cartesian=False) return defect_site
def apply_tags(self): tags = {} for tag in self.tags: istruct = tag.get('istruct', 'all') if istruct != 'all': if istruct != self.istruct: continue site_index = tag['site_index'] color = tag.get('color', [0.5, 0.5, 0.5]) opacity = tag.get('opacity', 0.5) if site_index == 'unit_cell_all': struct_radii = self.all_vis_radii[self.istruct] for isite, site in enumerate(self.current_structure): vis_radius = 1.5 * tag.get('radius', struct_radii[isite]) tags[(isite, (0, 0, 0))] = { 'radius': vis_radius, 'color': color, 'opacity': opacity } continue cell_index = tag['cell_index'] if 'radius' in tag: vis_radius = tag['radius'] elif 'radius_factor' in tag: vis_radius = tag['radius_factor'] * self.all_vis_radii[ self.istruct][site_index] else: vis_radius = 1.5 * self.all_vis_radii[self.istruct][site_index] tags[(site_index, cell_index)] = { 'radius': vis_radius, 'color': color, 'opacity': opacity } for site_and_cell_index, tag_style in tags.items(): isite, cell_index = site_and_cell_index site = self.current_structure[isite] if cell_index == (0, 0, 0): coords = site.coords else: fcoords = site.frac_coords + np.array(cell_index) site_image = PeriodicSite(site.species, fcoords, self.current_structure.lattice, to_unit_cell=False, coords_are_cartesian=False, properties=site.properties) self.add_site(site_image) coords = site_image.coords vis_radius = tag_style['radius'] color = tag_style['color'] opacity = tag_style['opacity'] self.add_partial_sphere(coords=coords, radius=vis_radius, color=color, start=0, end=360, opacity=opacity)
def get_freysoldt_correction(defect_type, defect_specie, path_to_defect_locpot,path_to_pure_locpot,charge, dielectric_constant,defect_site_coordinates,energy_cutoff=500,get_plot=False): ''' Function to perform charge corrections according to the method proposed py Freysoldt If this correction is used, please reference Freysoldt's original paper. doi: 10.1103/PhysRevLett.102.016402 Args: defect_type: 'vacancy' or 'interstitial' defect_specie: string with element occupying the defect site path_to_defect_locpot: path to LOCPOT file of defect structure path_to_pure_locpot: path to LOCPOT file of Pure structure charge: Charge of the defected system dielectric_constant: Dielectric constant defect_site_coordinates: numpy array with fractional coordinates of defect site energy_cutoff: Cut-off of plane wave expansion get_plot: return also Matplotlib object with plot Returns: Freysoldt corrections values as a dictionary ''' # acquiring data from LOCPOT files locpot_pure = Locpot.from_file(path_to_pure_locpot) vol_data_pure = VolumetricData(locpot_pure.structure,locpot_pure.data) locpot_defect = Locpot.from_file(path_to_defect_locpot) vol_data_defect = VolumetricData(locpot_defect.structure,locpot_defect.data) parameters = {} parameters['axis_grid'] = [] parameters['bulk_planar_averages'] = [] parameters['defect_planar_averages'] = [] for i in range(0,3): parameters['axis_grid'].append(vol_data_pure.get_axis_grid(i)) parameters['bulk_planar_averages'].append(vol_data_pure.get_average_along_axis(i)) parameters['defect_planar_averages'].append(vol_data_defect.get_average_along_axis(i)) parameters['initial_defect_structure'] = locpot_defect.structure parameters['defect_frac_sc_coords'] = defect_site_coordinates structure_bulk = locpot_pure.structure defect_site = PeriodicSite(defect_specie, coords=defect_site_coordinates, lattice = locpot_pure.structure.lattice) module = importlib.import_module("pymatgen.analysis.defects.core") defect_class = getattr(module,defect_type) defect = defect_class(structure_bulk, defect_site, charge=charge, multiplicity=None) defect_entry = DefectEntry(defect,None,corrections=None,parameters=parameters) freysoldt_class = FreysoldtCorrection(dielectric_constant,energy_cutoff=energy_cutoff) freysoldt_corrections = freysoldt_class.get_correction(defect_entry) if get_plot: plt = freysoldt_class.plot(1) return freysoldt_corrections , plt else: return freysoldt_corrections
def nearest_atom_mine(atoms, position, nth_closest=0): """ args: atoms: position: nth_closest: pass 0 for nearest atom, 1 for 2nd neareset and so on... """ #| - nearest_atom_mine struct = AseAtomsAdaptor.get_structure(atoms) Lattice = struct.lattice # ######################################################### dummy_site_j = PeriodicSite("N", position, Lattice, to_unit_cell=False, coords_are_cartesian=True, properties=None, skip_checks=False) data_dict_list = [] for index_i, site_i in enumerate(struct): data_dict_i = dict() # site_i = struct[32] distance_i, image_i = site_i.distance_and_image(dummy_site_j) # print("distance_i:", distance_i) # ##################################################### data_dict_i["atoms_index"] = int(index_i) data_dict_i["distance"] = distance_i data_dict_i["image"] = image_i # ##################################################### data_dict_list.append(data_dict_i) # ######################################################### df_dist = pd.DataFrame(data_dict_list) df_dist = df_dist.sort_values("distance") # ######################################################### closest_site = df_dist.iloc[nth_closest] closest_index = int(closest_site.atoms_index) closest_distance = closest_site.distance image = closest_site.image closest_atom = atoms[closest_index] out_dict = dict( closest_atom=closest_atom, closest_distance=closest_distance, image=image, ) return (out_dict)
def insert_site(self, i, species, coords, coords_are_cartesian=False, validate_proximity=True, properties=None): """ Insert a site to the structure. Args: i: index to insert site species: species of inserted site coords: coordinates of inserted site coords_are_cartesian: Whether coordinates are cartesian. Defaults to False. validate_proximity: Whether to check if inserted site is too close to an existing site. Defaults to True. """ if not coords_are_cartesian: new_site = PeriodicSite(species, coords, self._lattice, properties=properties) else: frac_coords = self._lattice.get_fractional_coords(coords) new_site = PeriodicSite(species, frac_coords, self._lattice, properties=properties) if validate_proximity: for site in self._sites: if site.distance(new_site) < self.DISTANCE_TOLERANCE: raise ValueError("New site is too close to an existing " "site!") self._sites.insert(i, new_site)
def write_input(self, output_dir, make_dir_if_not_present=True, write_cif=False, write_path_cif=False, write_endpoint_inputs=False): """ NEB inputs has a special directory structure where inputs are in 00, 01, 02, .... Args: output_dir (str): Directory to output the VASP input files make_dir_if_not_present (bool): Set to True if you want the directory (and the whole path) to be created if it is not present. write_cif (bool): If true, writes a cif along with each POSCAR. write_path_cif (bool): If true, writes a cif for each image. write_endpoint_inputs (bool): If true, writes input files for running endpoint calculations. """ if make_dir_if_not_present and not os.path.exists(output_dir): os.makedirs(output_dir) self.incar.write_file(os.path.join(output_dir, 'INCAR')) self.kpoints.write_file(os.path.join(output_dir, 'KPOINTS')) self.potcar.write_file(os.path.join(output_dir, 'POTCAR')) for i, p in enumerate(self.poscars): d = os.path.join(output_dir, str(i).zfill(2)) if not os.path.exists(d): os.makedirs(d) p.write_file(os.path.join(d, 'POSCAR')) if write_cif: p.structure.to(filename=os.path.join(d, '{}.cif'.format(i))) if write_endpoint_inputs: end_point_param = MPRelaxSet( self.structures[0], user_incar_settings=self.user_incar_settings) for image in ['00', str(len(self.structures) - 1).zfill(2)]: end_point_param.incar.write_file( os.path.join(output_dir, image, 'INCAR')) end_point_param.kpoints.write_file( os.path.join(output_dir, image, 'KPOINTS')) end_point_param.potcar.write_file( os.path.join(output_dir, image, 'POTCAR')) if write_path_cif: sites = set() l = self.structures[0].lattice for site in chain(*(s.sites for s in self.structures)): sites.add( PeriodicSite(site.species_and_occu, site.frac_coords, l)) path = Structure.from_sites(sorted(sites)) path.to(filename=os.path.join(output_dir, 'path.cif'))