def from_dict(cls, d): """ Args: d (dict): A dict with all data for a band structure symm line object. Returns: A BandStructureSymmLine object """ try: # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) labels_dict = {k.strip(): v for k, v in d["labels_dict"].items()} projections = {} structure = None if d.get("projections"): if isinstance(d["projections"]["1"][0][0], dict): raise ValueError("Old band structure dict format detected!") structure = Structure.from_dict(d["structure"]) projections = { Spin(int(spin)): np.array(v) for spin, v in d["projections"].items() } return LobsterBandStructureSymmLine( d["kpoints"], {Spin(int(k)): d["bands"][k] for k in d["bands"]}, Lattice(d["lattice_rec"]["matrix"]), d["efermi"], labels_dict, structure=structure, projections=projections, ) except Exception: warnings.warn( "Trying from_dict failed. Now we are trying the old " "format. Please convert your BS dicts to the new " "format. The old format will be retired in pymatgen " "5.0." ) return LobsterBandStructureSymmLine.from_old_dict(d)
def apply_operation(self, symmop): """ Apply a symmetry operation to the structure and return the new structure. The lattice is operated by the rotation matrix only. Coords are operated in full and then transformed to the new lattice. Args: symmop: Symmetry operation to apply. """ self._lattice = Lattice( [symmop.apply_rotation_only(row) for row in self._lattice.matrix]) 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) self._sites = map(operate_site, self._sites)
def test_apply_transformation(self): t = OxidationStateRemovalTransformation() coords = [] coords.append([0, 0, 0]) coords.append([0.75, 0.75, 0.75]) coords.append([0.5, 0.5, 0.5]) coords.append([0.25, 0.25, 0.25]) lattice = Lattice([ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ]) struct = Structure(lattice, ["Li+", "Li+", "O2-", "O2-"], coords) s = t.apply_transformation(struct) self.assertEqual(s[0].species_string, "Li") self.assertEqual(s[2].species_string, "O") d = t.as_dict() self.assertEqual( type(OxidationStateRemovalTransformation.from_dict(d)), OxidationStateRemovalTransformation, )
def get_displacements(self): """ Return the initial structure and displacements for each time step. Used to interface witht the DiffusionAnalyzer. Returns: Structure object, numpy array of displacements """ lattice = Lattice([[self.box_lengths[0], 0, 0], [0, self.box_lengths[1], 0], [0, 0, self.box_lengths[2]]]) mass_to_symbol = dict( (round(y["Atomic mass"], 1), x) for x, y in _pt_data.items()) unique_atomic_masses = np.array(self.lammps_data.atomic_masses)[:, 1] frac_coords = [] for step in range(self.timesteps.size): begin = step * self.natoms end = (step + 1) * self.natoms mol_vector_structured = \ self.trajectory[begin:end][:][["x", "y", "z"]] new_shape = mol_vector_structured.shape + (-1,) mol_vector = mol_vector_structured.view(np.float64).reshape( new_shape) coords = mol_vector.copy() if step == 0: species = [ mass_to_symbol[round(unique_atomic_masses[atype - 1], 1)] for atype in self.trajectory[begin:end][:]["atom_type"]] mol = Molecule(species, coords) structure = mol.get_boxed_structure(*self.box_lengths) step_frac_coords = [lattice.get_fractional_coords(crd) for crd in coords] frac_coords.append(np.array(step_frac_coords)[:, None]) frac_coords = np.concatenate(frac_coords, axis=1) dp = frac_coords[:, 1:] - frac_coords[:, :-1] dp = dp - np.round(dp) f_disp = np.cumsum(dp, axis=1) disp = lattice.get_cartesian_coords(f_disp) return structure, disp
def test_get_distance_and_image_strict(self): for count in range(10): lengths = [np.random.randint(1, 100) for i in range(3)] lattice = [np.random.rand(3) * lengths[i] for i in range(3)] lattice = Lattice(np.array(lattice)) f1 = np.random.rand(3) f2 = np.random.rand(3) scope = list(range(-3, 4)) min_image_dist = (float("inf"), None) for image in itertools.product(scope, scope, scope): cart = lattice.get_cartesian_coords(f1 - (f2 + image)) dist = np.dot(cart, cart) ** 0.5 if dist < min_image_dist[0]: min_image_dist = (dist, image) pmg_result = lattice.get_distance_and_image(f1, f2) self.assertGreaterEqual(min_image_dist[0] + 1e-7, pmg_result[0]) if abs(min_image_dist[0] - pmg_result[0]) < 1e-12: self.assertArrayAlmostEqual(min_image_dist[1], pmg_result[1])
def test_static_methods(self): lengths_c = [3.840198, 3.84019885, 3.8401976] angles_c = [119.99998575, 90, 60.00000728] mat_c = [[3.840198, 0.000000, 0.0000], [1.920099, 3.325710, 0.000000], [0.000000, -2.217138, 3.135509]] # should give the lengths and angles above newlatt = Lattice(mat_c) lengths = newlatt.lengths angles = newlatt.angles for i in range(0, 3): self.assertAlmostEqual(lengths[i], lengths_c[i], 5, "Lengths incorrect!") self.assertAlmostEqual(angles[i], angles_c[i], 5, "Angles incorrect!") latt = Lattice.from_parameters(*lengths, *angles) lengths = latt.lengths angles = latt.angles for i in range(0, 3): self.assertAlmostEqual(lengths[i], lengths_c[i], 5, "Lengths incorrect!") self.assertAlmostEqual(angles[i], angles_c[i], 5, "Angles incorrect!")
def test_kpath_generation(self): triclinic = [1, 2] monoclinic = range(3, 16) orthorhombic = range(16, 75) tetragonal = range(75, 143) rhombohedral = range(143, 168) hexagonal = range(168, 195) cubic = range(195, 231) species = ["K", "La", "Ti"] coords = [[0.345, 5, 0.77298], [0.1345, 5.1, 0.77298], [0.7, 0.8, 0.9]] for i in range(230): sg_num = i + 1 if sg_num in triclinic: lattice = Lattice( [ [3.0233057319441246, 1, 0], [0, 7.9850357844548681, 1], [0, 1.2, 8.1136762279561818], ] ) elif sg_num in monoclinic: lattice = Lattice.monoclinic(2, 9, 1, 99) elif sg_num in orthorhombic: lattice = Lattice.orthorhombic(2, 9, 1) elif sg_num in tetragonal: lattice = Lattice.tetragonal(2, 9) elif sg_num in rhombohedral: lattice = Lattice.hexagonal(2, 95) elif sg_num in hexagonal: lattice = Lattice.hexagonal(2, 9) elif sg_num in cubic: lattice = Lattice.cubic(2) struct = Structure.from_spacegroup(sg_num, lattice, species, coords) # Throws error if something doesn't work, causing test to fail. kpath = HighSymmKpath(struct, path_type="all") kpoints = kpath.get_kpoints()
def from_old_dict(cls, d): """ Args: d (dict): A dict with all data for a band structure symm line object. Returns: A BandStructureSymmLine object """ # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) labels_dict = {k.strip(): v for k, v in d['labels_dict'].items()} projections = {} structure = None if 'projections' in d and len(d['projections']) != 0: structure = Structure.from_dict(d['structure']) projections = {} for spin in d['projections']: dd = [] for i in range(len(d['projections'][spin])): ddd = [] for j in range(len(d['projections'][spin][i])): dddd = [] for k in range(len(d['projections'][spin][i][j])): ddddd = [] orb = Orbital(k).name for l in range(len(d['projections'][spin][i][j][ orb])): ddddd.append(d['projections'][spin][i][j][ orb][l]) dddd.append(np.array(ddddd)) ddd.append(np.array(dddd)) dd.append(np.array(ddd)) projections[Spin(int(spin))] = np.array(dd) return BandStructureSymmLine( d['kpoints'], {Spin(int(k)): d['bands'][k] for k in d['bands']}, Lattice(d['lattice_rec']['matrix']), d['efermi'], labels_dict, structure=structure, projections=projections)
def get_structures_from_trajectory(self): """ Convert the coordinates in each time step to a structure(boxed molecule). Used to construct DiffusionAnalyzer object. Returns: list of Structure objects """ lattice = Lattice([[self.box_lengths[0], 0, 0], [0, self.box_lengths[1], 0], [0, 0, self.box_lengths[2]]]) structures = [] mass_to_symbol = dict( (round(y["Atomic mass"], 1), x) for x, y in _pt_data.items()) unique_atomic_masses = np.array(self.lammps_data.atomic_masses)[:, 1] for step in range(self.timesteps.size): begin = step * self.natoms end = (step + 1) * self.natoms mol_vector_structured = \ self.trajectory[begin:end][:][["x", "y", "z"]] new_shape = mol_vector_structured.shape + (-1, ) mol_vector = mol_vector_structured.view( np.float64).reshape(new_shape) coords = mol_vector.copy() species = [ mass_to_symbol[round(unique_atomic_masses[atype - 1], 1)] for atype in self.trajectory[begin:end][:]["atom_type"] ] try: structure = Structure(lattice, species, coords, coords_are_cartesian=True) except ValueError as error: print("Error: '{}' at timestep {} in the trajectory".format( error, int(self.timesteps[step]))) structures.append(structure) return structures
def test_apply_transformation(self): t = OrderDisorderedStructureTransformation() coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.75, 0.75]) coords.append([0.5, 0.5, 0.5]) coords.append([0.25, 0.25, 0.25]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) struct = Structure(lattice, [{"Si4+":0.5, "O2-": 0.25, "P5+": 0.25}, {"Si4+":0.5, "O2-": 0.25, "P5+": 0.25}, {"Si4+":0.5, "O2-": 0.25, "P5+": 0.25}, {"Si4+":0.5, "O2-": 0.25, "P5+": 0.25}], coords) output = t.apply_transformation(struct, return_ranked_list=50) self.assertEqual(len(output), 12) self.assertIsInstance(output[0]['structure'], Structure) struct = Structure(lattice, [{"Si4+": 0.5}, {"Si4+": 0.5}, {"P5+": 0.5, "O2-": 0.5}, {"P5+": 0.5, "O2-": 0.5}], coords) output = t.apply_transformation(struct, return_ranked_list=50) self.assertIsInstance(output, list) self.assertEqual(len(output), 4) self.assertEqual(t.lowest_energy_structure, output[0]['structure']) struct = Structure(lattice, [{"Si4+":0.5}, {"Si4+":0.5}, {"O2-": 0.5}, {"O2-": 0.5}], coords) allstructs = t.apply_transformation(struct, 50) self.assertEqual(len(allstructs), 4) struct = Structure(lattice, [{"Si4+": 0.333}, {"Si4+": 0.333}, {"Si4+": 0.333}, "O2-"], coords) allstructs = t.apply_transformation(struct, 50) self.assertEqual(len(allstructs), 3)
def from_dict(cls, d): """ Create from dict. Args: A dict with all data for a band structure object. Returns: A BandStructure object """ labels_dict = d['labels_dict'] projections = {} structure = None if 'structure' in d: structure = Structure.from_dict(d['structure']) if 'projections' in d and len(d['projections']) != 0: projections = { Spin.from_int(int(spin)): [[{ Orbital.from_string(orb): [ d['projections'][spin][i][j][orb][k] for k in range(len(d['projections'][spin][i][j][orb])) ] for orb in d['projections'][spin][i][j] } for j in range(len(d['projections'][spin][i]))] for i in range(len(d['projections'][spin]))] for spin in d['projections'] } return BandStructure( d['kpoints'], {Spin.from_int(int(k)): d['bands'][k] for k in d['bands']}, Lattice(d['lattice_rec']['matrix']), d['efermi'], labels_dict, structure=structure, projections=projections)
def from_dict(cls, d): """ Args: d (dict): Dict representation Returns: GruneisenPhononBandStructure: Phonon band structure with Grueneisen parameters. """ lattice_rec = Lattice(d["lattice_rec"]["matrix"]) eigendisplacements = np.array(d["eigendisplacements"]["real"]) + np.array(d["eigendisplacements"]["imag"]) * 1j structure = Structure.from_dict(d["structure"]) if "structure" in d else None return cls( qpoints=d["qpoints"], frequencies=np.array(d["bands"]), gruneisenparameters=np.array(d["gruneisen"]), lattice=lattice_rec, eigendisplacements=eigendisplacements, labels_dict=d["labels_dict"], structure=structure, )
def test_get_vector_along_lattice_directions(self): lattice_mat = np.array( [[0.5, 0.0, 0.0], [0.5, np.sqrt(3) / 2.0, 0.0], [0.0, 0.0, 1.0]] ) lattice = Lattice(lattice_mat) cart_coord = np.array([0.5, np.sqrt(3) / 4.0, 0.5]) latt_coord = np.array([0.25, 0.5, 0.5]) from_direct = lattice.get_fractional_coords(cart_coord) * lattice.lengths self.assertArrayAlmostEqual( lattice.get_vector_along_lattice_directions(cart_coord), from_direct ) self.assertArrayAlmostEqual( lattice.get_vector_along_lattice_directions(cart_coord), latt_coord ) self.assertArrayEqual( lattice.get_vector_along_lattice_directions(cart_coord).shape, [3,] ) self.assertArrayEqual( lattice.get_vector_along_lattice_directions( cart_coord.reshape([1, 3]) ).shape, [1, 3], )
def test_apply_transformation(self): t = ChargeBalanceTransformation("Li+") coords = [] coords.append([0, 0, 0]) coords.append([0.375, 0.375, 0.375]) coords.append([0.5, 0.5, 0.5]) coords.append([0.875, 0.875, 0.875]) coords.append([0.125, 0.125, 0.125]) coords.append([0.25, 0.25, 0.25]) coords.append([0.625, 0.625, 0.625]) coords.append([0.75, 0.75, 0.75]) lattice = Lattice( [ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ] ) struct = Structure(lattice, ["Li+", "Li+", "Li+", "Li+", "Li+", "Li+", "O2-", "O2-"], coords) s = t.apply_transformation(struct) self.assertAlmostEqual(s.charge, 0, 5)
def test_fractional_substitution(self): t = SubstitutionTransformation({ "Li+": "Na+", "O2-": { "S2-": 0.5, "Se2-": 0.5 } }) # test the to and from dict on the nested dictionary t = SubstitutionTransformation.from_dict(t.as_dict()) coords = [] coords.append([0, 0, 0]) coords.append([0.75, 0.75, 0.75]) coords.append([0.5, 0.5, 0.5]) coords.append([0.25, 0.25, 0.25]) lattice = Lattice([ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ]) struct = Structure(lattice, ["Li+", "Li+", "O2-", "O2-"], coords) s = t.apply_transformation(struct) self.assertEqual(s.composition.formula, "Na2 Se1 S1")
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, 3.1, 10.0, 2.96, 2.0, 1.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, 0.15495358379511573) self.assertEqual(list(img), [-11, 6, 0])
def lammps_lattice(structure): """ Imposes transformation for non-orthorobic cell for LAMMPS to read cell_lengths and tilt_factors, creates a new pymatgen structure object with the new transformation and associated forces. Args: structure (obj): A pymatgen structural object created from a POSCAR, with forces from an OUTCAR included as site properties. Returns: cell_lengths (np.array): Lengths of each cell direction. tilt_factors (np.array): Tilt factors of the cell. new_structure (obj): A pymatgen structural object created from the transformed matrix structure, with forces included as site properties. """ a, b, c = structure.lattice.matrix if np.cross(a, b).dot(c) < 0: raise ValueError( 'This is a left-hand coordinate system. Lammps requires a right-hand coordinate system.' ) else: abc = abc_matrix(a, b, c) # abc[0,1] = 0.00 # abc[0,2] = 0.00 # abc[1,2] = 0.00 new_lattice = Lattice(abc) cell_lengths = np.array([abc[0, 0], abc[1, 1], abc[2, 2]]) tilt_factors = np.array([abc[0, 1], abc[0, 2], abc[1, 2]]) new_base = new_basis(abc, structure.lattice) new_coords = apply_new_basis(new_base, structure.cart_coords.T) new_forces = apply_new_basis( new_base, np.array(structure.site_properties['forces']).T) new_structure = Structure(new_lattice, structure.species, new_coords, coords_are_cartesian=True, site_properties={'forces': new_forces}) return cell_lengths, tilt_factors, new_structure
def get_orthogonal_c_slab(self): """ This method returns a Slab where the normal (c lattice vector) is "forced" to be exactly orthogonal to the surface a and b lattice vectors. **Note that this breaks inherent symmetries in the slab.** It should be pointed out that orthogonality is not required to get good surface energies, but it can be useful in cases where the slabs are subsequently used for postprocessing of some kind, e.g. generating GBs or interfaces. """ a, b, c = self.lattice.matrix new_c = np.cross(a, b) new_c /= np.linalg.norm(new_c) new_c = np.dot(c, new_c) * new_c new_latt = Lattice([a, b, new_c]) return Slab(lattice=new_latt, species=self.species, coords=self.cart_coords, miller_index=self.miller_index, oriented_unit_cell=self.oriented_unit_cell, shift=self.shift, scale_factor=self.scale_factor, coords_are_cartesian=True, energy=self.energy)
def from_dict(cls, d): """ Create from dict. Args: A dict with all data for a band structure object. Returns: A BandStructure object """ eigenvals = {} labels_dict = d['labels_dict'] projections = {} structure = None if isinstance(d['bands'].values()[0], dict): eigenvals = { Spin(int(k)): np.array(d['bands'][k]['data']) for k in d['bands'] } else: eigenvals = {Spin(int(k)): d['bands'][k] for k in d['bands']} if 'structure' in d: structure = Structure.from_dict(d['structure']) if d.get('projections'): projections = { Spin(int(spin)): np.array(v) for spin, v in d["projections"].items() } return BandStructure(d['kpoints'], eigenvals, Lattice(d['lattice_rec']['matrix']), d['efermi'], labels_dict, structure=structure, projections=projections)
def from_dict(cls, d): """ Args: d (dict): A dict with all data for a band structure symm line object. Returns: A BandStructureSymmLine object """ try: # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) labels_dict = {k.strip(): v for k, v in d['labels_dict'].items()} projections = {} structure = None if d.get('projections'): structure = Structure.from_dict(d['structure']) projections = { Spin(int(spin)): np.array(v) for spin, v in d["projections"].items() } return BandStructureSymmLine( d['kpoints'], {Spin(int(k)): d['bands'][k] for k in d['bands']}, Lattice(d['lattice_rec']['matrix']), d['efermi'], labels_dict, structure=structure, projections=projections) except: warnings.warn("Trying from_dict failed. Now we are trying the old " "format. Please convert your BS dicts to the new " "format. The old format will be retired in pymatgen " "5.0.") return BandStructureSymmLine.from_old_dict(d)
def from_dict(cls, d): """ Args: A dict with all data for a band structure symm line object. Returns: A BandStructureSymmLine object """ # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) labels_dict = {k.strip(): v for k, v in d['labels_dict'].items()} projections = {} structure = None if 'projections' in d and len(d['projections']) != 0: structure = Structure.from_dict(d['structure']) projections = { Spin.from_int(int(spin)): [[{ Orbital.from_string(orb): [ d['projections'][spin][i][j][orb][k] for k in range(len(d['projections'][spin][i][j][orb])) ] for orb in d['projections'][spin][i][j] } for j in range(len(d['projections'][spin][i]))] for i in range(len(d['projections'][spin]))] for spin in d['projections'] } return BandStructureSymmLine( d['kpoints'], {Spin.from_int(int(k)): d['bands'][k] for k in d['bands']}, Lattice(d['lattice_rec']['matrix']), d['efermi'], labels_dict, structure=structure, projections=projections)
def from_dict(cls, d): """ Args: d: Dict representation Returns: PhononBandStructureSummLine """ lattice_rec = Lattice(d["lattice_rec"]["matrix"]) eigendisplacements = np.array( d["eigendisplacements"]["real"]) + np.array( d["eigendisplacements"]["imag"]) * 1j structure = Structure.from_dict( d["structure"]) if "structure" in d else None return cls( d["qpoints"], np.array(d["bands"]), lattice_rec, d["has_nac"], eigendisplacements, d["labels_dict"], structure=structure, )
def setUp(self): self.lattice = Lattice([0.5, 0.5, 0, 0, 0.5, 0, 0, 0, 0.4])
def get_ieee_rotation(structure, refine_rotation=True): """ Given a structure associated with a tensor, determines the rotation matrix for IEEE conversion according to the 1987 IEEE standards. Args: structure (Structure): a structure associated with the tensor to be converted to the IEEE standard refine_rotation (bool): whether to refine the rotation using SquareTensor.refine_rotation """ # Check conventional setting: sga = SpacegroupAnalyzer(structure) dataset = sga.get_symmetry_dataset() trans_mat = dataset['transformation_matrix'] conv_latt = Lattice( np.transpose( np.dot(np.transpose(structure.lattice.matrix), np.linalg.inv(trans_mat)))) xtal_sys = sga.get_crystal_system() vecs = conv_latt.matrix lengths = np.array(conv_latt.abc) angles = np.array(conv_latt.angles) rotation = np.zeros((3, 3)) # IEEE rules: a,b,c || x1,x2,x3 if xtal_sys == "cubic": rotation = [vecs[i] / lengths[i] for i in range(3)] # IEEE rules: a=b in length; c,a || x3, x1 elif xtal_sys == "tetragonal": rotation = np.array([ vec / mag for (mag, vec) in sorted(zip(lengths, vecs), key=lambda x: x[0]) ]) if abs(lengths[2] - lengths[1]) < abs(lengths[1] - lengths[0]): rotation[0], rotation[2] = rotation[2], rotation[0].copy() rotation[1] = get_uvec(np.cross(rotation[2], rotation[0])) # IEEE rules: c<a<b; c,a || x3,x1 elif xtal_sys == "orthorhombic": rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))] rotation = np.roll(rotation, 2, axis=0) # IEEE rules: c,a || x3,x1, c is threefold axis # Note this also includes rhombohedral crystal systems elif xtal_sys in ("trigonal", "hexagonal"): # find threefold axis: tf_index = np.argmin(abs(angles - 120.)) non_tf_mask = np.logical_not(angles == angles[tf_index]) rotation[2] = get_uvec(vecs[tf_index]) rotation[0] = get_uvec(vecs[non_tf_mask][0]) rotation[1] = get_uvec(np.cross(rotation[2], rotation[0])) # IEEE rules: b,c || x2,x3; alpha=beta=90, c<a elif xtal_sys == "monoclinic": # Find unique axis u_index = np.argmax(abs(angles - 90.)) n_umask = np.logical_not(angles == angles[u_index]) rotation[1] = get_uvec(vecs[u_index]) # Shorter of remaining lattice vectors for c axis c = [ vec / mag for (mag, vec) in sorted(zip(lengths[n_umask], vecs[n_umask])) ][0] rotation[2] = np.array(c) rotation[0] = np.cross(rotation[1], rotation[2]) # IEEE rules: c || x3, x2 normal to ac plane elif xtal_sys == "triclinic": rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))] rotation[1] = get_uvec(np.cross(rotation[2], rotation[0])) rotation[0] = np.cross(rotation[1], rotation[2]) rotation = SquareTensor(rotation) if refine_rotation: rotation = rotation.refine_rotation() return rotation
def structure_from_string(data): """ Parses a rndstr.in, lat.in or bestsqs.out file into pymatgen's Structure format. :param data: contents of a rndstr.in, lat.in or bestsqs.out file :return: Structure object """ data = data.splitlines() data = [x.split() for x in data if x] # remove empty lines # following specification/terminology given in manual if len(data[0]) == 6: # lattice parameters a, b, c, alpha, beta, gamma = map(float, data[0]) coord_system = Lattice.from_parameters(a, b, c, alpha, beta, gamma).matrix lattice_vecs = np.array( [ [data[1][0], data[1][1], data[1][2]], [data[2][0], data[2][1], data[2][2]], [data[3][0], data[3][1], data[3][2]], ], dtype=float, ) first_species_line = 4 else: coord_system = np.array( [ [data[0][0], data[0][1], data[0][2]], [data[1][0], data[1][1], data[1][2]], [data[2][0], data[2][1], data[2][2]], ], dtype=float, ) lattice_vecs = np.array( [ [data[3][0], data[3][1], data[3][2]], [data[4][0], data[4][1], data[4][2]], [data[5][0], data[5][1], data[5][2]], ], dtype=float, ) first_species_line = 6 scaled_matrix = np.matmul(lattice_vecs, coord_system) lattice = Lattice(scaled_matrix) all_coords = [] all_species = [] for l in data[first_species_line:]: coords = np.array([l[0], l[1], l[2]], dtype=float) scaled_coords = np.matmul(coords, np.linalg.inv(lattice_vecs)) all_coords.append(scaled_coords) species_strs = "".join( l[3:]) # join multiple strings back together species_strs = species_strs.replace(" ", "") # trim any white space species_strs = species_strs.split(",") # comma-delimited species = {} for species_occ in species_strs: # gets a species, occupancy pair species_occ = species_occ.split("=") if len(species_occ) == 1: # assume occupancy is 1.0 species_occ = [species_occ[0], 1.0] if "_" in species_occ[0]: # see to_string() method in this file, since , and = are not valid # species names in AT-AT we replace "," with "__" and "=" with "___", # for pymatgen to parse these back correctly we have to replace them back species_occ[0] = (species_occ[0].replace("___", "=").replace( "__", ",")) species[get_el_sp(species_occ[0])] = float(species_occ[1]) all_species.append(species) return Structure(lattice, all_species, all_coords)
def read_data(data=None, ff=None): """ Read LAMMPS data file Args: data: data file path ff: potential.mod/potential information file path Returns: struct: Structure object """ pot_file = open(ff, "r") lines = pot_file.read().splitlines() symb = [] count = 0 from pymatgen.core.periodic_table import Element for i, line in enumerate(lines): if "pair_coeff" in line.split(): sp = line.split() print("spsplit", sp, os.getcwd()) for el in sp: try: if Element(el): # if el=='M': # el='Mo' # count=count+1 # if count >4: symb.append(el) except: pass print("symb=", symb) f = open(data, "r") lines = f.read().splitlines() for i, line in enumerate(lines): if "atoms" in line.split(): natoms = int(line.split()[0]) if "types" in line.split(): print(line) ntypes = int(line.split()[0]) if "xlo" in line.split(): xlo = float(line.split()[0]) xhi = float(line.split()[1]) if "ylo" in line.split(): ylo = float(line.split()[0]) yhi = float(line.split()[1]) if "zlo" in line.split(): zlo = float(line.split()[0]) zhi = float(line.split()[1]) if "xy" in line.split(): xy = float(line.split()[0]) xz = float(line.split()[1]) yz = float(line.split()[2]) if len(symb) != ntypes: print("Something wrong in atom type assignment", len(symb), ntypes) sys.exit() lat = Lattice([[xhi - xlo, 0.0, 0.0], [xy, yhi - ylo, 0.0], [xz, yz, zhi - zlo]]) typ = np.empty((natoms), dtype="S20") x = np.zeros((natoms)) y = np.zeros((natoms)) z = np.zeros((natoms)) q = np.zeros((natoms)) coords = list() for i, line in enumerate(lines): if "Atoms" in line.split(): for j in range(0, natoms): # print int(((lines[j+2]).split()[1]))-1 typ[j] = symb[int(((lines[i + j + 2]).split()[1])) - 1] q[j] = (lines[i + j + 2]).split()[2] x[j] = (lines[i + j + 2]).split()[3] y[j] = (lines[i + j + 2]).split()[4] z[j] = (lines[i + j + 2]).split()[5] coords.append([x[j], y[j], z[j]]) f.close() # print ("info",(typ),'coo',(coords),'latt',lat) pot_file.close() typ_sp = [str(i, "utf-8") for i in typ] struct = Structure(lat, typ_sp, coords, coords_are_cartesian=True) # print struct # finder = SpacegroupAnalyzer(struct) # num=finder.get_spacegroup_symbol() # print(num) return struct
def setUp(self): self.ref_lat = np.array([[1.59205, -2.757511, 0.0], [1.59205, 2.757511, 0.0], [0.0, 0.0, 5.1551]]) self.ref_pmg_lat = Lattice(self.ref_lat)
def test_get_wigner_seitz_cell(self): ws_cell = Lattice([[10, 0, 0], [0, 5, 0], [0, 0, 1]])\ .get_wigner_seitz_cell() self.assertEqual(6, len(ws_cell)) for l in ws_cell[3]: self.assertEqual([abs(i) for i in l], [5.0, 2.5, 0.5])
def setUp(self): self.silicon = Structure( Lattice.from_lengths_and_angles([5.47, 5.47, 5.47], [90.0, 90.0, 90.0]), ["Si", "Si", "Si", "Si", "Si", "Si", "Si", "Si"], [[0.000000, 0.000000, 0.500000], [0.750000, 0.750000, 0.750000], [0.000000, 0.500000, 1.000000], [0.750000, 0.250000, 0.250000], [0.500000, 0.000000, 1.000000], [0.250000, 0.750000, 0.250000], [0.500000, 0.500000, 0.500000], [0.250000, 0.250000, 0.750000]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=False, site_properties=None) self.smi = StructureMotifInterstitial( self.silicon, "Si", motif_types=["tetrahedral", "octahedral"], op_threshs=[0.3, 0.5], dl=0.4, doverlap=1.0, facmaxdl=1.51) self.diamond = Structure( Lattice([[2.189, 0, 1.264], [0.73, 2.064, 1.264], [0, 0, 2.528]]), ["C0+", "C0+"], [[2.554, 1.806, 4.423], [0.365, 0.258, 0.632]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=True, site_properties=None) self.nacl = Structure(Lattice([[3.485, 0, 2.012], [1.162, 3.286, 2.012], [0, 0, 4.025]]), ["Na1+", "Cl1-"], [[0, 0, 0], [2.324, 1.643, 4.025]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=True, site_properties=None) self.cscl = Structure(Lattice([[4.209, 0, 0], [0, 4.209, 0], [0, 0, 4.209]]), ["Cl1-", "Cs1+"], [[2.105, 2.105, 2.105], [0, 0, 0]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=True, site_properties=None) self.square_pyramid = Structure(Lattice([[100, 0, 0], [0, 100, 0], [0, 0, 100]]), ["C", "C", "C", "C", "C", "C"], [[0, 0, 0], [1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=True, site_properties=None) self.trigonal_bipyramid = Structure( Lattice([[100, 0, 0], [0, 100, 0], [0, 0, 100]]), ["P", "Cl", "Cl", "Cl", "Cl", "Cl"], [[0, 0, 0], [0, 0, 2.14], [0, 2.02, 0], [1.74937, -1.01, 0], [-1.74937, -1.01, 0], [0, 0, -2.14]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=True, site_properties=None)
def from_string(data): """ Reads the exciting input from a string """ root = ET.fromstring(data) speciesnode = root.find('structure').iter('species') elements = [] positions = [] vectors = [] lockxyz = [] # get title title_in = str(root.find('title').text) # Read elements and coordinates for nodes in speciesnode: symbol = nodes.get('speciesfile').split('.')[0] if len(symbol.split('_')) == 2: symbol = symbol.split('_')[0] if Element.is_valid_symbol(symbol): # Try to recognize the element symbol element = symbol else: raise ValueError("Unknown element!") for atom in nodes.iter('atom'): x, y, z = atom.get('coord').split() positions.append([float(x), float(y), float(z)]) elements.append(element) # Obtain lockxyz for each atom if atom.get('lockxyz') is not None: lxyz = [] for l in atom.get('lockxyz').split(): if l == 'True' or l == 'true': lxyz.append(True) else: lxyz.append(False) lockxyz.append(lxyz) else: lockxyz.append([False, False, False]) # check the atomic positions type if 'cartesian' in root.find('structure').attrib.keys(): if root.find('structure').attrib['cartesian']: cartesian = True for i in range(len(positions)): for j in range(3): positions[i][ j] = positions[i][j] * ExcitingInput.bohr2ang print(positions) else: cartesian = False # get the scale attribute scale_in = root.find('structure').find('crystal').get('scale') if scale_in: scale = float(scale_in) * ExcitingInput.bohr2ang else: scale = ExcitingInput.bohr2ang # get the stretch attribute stretch_in = root.find('structure').find('crystal').get('stretch') if stretch_in: stretch = np.array([float(a) for a in stretch_in]) else: stretch = np.array([1.0, 1.0, 1.0]) # get basis vectors and scale them accordingly basisnode = root.find('structure').find('crystal').iter('basevect') for vect in basisnode: x, y, z = vect.text.split() vectors.append([ float(x) * stretch[0] * scale, float(y) * stretch[1] * scale, float(z) * stretch[2] * scale ]) # create lattice and structure object lattice_in = Lattice(vectors) structure_in = Structure(lattice_in, elements, positions, coords_are_cartesian=cartesian) return ExcitingInput(structure_in, title_in, lockxyz)