def test_get_slab(self): s = self.get_structure("LiFePO4") gen = SlabGenerator(s, [0, 0, 1], 10, 10) s = gen.get_slab(0.25) self.assertAlmostEqual(s.lattice.abc[2], 20.820740000000001) fcc = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Fe"], [[0, 0, 0]]) gen = SlabGenerator(fcc, [1, 1, 1], 10, 10) slab = gen.get_slab() gen = SlabGenerator(fcc, [1, 1, 1], 10, 10, primitive=False) slab_non_prim = gen.get_slab() self.assertEqual(len(slab), 6) self.assertEqual(len(slab_non_prim), len(slab) * 4) #Some randomized testing of cell vectors for i in range(1, 231): i = random.randint(1, 230) sg = SpaceGroup.from_int_number(i) if sg.crystal_system == "hexagonal" or (sg.crystal_system == \ "trigonal" and sg.symbol.endswith("H")): latt = Lattice.hexagonal(5, 10) else: #Cubic lattice is compatible with all other space groups. latt = Lattice.cubic(5) s = Structure.from_spacegroup(i, latt, ["H"], [[0, 0, 0]]) miller = (0, 0, 0) while miller == (0, 0, 0): miller = (random.randint(0, 6), random.randint(0, 6), random.randint(0, 6)) gen = SlabGenerator(s, miller, 10, 10) a, b, c = gen.oriented_unit_cell.lattice.matrix self.assertAlmostEqual(np.dot(a, gen._normal), 0) self.assertAlmostEqual(np.dot(b, gen._normal), 0)
def to(self, fmt, filename, weight=True, lattice=None): composition = self.to_composition(weight) charge = sum([k.oxi_state * v for k, v in composition.iteritems()]) s = Structure(lattice, [self.central_subsite.specie] + [site.specie for site in self.peripheral_subsites], [self.central_subsite.site.coords] + [site.site.coords for site in self.peripheral_subsites], coords_are_cartesian=True) s.to(fmt, filename)
def test_to_from_file_string(self): for fmt in ["cif", "json", "poscar", "cssr"]: s = self.struct.to(fmt=fmt) self.assertIsNotNone(s) ss = IStructure.from_str(s, fmt=fmt) self.assertArrayAlmostEqual( ss.lattice.lengths_and_angles, self.struct.lattice.lengths_and_angles, decimal=5) self.assertArrayAlmostEqual(ss.frac_coords, self.struct.frac_coords) self.assertIsInstance(ss, IStructure) self.struct.to(filename="POSCAR.testing") self.assertTrue(os.path.exists("POSCAR.testing")) os.remove("POSCAR.testing") self.struct.to(filename="Si_testing.yaml") self.assertTrue(os.path.exists("Si_testing.yaml")) s = Structure.from_file("Si_testing.yaml") self.assertEqual(s, self.struct) os.remove("Si_testing.yaml") self.struct.to(filename="POSCAR.testing.gz") s = Structure.from_file("POSCAR.testing.gz") self.assertEqual(s, self.struct) os.remove("POSCAR.testing.gz")
def test_get_slabs(self): gen = SlabGenerator(self.get_structure("CsCl"), [0, 0, 1], 10, 10) #Test orthogonality of some internal variables. a, b, c = gen.oriented_unit_cell.lattice.matrix self.assertAlmostEqual(np.dot(a, gen._normal), 0) self.assertAlmostEqual(np.dot(b, gen._normal), 0) self.assertEqual(len(gen.get_slabs()), 1) s = self.get_structure("LiFePO4") gen = SlabGenerator(s, [0, 0, 1], 10, 10) self.assertEqual(len(gen.get_slabs()), 5) self.assertEqual(len(gen.get_slabs(bonds={("P", "O"): 3})), 2) # There are no slabs in LFP that does not break either P-O or Fe-O # bonds for a miller index of [0, 0, 1]. self.assertEqual(len(gen.get_slabs( bonds={("P", "O"): 3, ("Fe", "O"): 3})), 0) #If we allow some broken bonds, there are a few slabs. self.assertEqual(len(gen.get_slabs( bonds={("P", "O"): 3, ("Fe", "O"): 3}, max_broken_bonds=2)), 2) # At this threshold, only the origin and center Li results in # clustering. All other sites are non-clustered. So the of # slabs is of sites in LiFePO4 unit cell - 2 + 1. self.assertEqual(len(gen.get_slabs(tol=1e-4)), 15) LiCoO2=Structure.from_file(get_path("icsd_LiCoO2.cif"), primitive=False) gen = SlabGenerator(LiCoO2, [0, 0, 1], 10, 10) lco = gen.get_slabs(bonds={("Co", "O"): 3}) self.assertEqual(len(lco), 1) a, b, c = gen.oriented_unit_cell.lattice.matrix self.assertAlmostEqual(np.dot(a, gen._normal), 0) self.assertAlmostEqual(np.dot(b, gen._normal), 0) scc = Structure.from_spacegroup("Pm-3m", Lattice.cubic(3), ["Fe"], [[0, 0, 0]]) gen = SlabGenerator(scc, [0, 0, 1], 10, 10) slabs = gen.get_slabs() self.assertEqual(len(slabs), 1) gen = SlabGenerator(scc, [1, 1, 1], 10, 10, max_normal_search=1) slabs = gen.get_slabs() self.assertEqual(len(slabs), 1) # Test whether using units of hkl planes instead of Angstroms for # min_slab_size and min_vac_size will give us the same number of atoms natoms = [] for a in [1, 1.4, 2.5, 3.6]: s = Structure.from_spacegroup("Im-3m", Lattice.cubic(a), ["Fe"], [[0,0,0]]) slabgen = SlabGenerator(s, (1,1,1), 10, 10, in_unit_planes=True, max_normal_search=2) natoms.append(len(slabgen.get_slab())) n = natoms[0] for i in natoms: self.assertEqual(n, i)
def test_from_magnetic_spacegroup(self): # AFM MnF s1 = Structure.from_magnetic_spacegroup("P4_2'/mnm'", Lattice.tetragonal(4.87, 3.30), ["Mn", "F"], [[0, 0, 0], [0.30, 0.30, 0.00]], {'magmom': [4, 0]}) self.assertEqual(s1.formula, "Mn2 F4") self.assertEqual(sum(map(float, s1.site_properties['magmom'])), 0) self.assertEqual(max(map(float, s1.site_properties['magmom'])), 4) self.assertEqual(min(map(float, s1.site_properties['magmom'])), -4) # AFM LaMnO3, ordered on (001) planes s2 = Structure.from_magnetic_spacegroup("Pn'ma'", Lattice.orthorhombic(5.75, 7.66, 5.53), ["La", "Mn", "O", "O"], [[0.05, 0.25, 0.99], [0.00, 0.00, 0.50], [0.48, 0.25, 0.08], [0.31, 0.04, 0.72]], {'magmom': [0, Magmom([4, 0, 0]), 0, 0]}) self.assertEqual(s2.formula, "La4 Mn4 O12") self.assertEqual(sum(map(float, s2.site_properties['magmom'])), 0) self.assertEqual(max(map(float, s2.site_properties['magmom'])), 4) self.assertEqual(min(map(float, s2.site_properties['magmom'])), -4)
def test_from_dict(self): d = self.propertied_structure.to_dict s = Structure.from_dict(d) self.assertEqual(s[0].magmom, 5) d = {'lattice': {'a': 3.8401979337, 'volume': 40.044794644251596, 'c': 3.8401979337177736, 'b': 3.840198994344244, 'matrix': [[3.8401979337, 0.0, 0.0], [1.9200989668, 3.3257101909, 0.0], [0.0, -2.2171384943, 3.1355090603]], 'alpha': 119.9999908639842, 'beta': 90.0, 'gamma': 60.000009137322195}, 'sites': [{'properties': {'magmom': 5}, 'abc': [0.0, 0.0, 0.0], 'occu': 1.0, 'species': [{'occu': 1.0, 'oxidation_state':-2, 'properties': {'spin': 3}, 'element': 'O'}], 'label': 'O2-', 'xyz': [0.0, 0.0, 0.0]}, {'properties': {'magmom':-5}, 'abc': [0.75, 0.5, 0.75], 'occu': 0.8, 'species': [{'occu': 0.8, 'oxidation_state': 2, 'properties': {'spin': 2}, 'element': 'Mg'}], 'label': 'Mg2+:0.800', 'xyz': [3.8401979336749994, 1.2247250003039056e-06, 2.351631795225]}]} s = Structure.from_dict(d) self.assertEqual(s[0].magmom, 5) self.assertEqual(s[0].specie.spin, 3)
def get_aligned_lattices(slab_sub, slab_2d, max_area=200, max_mismatch=0.05, max_angle_diff=1, r1r2_tol=0.2): """ given the 2 slab structures and the alignment paramters, return slab structures with lattices that are aligned with respect to each other """ # get the matching substrate and 2D material lattices uv_substrate, uv_mat2d = get_matching_lattices(slab_sub, slab_2d, max_area=max_area, max_mismatch=max_mismatch, max_angle_diff=max_angle_diff, r1r2_tol=r1r2_tol) if not uv_substrate and not uv_mat2d: print("no matching u and v, trying adjusting the parameters") sys.exit() substrate = Structure.from_sites(slab_sub) mat2d = Structure.from_sites(slab_2d) # map the intial slabs to the newly found matching lattices substrate_latt = Lattice(np.array( [ uv_substrate[0][:], uv_substrate[1][:], substrate.lattice.matrix[2, :] ])) # to avoid numerical issues with find_mapping mat2d_fake_c = mat2d.lattice.matrix[2, :] / np.linalg.norm(mat2d.lattice.matrix[2, :]) * 5.0 mat2d_latt = Lattice(np.array( [ uv_mat2d[0][:], uv_mat2d[1][:], mat2d_fake_c ])) mat2d_latt_fake = Lattice(np.array( [ mat2d.lattice.matrix[0, :], mat2d.lattice.matrix[1, :], mat2d_fake_c ])) _, __, scell = substrate.lattice.find_mapping(substrate_latt, ltol=0.05, atol=1) scell[2] = np.array([0, 0, 1]) substrate.make_supercell(scell) _, __, scell = mat2d_latt_fake.find_mapping(mat2d_latt, ltol=0.05, atol=1) scell[2] = np.array([0, 0, 1]) mat2d.make_supercell(scell) # modify the substrate lattice so that the 2d material can be # grafted on top of it lmap = Lattice(np.array( [ substrate.lattice.matrix[0, :], substrate.lattice.matrix[1, :], mat2d.lattice.matrix[2, :] ])) mat2d.modify_lattice(lmap) return substrate, mat2d
def generate_defect_structure(self, supercell=(1, 1, 1)): """ Returns Defective Interstitial structure, decorated with charge If bulk structure had any site properties, all of these properties are removed in the resulting defect structure Args: supercell (int, [3x1], or [[]] (3x3)): supercell integer, vector, or scaling matrix """ defect_structure = Structure( self.bulk_structure.copy().lattice, [site.specie for site in self.bulk_structure], [site.frac_coords for site in self.bulk_structure], to_unit_cell=True, coords_are_cartesian = False, site_properties = None) #remove all site_properties defect_structure.make_supercell(supercell) #create a trivial defect structure to find where supercell transformation moves the defect site struct_for_defect_site = Structure( self.bulk_structure.copy().lattice, [self.site.specie], [self.site.frac_coords], to_unit_cell=True, coords_are_cartesian = False) struct_for_defect_site.make_supercell(supercell) defect_site = struct_for_defect_site[0] defect_structure.append(self.site.specie.symbol, defect_site.coords, coords_are_cartesian=True, properties = None) defect_structure.set_charge(self.charge) return defect_structure
def test_merge_sites(self): species = [{'Ag': 0.5}, {'Cl': 0.25}, {'Cl': 0.1}, {'Ag': 0.5}, {'F': 0.15}, {'F': 0.1}] coords = [[0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0, 0, 0], [0.5, 0.5, 1.501], [0.5, 0.5, 1.501]] s = Structure(Lattice.cubic(1), species, coords) s.merge_sites(mode="s") self.assertEqual(s[0].specie.symbol, 'Ag') self.assertEqual(s[1].species_and_occu, Composition({'Cl': 0.35, 'F': 0.25})) self.assertArrayAlmostEqual(s[1].frac_coords, [.5, .5, .5005]) # Test for TaS2 with spacegroup 166 in 160 setting. l = Lattice.from_lengths_and_angles([3.374351, 3.374351, 20.308941], [90.000000, 90.000000, 120.000000]) species = ["Ta", "S", "S"] coords = [[0.000000, 0.000000, 0.944333], [0.333333, 0.666667, 0.353424], [0.666667, 0.333333, 0.535243]] tas2 = Structure.from_spacegroup(160, l, species, coords) assert len(tas2) == 13 tas2.merge_sites(mode="d") assert len(tas2) == 9 l = Lattice.from_lengths_and_angles([3.587776, 3.587776, 19.622793], [90.000000, 90.000000, 120.000000]) species = ["Na", "V", "S", "S"] coords = [[0.333333, 0.666667, 0.165000], [0.000000, 0.000000, 0.998333], [0.333333, 0.666667, 0.399394], [0.666667, 0.333333, 0.597273]] navs2 = Structure.from_spacegroup(160, l, species, coords) assert len(navs2) == 18 navs2.merge_sites(mode="d") assert len(navs2) == 12
def test_get_primitive_structure(self): coords = [[0, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]] fcc_ag = Structure(Lattice.cubic(4.09), ["Ag"] * 4, coords) self.assertEqual(len(fcc_ag.get_primitive_structure()), 1) coords = [[0, 0, 0], [0.5, 0.5, 0.5]] bcc_li = Structure(Lattice.cubic(4.09), ["Li"] * 2, coords) self.assertEqual(len(bcc_li.get_primitive_structure()), 1)
def test_primitive_on_large_supercell(self): coords = [[0, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]] fcc_ag = Structure(Lattice.cubic(4.09), ["Ag"] * 4, coords) fcc_ag.make_supercell([2, 2, 2]) fcc_ag_prim = fcc_ag.get_primitive_structure() self.assertEqual(len(fcc_ag_prim), 1) self.assertAlmostEqual(fcc_ag_prim.volume, 17.10448225)
def get_refined_structure(self): """ Get the refined structure based on detected symmetry. The refined structure is a *conventional* cell setting with atoms moved to the expected symmetry positions. Returns: Refined structure. """ # Atomic positions have to be specified by scaled positions for spglib. num_atom = self._structure.num_sites lattice = self._transposed_latt.copy() pos = np.zeros((num_atom * 4, 3), dtype='double') pos[:num_atom] = self._positions.copy() zs = np.zeros(num_atom * 4, dtype='intc') zs[:num_atom] = np.array(self._numbers, dtype='intc') num_atom_bravais = spg.refine_cell( lattice, pos, zs, num_atom, self._symprec, self._angle_tol) zs = zs[:num_atom_bravais] species = [self._unique_species[i - 1] for i in zs] s = Structure(lattice.T.copy(), species, pos[:num_atom_bravais]) return s.get_sorted_structure()
def _preprocess(self, struct1, struct2, niggli=True): """ Rescales, finds the reduced structures (primitive and niggli), and finds fu, the supercell size to make struct1 comparable to s2 """ struct1 = Structure.from_sites(struct1) struct2 = Structure.from_sites(struct2) if niggli: struct1 = struct1.get_reduced_structure(reduction_algo="niggli") struct2 = struct2.get_reduced_structure(reduction_algo="niggli") #primitive cell transformation if self._primitive_cell: struct1 = struct1.get_primitive_structure() struct2 = struct2.get_primitive_structure() if self._supercell: fu, s1_supercell = self._get_supercell_size(struct1, struct2) else: fu, s1_supercell = 1, True mult = fu if s1_supercell else 1/fu #rescale lattice to same volume if self._scale: ratio = (struct2.volume / (struct1.volume * mult)) ** (1 / 6) nl1 = Lattice(struct1.lattice.matrix * ratio) struct1.modify_lattice(nl1) nl2 = Lattice(struct2.lattice.matrix / ratio) struct2.modify_lattice(nl2) return struct1, struct2, fu, s1_supercell
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 = [[.345, 5, .77298], [.1345, 5.1, .77298], [.7, .8, .9]] for i in range(230): sg_num = i + 1 if sg_num in triclinic: lattice = Lattice([[3.0233057319441246,0,0], [0,7.9850357844548681,0], [0,0,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) kpath = HighSymmKpath(struct) #Throws error if something doesn't work, causing test to fail. struct_file_path = os.path.join(test_dir_structs, 'ICSD_170.cif') struct = Structure.from_file(struct_file_path) hkp = HighSymmKpath(struct) self.assertEqual(hkp.name, 'MCLC5')
def interpolate_poscar(self, fw_spec): # make folder for poscar interpolation start and end structure files. interpolate_folder = 'interpolate' if not os.path.exists(os.path.join(os.getcwd(), interpolate_folder)): os.makedirs(os.path.join(os.getcwd(), interpolate_folder)) # use method of GrabFilesFromCalcLoc to grab files from previous locations. CopyFilesFromCalcLoc(calc_dir=None, calc_loc=self["start"], filenames=["CONTCAR"], name_prepend=interpolate_folder + os.sep, name_append="_0").run_task(fw_spec=fw_spec) CopyFilesFromCalcLoc(calc_dir=None, calc_loc=self["end"], filenames=["CONTCAR"], name_prepend=interpolate_folder + os.sep, name_append="_1").run_task(fw_spec=fw_spec) # assuming first calc_dir is polar structure for ferroelectric search s1 = Structure.from_file(os.path.join(interpolate_folder, "CONTCAR_0")) s2 = Structure.from_file(os.path.join(interpolate_folder, "CONTCAR_1")) structs = s1.interpolate(s2, self["nimages"], interpolate_lattices=True, autosort_tol=self.get("autosort_tol", 0.0)) # save only the interpolation needed for this run i = self.get("this_image") return structs[i]
def relax(dim=2, submit=True, force_overwrite=False): """ Writes input files and (optionally) submits a self-consistent relaxation. Should be run before pretty much anything else, in order to get the right energy and structure of the material. Args: dim (int): 2 for relaxing a 2D material, 3 for a 3D material. submit (bool): Whether or not to submit the job. force_overwrite (bool): Whether or not to overwrite files if an already converged vasprun.xml exists in the directory. """ if force_overwrite or not utl.is_converged(os.getcwd()): directory = os.getcwd().split('/')[-1] # vdw_kernel.bindat file required for VDW calculations. if VDW_KERNEL: os.system('cp {} .'.format(VDW_KERNEL)) # KPOINTS Kpoints.automatic_density(Structure.from_file('POSCAR'), 1000).write_file('KPOINTS') # INCAR INCAR_DICT.update( {'MAGMOM': utl.get_magmom_string(Structure.from_file('POSCAR'))} ) Incar.from_dict(INCAR_DICT).write_file('INCAR') # POTCAR utl.write_potcar() # Special tasks only performed for 2D materials. if dim == 2: # Ensure 20A interlayer vacuum utl.ensure_vacuum(Structure.from_file('POSCAR'), 20) # Remove all z k-points. kpts_lines = open('KPOINTS').readlines() with open('KPOINTS', 'w') as kpts: for line in kpts_lines[:3]: kpts.write(line) kpts.write(kpts_lines[3].split()[0] + ' ' + kpts_lines[3].split()[1] + ' 1') # Submission script if dim == 2: binary = VASP_TWOD_BIN elif dim == 3: binary = VASP_STD_BIN if QUEUE_SYSTEM == 'pbs': utl.write_pbs_runjob(directory, 1, 16, '800mb', '6:00:00', binary) submission_command = 'qsub runjob' elif QUEUE_SYSTEM == 'slurm': utl.write_slurm_runjob(directory, 16, '800mb', '6:00:00', binary) submission_command = 'sbatch runjob' if submit: os.system(submission_command)
def setUp(self): """Loading structures before tests""" #print "TestConnectivityMethods:setUp_" print "Loading structures from file" self.fe_structure = Structure.from_file('Fe.cif', True, True) self.caf2_structure = Structure.from_file('CaF2.cif', True, True) self.licoo2_structure = Structure.from_file('LiCoO2.cif', True, True) print "Structures from file loaded"
def test_disordered_supercell_primitive_cell(self): l = Lattice.cubic(2) f = [[0.5, 0.5, 0.5]] sp = [{'Si': 0.54738}] s = Structure(l, sp, f) #this supercell often breaks things s.make_supercell([[0,-1,1],[-1,1,0],[1,1,1]]) self.assertEqual(len(s.get_primitive_structure()), 1)
def test_get_sorted_structure(self): coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) s = Structure(self.lattice, ["O", "Li"] , coords) sorted_s = s.get_sorted_structure() self.assertEqual(sorted_s[0].species_and_occu, {Element("Li"):1}) self.assertEqual(sorted_s[1].species_and_occu, {Element("O"):1})
def test_primitive_structure_volume_check(self): l = Lattice.tetragonal(10, 30) coords = [[0.5, 0.8, 0], [0.5, 0.2, 0], [0.5, 0.8, 0.333], [0.5, 0.5, 0.333], [0.5, 0.5, 0.666], [0.5, 0.2, 0.666]] s = Structure(l, ["Ag"] * 6, coords) sprim = s.get_primitive_structure(tolerance=0.1) self.assertEqual(len(sprim), 6)
def test_primitive_cell_site_merging(self): l = Lattice.cubic(10) coords = [[0, 0, 0], [0, 0, 0.5], [0, 0, 0.26], [0, 0, 0.74]] sp = ['Ag', 'Ag', 'Be', 'Be'] s = Structure(l, sp, coords) dm = s.get_primitive_structure().distance_matrix self.assertArrayAlmostEqual(dm, [[0, 2.5], [2.5, 0]])
def setUp(self): l = Lattice.cubic(3.51) species = ["Ni"] coords = [[0,0,0]] self.Ni = Structure.from_spacegroup("Fm-3m", l, species, coords) self.Si = Structure.from_spacegroup("Fd-3m", Lattice.cubic(5.430500), ["Si"], [(0, 0, 0.5)])
def get_basic_analysis_and_error_checks(d, max_force_threshold=0.5, volume_change_threshold=0.2): initial_vol = d["input"]["crystal"]["lattice"]["volume"] final_vol = d["output"]["crystal"]["lattice"]["volume"] delta_vol = final_vol - initial_vol percent_delta_vol = delta_vol / initial_vol coord_num = get_coordination_numbers(d) calc = d["calculations"][-1] gap = calc["output"]["bandgap"] cbm = calc["output"]["cbm"] vbm = calc["output"]["vbm"] is_direct = calc["output"]["is_gap_direct"] warning_msgs = [] error_msgs = [] if abs(percent_delta_vol) > volume_change_threshold: warning_msgs.append("Volume change > {}%" .format(volume_change_threshold * 100)) bv_struct = Structure.from_dict(d["output"]["crystal"]) try: bva = BVAnalyzer() bv_struct = bva.get_oxi_state_decorated_structure(bv_struct) except ValueError as e: logger.error("Valence cannot be determined due to {e}." .format(e=e)) except Exception as ex: logger.error("BVAnalyzer error {e}.".format(e=str(ex))) max_force = None if d["state"] == "successful" and \ d["calculations"][0]["input"]["parameters"].get("NSW", 0) > 0: # handle the max force and max force error max_force = max([np.linalg.norm(a) for a in d["calculations"][-1]["output"] ["ionic_steps"][-1]["forces"]]) if max_force > max_force_threshold: error_msgs.append("Final max force exceeds {} eV" .format(max_force_threshold)) d["state"] = "error" s = Structure.from_dict(d["output"]["crystal"]) if not s.is_valid(): error_msgs.append("Bad structure (atoms are too close!)") d["state"] = "error" return {"delta_volume": delta_vol, "max_force": max_force, "percent_delta_volume": percent_delta_vol, "warnings": warning_msgs, "errors": error_msgs, "coordination_numbers": coord_num, "bandgap": gap, "cbm": cbm, "vbm": vbm, "is_gap_direct": is_direct, "bv_structure": bv_struct.as_dict()}
def _calc_recip(self): """ Perform the reciprocal space summation. Calculates the quantity E_recip = 1/(2PiV) sum_{G < Gmax} exp(-(G.G/4/eta))/(G.G) S(G)S(-G) where S(G) = sum_{k=1,N} q_k exp(-i G.r_k) S(G)S(-G) = |S(G)|**2 This method is heavily vectorized to utilize numpy's C backend for speed. """ numsites = self._s.num_sites prefactor = 2 * pi / self._vol erecip = np.zeros((numsites, numsites)) forces = np.zeros((numsites, 3)) coords = self._coords recip = Structure(self._s.lattice.reciprocal_lattice, ["H"], [np.array([0, 0, 0])]) recip_nn = recip.get_neighbors(recip[0], self._gmax) for (n, dist) in recip_nn: gvect = n.coords gsquare = np.linalg.norm(gvect) ** 2 expval = exp(-1.0 * gsquare / (4.0 * self._eta)) gvect_tile = np.tile(gvect, (numsites, 1)) gvectdot = np.sum(gvect_tile * coords, 1) #calculate the structure factor sfactor = np.zeros((numsites, numsites)) sreal = 0.0 simag = 0.0 for i in xrange(numsites): qi = self._oxi_states[i] g_dot_i = gvectdot[i] sfactor[i, i] = qi * qi sreal += qi * cos(g_dot_i) simag += qi * sin(g_dot_i) for j in xrange(i + 1, numsites): qj = self._oxi_states[j] exparg = g_dot_i - gvectdot[j] cosa = cos(exparg) sina = sin(exparg) sfactor[i, j] = qi * qj * (cosa + sina) """ Uses the property that when sitei and sitej are switched, exparg' == - exparg. This implies cos (exparg') = cos (exparg) and sin (exparg') = - sin (exparg) Halves all computations. """ sfactor[j, i] = qi * qj * (cosa - sina) erecip += expval / gsquare * sfactor pref = 2 * expval / gsquare * np.array(self._oxi_states) factor = prefactor * pref * (sreal * np.sin(gvectdot) - simag * np.cos(gvectdot)) * EwaldSummation.CONV_FACT forces += np.tile(factor, (3, 1)).transpose() * gvect_tile return (erecip * prefactor * EwaldSummation.CONV_FACT , forces)
def cluster_Si4(): a = 5.43 c = a/2 fcc = Lattice([[a/2,a/2,0],[a/2,0,c/2],[0,a/2,c/2]]) cluster = Structure(fcc,['Si'],[[0.25,0.25,0.25]]) cluster.make_supercell([[1,1,-1],[1,-1,1],[-1,1,1]]) return cluster
def from_string(header_str): """ Reads Header string and returns Header object if header was generated by pymatgen. Args: header_str: pymatgen generated feff.inp header Returns: structure object. """ lines = tuple(clean_lines(header_str.split("\n"), False)) comment = lines[0] feffpmg = comment.find("pymatgen") if feffpmg > 0: source = lines[1].split()[2] natoms = int(lines[7].split()[2]) basis_vec = lines[5].split() a = float(basis_vec[2]) b = float(basis_vec[3]) c = float(basis_vec[4]) lengths = [a, b, c] basis_ang = lines[6].split() alpha = float(basis_ang[2]) beta = float(basis_ang[3]) gamma = float(basis_ang[4]) angles = [alpha, beta, gamma] lattice = Lattice.from_lengths_and_angles(lengths, angles) atomic_symbols = [] for i in xrange(8, 8 + natoms): atomic_symbols.append(lines[i].split()[2]) # read the atomic coordinates coords = [] for i in xrange(natoms): toks = lines[i + 8].split() coords.append([float(s) for s in toks[3:]]) struct_fromfile = Structure(lattice, atomic_symbols, coords, False, False, False) struct_fromfile.compound = lines[3].split()[2] h = Header(struct_fromfile, comment) h.set_source(source) return h else: return "Header not generated by pymatgen, " \ "cannot return header object"
def test_merge_sites(self): species = [{'Ag': 0.5}, {'Cl': 0.35}, {'Ag': 0.5}, {'F': 0.25}] coords = [[0, 0, 0], [0.5, 0.5, 0.5], [0, 0, 0], [0.5, 0.5, 1.501]] s = Structure(Lattice.cubic(1), species, coords) s.merge_sites() self.assertEqual(s[0].specie.symbol, 'Ag') self.assertEqual(s[1].species_and_occu, Composition({'Cl': 0.35, 'F': 0.25})) self.assertArrayAlmostEqual(s[1].frac_coords, [.5, .5, .5005])
def test_getinterpolatedposcar(self): nimages = 5 this_image = 1 autosort_tol = 0.5 fw1 = Firework([CopyVaspOutputs(calc_dir=self.static_outdir, contcar_to_poscar=False, additional_files=["CONTCAR"]), PassCalcLocs(name="fw1")], name="fw1") fw2 = Firework([CopyVaspOutputs(calc_dir=self.opt_outdir, contcar_to_poscar=False, additional_files=["CONTCAR"]), PassCalcLocs(name="fw2")], name="fw2") fw3 = Firework([GetInterpolatedPOSCAR(start="fw1", end="fw2", this_image=this_image, nimages=nimages, autosort_tol=autosort_tol), PassCalcLocs(name="fw3")], name="fw3", parents=[fw1, fw2]) fw4 = Firework([PassCalcLocs(name="fw4")], name="fw4", parents=fw3) wf = Workflow([fw1, fw2, fw3, fw4]) self.lp.add_wf(wf) rapidfire(self.lp) fw4 = self.lp.get_fw_by_id(self.lp.get_fw_ids({"name": "fw4"})[0]) calc_locs = fw4.spec["calc_locs"] self.assertTrue(os.path.exists(get_calc_loc("fw3", calc_locs)["path"] + "/POSCAR")) self.assertTrue(os.path.exists(get_calc_loc("fw3", calc_locs)["path"] + "/interpolate/CONTCAR_0")) self.assertTrue(os.path.exists(get_calc_loc("fw3", calc_locs)["path"] + "/interpolate/CONTCAR_1")) struct_start = Structure.from_file(get_calc_loc("fw3", calc_locs)["path"] + "/interpolate/CONTCAR_0") struct_end = Structure.from_file(get_calc_loc("fw3", calc_locs)["path"] + "/interpolate/CONTCAR_1") struct_inter = Structure.from_file(get_calc_loc("fw3", calc_locs)["path"] + "/POSCAR") structs = struct_start.interpolate(struct_end, nimages, interpolate_lattices=True, autosort_tol=autosort_tol) # Check x of 1st site. self.assertAlmostEqual(structs[this_image][1].coords[0], struct_inter[1].coords[0]) # Check c lattice parameter self.assertAlmostEqual(structs[this_image].lattice.abc[0], struct_inter.lattice.abc[0])
def test_get_all_neighbors_outside_cell(self): s = Structure(Lattice.cubic(2), ['Li', 'Li', 'Li', 'Si'], [[3.1] * 3, [0.11] * 3, [-1.91] * 3, [0.5] * 3]) all_nn = s.get_all_neighbors(0.2, True) for site, nns in zip(s, all_nn): for nn in nns: self.assertTrue(nn[0].is_periodic_image(s[nn[2]])) d = sum((site.coords - nn[0].coords) ** 2) ** 0.5 self.assertAlmostEqual(d, nn[1]) self.assertEqual(list(map(len, all_nn)), [2, 2, 2, 0])
def setUp(self): self.cscl = self.get_structure("CsCl") self.lifepo4 = self.get_structure("LiFePO4") self.tei = Structure.from_file(get_path("icsd_TeI.cif"), primitive=False) self.LiCoO2 = Structure.from_file(get_path("icsd_LiCoO2.cif"), primitive=False) self.p1 = Structure(Lattice.from_parameters(3, 4, 5, 31, 43, 50), ["H", "He"], [[0, 0, 0], [0.1, 0.2, 0.3]])
def setUp(self): feo4 = Structure.from_file(os.path.join(test_dir, "LiFePO4.cif")) feo4.remove_species(["Li"]) feo4.remove_oxidation_states() self.feo4 = feo4
def get_structure_components(bonded_structure, inc_orientation=False, inc_site_ids=False, inc_molecule_graph=False): """ Gets information on the components in a bonded structure. Correctly determines the dimensionality of all structures, regardless of structure type or improper connections due to periodic boundary conditions. Requires a StructureGraph object as input. This can be generated using one of the NearNeighbor classes. For example, using the CrystalNN class:: bonded_structure = CrystalNN().get_bonded_structure(structure) Based on the modified breadth-first-search algorithm described in: P. Larsem, M. Pandey, M. Strange, K. W. Jacobsen, 2018, arXiv:1808.02114 Args: bonded_structure (StructureGraph): A structure with bonds, represented as a pymatgen structure graph. For example, generated using the CrystalNN.get_bonded_structure() method. inc_orientation (bool, optional): Whether to include the orientation of the structure component. For surfaces, the miller index is given, for one-dimensional structures, the direction of the chain is given. inc_site_ids (bool, optional): Whether to include the site indices of the sites in the structure component. inc_molecule_graph (bool, optional): Whether to include MoleculeGraph objects for zero-dimensional components. Returns: (list of dict): Information on the components in a structure as a list of dictionaries with the keys: - "structure_graph": A pymatgen StructureGraph object for the component. - "dimensionality": The dimensionality of the structure component as an int. - "orientation": If inc_orientation is `True`, the orientation of the component as a tuple. E.g. (1, 1, 1) - "site_ids": If inc_site_ids is `True`, the site indices of the sites in the component as a tuple. - "molecule_graph": If inc_molecule_graph is `True`, the site a MoleculeGraph object for zero-dimensional components. """ import networkx as nx # optional dependency therefore not top level import comp_graphs = ( bonded_structure.graph.subgraph(c) for c in nx.weakly_connected_components(bonded_structure.graph)) components = [] for graph in comp_graphs: dimensionality, vertices = calculate_dimensionality_of_site( bonded_structure, list(graph.nodes())[0], inc_vertices=True) component = {'dimensionality': dimensionality} if inc_orientation: if dimensionality in [1, 2]: vertices = np.array(vertices) g = vertices.sum(axis=0) / vertices.shape[0] # run singular value decomposition _, _, vh = np.linalg.svd(vertices - g) # get direction (first column is best fit line, # 3rd column is unitary norm) index = 2 if dimensionality == 2 else 0 orientation = get_integer_index(vh[index, :]) else: orientation = None component['orientation'] = orientation if inc_site_ids: component['site_ids'] = tuple(graph.nodes()) if inc_molecule_graph and dimensionality == 0: component['molecule_graph'] = zero_d_graph_to_molecule_graph( bonded_structure, graph) component_structure = Structure.from_sites( [bonded_structure.structure[n] for n in sorted(graph.nodes())]) sorted_graph = nx.convert_node_labels_to_integers(graph, ordering="sorted") component_graph = StructureGraph( component_structure, graph_data=json_graph.adjacency_data(sorted_graph)) component['structure_graph'] = component_graph components.append(component) return components
def __getitem__(self, frames): """ Gets a subset of the trajectory if a slice is given, if an int is given, return a structure Args: frames (int, slice): int or slice of trajectory to return Return: (Trajectory, Structure) Subset of trajectory """ # If trajectory is in displacement mode, return the displacements at that frame if self.coords_are_displacement: if isinstance(frames, int): if frames >= np.shape(self.frac_coords)[0]: raise ValueError( "Selected frame exceeds trajectory length") # For integer input, return the displacements at that timestep return self.frac_coords[frames] if isinstance(frames, slice): # For slice input, return a list of the displacements start, stop, step = frames.indices(len(self)) return [self.frac_coords[i] for i in range(start, stop, step)] if isinstance(frames, (list, np.ndarray)): # For list input, return a list of the displacements pruned_frames = [ i for i in frames if i < len(self) ] # Get rid of frames that exceed trajectory length if len(pruned_frames) < len(frames): warnings.warn( "Some or all selected frames exceed trajectory length") return [self.frac_coords[i] for i in pruned_frames] raise Exception( "Given accessor is not of type int, slice, list, or array") # If trajectory is in positions mode, return a structure for the given frame or trajectory for the given frames if isinstance(frames, int): if frames >= np.shape(self.frac_coords)[0]: raise ValueError("Selected frame exceeds trajectory length") # For integer input, return the structure at that timestep lattice = self.lattice if self.constant_lattice else self.lattice[ frames] site_properties = self.site_properties[ frames] if self.site_properties else None site_properties = self.site_properties[ frames] if self.site_properties else None return Structure( Lattice(lattice), self.species, self.frac_coords[frames], site_properties=site_properties, to_unit_cell=True, ) if isinstance(frames, slice): # For slice input, return a trajectory of the sliced time start, stop, step = frames.indices(len(self)) pruned_frames = range(start, stop, step) lattice = self.lattice if self.constant_lattice else [ self.lattice[i] for i in pruned_frames ] frac_coords = [self.frac_coords[i] for i in pruned_frames] if self.site_properties is not None: site_properties = [ self.site_properties[i] for i in pruned_frames ] else: site_properties = None if self.frame_properties is not None: frame_properties = {} for key, item in self.frame_properties.items(): frame_properties[key] = [item[i] for i in pruned_frames] else: frame_properties = None return Trajectory( lattice, self.species, frac_coords, time_step=self.time_step, site_properties=site_properties, frame_properties=frame_properties, constant_lattice=self.constant_lattice, coords_are_displacement=False, base_positions=self.base_positions, ) if isinstance(frames, (list, np.ndarray)): # For list input, return a trajectory of the specified times pruned_frames = [ i for i in frames if i < len(self) ] # Get rid of frames that exceed trajectory length if len(pruned_frames) < len(frames): warnings.warn( "Some or all selected frames exceed trajectory length") lattice = self.lattice if self.constant_lattice else [ self.lattice[i] for i in pruned_frames ] frac_coords = [self.frac_coords[i] for i in pruned_frames] if self.site_properties is not None: site_properties = [ self.site_properties[i] for i in pruned_frames ] else: site_properties = None if self.frame_properties is not None: frame_properties = {} for key, item in self.frame_properties.items(): frame_properties[key] = [item[i] for i in pruned_frames] else: frame_properties = None return Trajectory( lattice, self.species, frac_coords, time_step=self.time_step, site_properties=site_properties, frame_properties=frame_properties, constant_lattice=self.constant_lattice, coords_are_displacement=False, base_positions=self.base_positions, ) raise Exception( "Given accessor is not of type int, slice, tuple, list, or array")
def get_conventional_standard_structure(self): """ Gives a structure with a conventional cell according to certain standards. The standards are defined in Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 They basically enforce as much as possible norm(a1)<norm(a2)<norm(a3) Returns: The structure in a conventional standardized cell """ tol = 1e-5 struct = self.get_refined_structure() latt = struct.lattice latt_type = self.get_lattice_type() sorted_lengths = sorted(latt.abc) sorted_dic = sorted([{'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i} for i in [0, 1, 2]], key=lambda k: k['length']) if latt_type in ("orthorhombic", "cubic"): #you want to keep the c axis where it is #to keep the C- settings transf = np.zeros(shape=(3, 3)) if self.get_spacegroup_symbol().startswith("C"): transf[2] = [0, 0, 1] a, b = sorted(latt.abc[:2]) sorted_dic = sorted([{'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i} for i in [0, 1]], key=lambda k: k['length']) for i in range(2): transf[i][sorted_dic[i]['orig_index']] = 1 c = latt.abc[2] else: for i in range(len(sorted_dic)): transf[i][sorted_dic[i]['orig_index']] = 1 a, b, c = sorted_lengths latt = Lattice.orthorhombic(a, b, c) elif latt_type == "tetragonal": #find the "a" vectors #it is basically the vector repeated two times transf = np.zeros(shape=(3, 3)) a, b, c = sorted_lengths for d in range(len(sorted_dic)): transf[d][sorted_dic[d]['orig_index']] = 1 if abs(b - c) < tol: a, c = c, a transf = np.dot([[0, 0, 1], [0, 1, 0], [1, 0, 0]], transf) latt = Lattice.tetragonal(a, c) elif latt_type in ("hexagonal", "rhombohedral"): #for the conventional cell representation, #we allways show the rhombohedral lattices as hexagonal #check first if we have the refined structure shows a rhombohedral #cell #if so, make a supercell a, b, c = latt.abc if np.all(np.abs([a - b, c - b, a - c]) < 0.001): struct = SupercellMaker(struct, ((1, -1, 0), (0, 1, -1), (1, 1, 1))).modified_structure a, b, c = sorted(struct.lattice.abc) if abs(b - c) < 0.001: a, c = c, a new_matrix = [[a / 2, -a * math.sqrt(3) / 2, 0], [a / 2, a * math.sqrt(3) / 2, 0], [0, 0, c]] latt = Lattice(new_matrix) transf = np.eye(3, 3) elif latt_type == "monoclinic": #you want to keep the c axis where it is #to keep the C- settings if self.get_spacegroup().int_symbol.startswith("C"): transf = np.zeros(shape=(3, 3)) transf[2] = [0, 0, 1] sorted_dic = sorted([{'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i} for i in [0, 1]], key=lambda k: k['length']) a = sorted_dic[0]['length'] b = sorted_dic[1]['length'] c = latt.abc[2] new_matrix = None for t in itertools.permutations(range(2), 2): m = latt.matrix landang = Lattice( [m[t[0]], m[t[1]], m[2]]).lengths_and_angles if landang[1][0] > 90: #if the angle is > 90 we invert a and b to get #an angle < 90 landang = Lattice( [-m[t[0]], -m[t[1]], m[2]]).lengths_and_angles transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = -1 transf[1][t[1]] = -1 transf[2][2] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] continue elif landang[1][0] < 90: transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = 1 transf[1][t[1]] = 1 transf[2][2] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: #this if is to treat the case #where alpha==90 (but we still have a monoclinic sg new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: #this if is to treat the case #where alpha==90 (but we still have a monoclinic sg new_matrix = [[a, 0, 0], [0, b, 0], [0, 0, c]] transf = np.zeros(shape=(3, 3)) for c in range(len(sorted_dic)): transf[c][sorted_dic[c]['orig_index']] = 1 #if not C-setting else: #try all permutations of the axis #keep the ones with the non-90 angle=alpha #and b<c new_matrix = None for t in itertools.permutations(range(3), 3): m = latt.matrix landang = Lattice( [m[t[0]], m[t[1]], m[t[2]]]).lengths_and_angles if landang[1][0] > 90 and landang[0][1] < landang[0][2]: landang = Lattice( [-m[t[0]], -m[t[1]], m[t[2]]]).lengths_and_angles transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = -1 transf[1][t[1]] = -1 transf[2][t[2]] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] continue elif landang[1][0] < 90 and landang[0][1] < landang[0][2]: transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = 1 transf[1][t[1]] = 1 transf[2][t[2]] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: transf = np.zeros(shape=(3, 3)) for c in range(len(sorted_dic)): transf[c][sorted_dic[c]['orig_index']] = 1 latt = Lattice(new_matrix) elif latt_type == "triclinic": #we use a LLL Minkowski-like reduction for the triclinic cells struct = struct.get_reduced_structure("LLL") a, b, c = latt.lengths_and_angles[0] alpha, beta, gamma = [math.pi * i / 180 for i in latt.lengths_and_angles[1]] new_matrix = None test_matrix = [[a, 0, 0], [b * cos(gamma), b * sin(gamma), 0.0], [c * cos(beta), c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] def is_all_acute_or_obtuse(m): recp_angles = np.array(Lattice(m).reciprocal_lattice.angles) return np.all(recp_angles <= 90) or np.all(recp_angles > 90) if is_all_acute_or_obtuse(test_matrix): transf = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] new_matrix = test_matrix test_matrix = [[-a, 0, 0], [b * cos(gamma), b * sin(gamma), 0.0], [-c * cos(beta), -c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), -c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] if is_all_acute_or_obtuse(test_matrix): transf = [[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]] new_matrix = test_matrix test_matrix = [[-a, 0, 0], [-b * cos(gamma), -b * sin(gamma), 0.0], [c * cos(beta), c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] if is_all_acute_or_obtuse(test_matrix): transf = [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]] new_matrix = test_matrix test_matrix = [[a, 0, 0], [-b * cos(gamma), -b * sin(gamma), 0.0], [-c * cos(beta), -c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), -c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] if is_all_acute_or_obtuse(test_matrix): transf = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]] new_matrix = test_matrix latt = Lattice(new_matrix) new_coords = np.dot(transf, np.transpose(struct.frac_coords)).T new_struct = Structure(latt, struct.species_and_occu, new_coords, site_properties=struct.site_properties) return new_struct.get_sorted_structure()
def Interface_generator(Ini_sub_slab, Ini_film_slab, sub_tr_mat, film_tr_mat, distance, fparam): raw_ini_sub_slab_mat = np.array(Ini_sub_slab.lattice.matrix) raw_ini_film_slab_mat = np.array(Ini_film_slab.lattice.matrix) sub_reduction = reduce_vectors(raw_ini_sub_slab_mat[0], raw_ini_sub_slab_mat[1]) film_reduction = reduce_vectors(raw_ini_film_slab_mat[0], raw_ini_film_slab_mat[1]) reduced_sub_mat = np.array( [sub_reduction[0], sub_reduction[1], raw_ini_sub_slab_mat[2]]) reduced_film_mat = np.array( [film_reduction[0], film_reduction[1], raw_ini_film_slab_mat[2]]) red_Ini_sub_slab = Structure(mg.Lattice(reduced_sub_mat), Ini_sub_slab.species, Ini_sub_slab.cart_coords, to_unit_cell=True, coords_are_cartesian=True) red_Ini_film_slab = Structure(mg.Lattice(reduced_film_mat), Ini_film_slab.species, Ini_film_slab.cart_coords, to_unit_cell=True, coords_are_cartesian=True) red_Ini_sub_slab.make_supercell(scaling_matrix=scale_mat(sub_tr_mat)) red_Ini_film_slab.make_supercell(scaling_matrix=scale_mat(film_tr_mat)) Ini_sub_mat = red_Ini_sub_slab.lattice.matrix Ini_film_mat = red_Ini_film_slab.lattice.matrix sub_r_vecs = reduce_vectors(Ini_sub_mat[0], Ini_sub_mat[1]) film_r_vecs = reduce_vectors(Ini_film_mat[0], Ini_film_mat[1]) sub_mat = np.array([sub_r_vecs[0], sub_r_vecs[1], Ini_sub_mat[2]]) film_mat = np.array([film_r_vecs[0], film_r_vecs[1], Ini_film_mat[2]]) AB_C = np.dot(np.cross(sub_mat[0], sub_mat[1]), sub_mat[2]) if AB_C > 0: Is_right_handed = True else: #Is_right_handed = False Is_right_handed = True modif_sub_struc = mg.Structure(mg.Lattice(sub_mat), red_Ini_sub_slab.species, red_Ini_sub_slab.cart_coords, to_unit_cell=True, coords_are_cartesian=True) modif_film_struc = mg.Structure(mg.Lattice(film_mat), red_Ini_film_slab.species, red_Ini_film_slab.cart_coords, to_unit_cell=True, coords_are_cartesian=True) sub_sl_vecs = [ modif_sub_struc.lattice.matrix[0], modif_sub_struc.lattice.matrix[1] ] film_sl_vecs = [ modif_film_struc.lattice.matrix[0], modif_film_struc.lattice.matrix[1] ] film_angel = angle(film_sl_vecs[0], film_sl_vecs[1]) sub_angel = angle(sub_sl_vecs[0], sub_sl_vecs[1]) u_size = fparam * (np.linalg.norm( sub_sl_vecs[0])) + (1 - fparam) * (np.linalg.norm(film_sl_vecs[0])) v_size = fparam * (np.linalg.norm( sub_sl_vecs[1])) + (1 - fparam) * (np.linalg.norm(film_sl_vecs[1])) mean_angle = fparam * sub_angel + (1 - fparam) * film_angel sub_rot_mat = [[ u_size, 0, 0 ], [v_size * math.cos(mean_angle), v_size * math.sin(mean_angle), 0], [0, 0, np.linalg.norm(modif_sub_struc.lattice.matrix[2])]] film_rot_mat = [ [u_size, 0, 0], [v_size * math.cos(mean_angle), v_size * math.sin(mean_angle), 0], [0, 0, -np.linalg.norm(modif_film_struc.lattice.matrix[2])] ] film_normal = np.cross(film_sl_vecs[0], film_sl_vecs[1]) sub_normal = np.cross(sub_sl_vecs[0], sub_sl_vecs[1]) film_un = film_normal / np.linalg.norm(film_normal) sub_un = sub_normal / np.linalg.norm(sub_normal) film_sl_vecs.append(film_un) L1_mat = np.transpose(film_sl_vecs) L1_res = [[u_size, v_size * math.cos(mean_angle), 0], [0, v_size * math.sin(mean_angle), 0], [0, 0, 1]] L1_mat_inv = np.linalg.inv(L1_mat) L1 = np.matmul(L1_res, L1_mat_inv) sub_sl_vecs.append(sub_un) L2_mat = np.transpose(sub_sl_vecs) L2_res = [[u_size, v_size * math.cos(mean_angle), 0], [0, v_size * math.sin(mean_angle), 0], [0, 0, -1]] L2_mat_inv = np.linalg.inv(L2_mat) L2 = np.matmul(L2_res, L2_mat_inv) sub_rot_lattice = mg.Lattice(sub_rot_mat) film_rot_lattice = mg.Lattice(film_rot_mat) r_sub_coords = np.array(modif_sub_struc.cart_coords) r_film_coords = np.array(modif_film_struc.cart_coords) for ii in range(len(r_sub_coords)): r_sub_coords[ii] = np.matmul(L2, r_sub_coords[ii]) for ii in range(len(r_film_coords)): r_film_coords[ii] = np.matmul(L1, r_film_coords[ii]) sub_slab = mg.Structure(sub_rot_lattice, modif_sub_struc.species, r_sub_coords, to_unit_cell=True, coords_are_cartesian=True) film_slab = mg.Structure(film_rot_lattice, modif_film_struc.species, r_film_coords, to_unit_cell=True, coords_are_cartesian=True) sub_sp_num = len(sub_slab.types_of_specie) film_sp_num = len(film_slab.types_of_specie) sub_slab_mat = np.array(sub_slab.lattice.matrix) film_slab_mat = np.array(film_slab.lattice.matrix) sub_slab_coords = sub_slab.cart_coords film_slab_coords = film_slab.cart_coords sub_slab_zmat = sub_slab_coords[:, [2]] film_slab_zmat = film_slab_coords[:, [2]] sub_slab_zmat = sub_slab_zmat - min(sub_slab_zmat) film_slab_zmat = film_slab_zmat - min(film_slab_zmat) sub_max_z = max(sub_slab_zmat) sub_min_z = min(sub_slab_zmat) modif_film_slab_zmat = film_slab_zmat + sub_max_z - sub_min_z + distance film_slab_coords[:, [2]] = modif_film_slab_zmat sub_slab_coords[:, [2]] = sub_slab_zmat sub_max_z = max(sub_slab_zmat) film_min_z = min(modif_film_slab_zmat) sub_max_list = coords_sperator_2(sub_slab_zmat, sub_sp_num, True) film_min_list = coords_sperator(modif_film_slab_zmat, film_sp_num, False) interface_coords = np.concatenate((sub_slab_coords, film_slab_coords), axis=0) interface_species = sub_slab.species + film_slab.species interface_latt = sub_slab_mat interface_latt[2][2] = abs(sub_slab_mat[2][2]) + abs( film_slab_mat[2][2]) + distance Adding_val = 0.5 * (interface_latt[2][2] - max(interface_coords[:, [2]])) sub_max_list += Adding_val film_min_list += Adding_val sub_max_z += Adding_val film_min_z += Adding_val interface_coords[:, [2]] += 0.5 * (interface_latt[2][2] - max(interface_coords[:, [2]])) sub_slab_coords[:, [2]] += Adding_val film_slab_coords[:, [2]] += Adding_val interface_lattice = mg.Lattice(interface_latt) interface_struc = mg.Structure(interface_lattice, interface_species, interface_coords, to_unit_cell=True, coords_are_cartesian=True) interface_struc = interface_struc.get_reduced_structure() return [ interface_struc, [sub_sp_num, film_sp_num], sub_slab_coords, film_slab_coords, sub_slab, film_slab, Is_right_handed ]
def read_data(data=None,ff=None): 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",len(typ),len(coords) pot_file.close() struct=Structure(lat,typ,coords,coords_are_cartesian=True) #print struct finder = SpacegroupAnalyzer(struct) num=finder.get_spacegroup_symbol() #print(num) return struct
def test_get_conventional_standard_structure(self): parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "bcc_1927.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 9.1980270633769461) self.assertAlmostEqual(conv.lattice.b, 9.1980270633769461) self.assertAlmostEqual(conv.lattice.c, 9.1980270633769461) parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "btet_1915.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 5.0615106678044235) self.assertAlmostEqual(conv.lattice.b, 5.0615106678044235) self.assertAlmostEqual(conv.lattice.c, 4.2327080177761687) parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "orci_1010.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 2.9542233922299999) self.assertAlmostEqual(conv.lattice.b, 4.6330325651443296) self.assertAlmostEqual(conv.lattice.c, 5.373703587040775) parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "orcc_1003.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 4.1430033493799998) self.assertAlmostEqual(conv.lattice.b, 31.437979757624728) self.assertAlmostEqual(conv.lattice.c, 3.99648651) parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "orac_632475.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 3.1790663399999999) self.assertAlmostEqual(conv.lattice.b, 9.9032878699999998) self.assertAlmostEqual(conv.lattice.c, 3.5372412099999999) parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "monoc_1028.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 117.53832420192903) self.assertAlmostEqual(conv.lattice.gamma, 90) self.assertAlmostEqual(conv.lattice.a, 14.033435583000625) self.assertAlmostEqual(conv.lattice.b, 3.96052850731) self.assertAlmostEqual(conv.lattice.c, 6.8743926325200002) parser = CifParser( os.path.join(PymatgenTest.TEST_FILES_DIR, "hex_1170.cif")) structure = parser.get_structures(False)[0] s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 90) self.assertAlmostEqual(conv.lattice.beta, 90) self.assertAlmostEqual(conv.lattice.gamma, 120) self.assertAlmostEqual(conv.lattice.a, 3.699919902005897) self.assertAlmostEqual(conv.lattice.b, 3.699919902005897) self.assertAlmostEqual(conv.lattice.c, 6.9779585500000003) structure = Structure.from_file( os.path.join(PymatgenTest.TEST_FILES_DIR, "tric_684654.json")) s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure() self.assertAlmostEqual(conv.lattice.alpha, 74.09581916308757) self.assertAlmostEqual(conv.lattice.beta, 75.72817279281173) self.assertAlmostEqual(conv.lattice.gamma, 63.63234318667333) self.assertAlmostEqual(conv.lattice.a, 3.741372924048738) self.assertAlmostEqual(conv.lattice.b, 3.9883228679270686) self.assertAlmostEqual(conv.lattice.c, 7.288495840048958) structure = Structure.from_file( os.path.join(PymatgenTest.TEST_FILES_DIR, "tric_684654.json")) structure.add_site_property("magmom", [1.0] * len(structure)) s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure(keep_site_properties=True) self.assertEqual(conv.site_properties["magmom"], [1.0] * len(conv)) structure = Structure.from_file( os.path.join(PymatgenTest.TEST_FILES_DIR, "tric_684654.json")) structure.add_site_property("magmom", [1.0] * len(structure)) s = SpacegroupAnalyzer(structure, symprec=1e-2) conv = s.get_conventional_standard_structure( keep_site_properties=False) self.assertEqual(conv.site_properties.get("magmom", None), None)
def make_confs(self, path_to_work, path_to_equi, refine=False): path_to_work = os.path.abspath(path_to_work) if os.path.exists(path_to_work): dlog.warning('%s already exists' % path_to_work) else: os.makedirs(path_to_work) path_to_equi = os.path.abspath(path_to_equi) if 'start_confs_path' in self.parameter and os.path.exists( self.parameter['start_confs_path']): init_path_list = glob.glob( os.path.join(self.parameter['start_confs_path'], '*')) struct_init_name_list = [] for ii in init_path_list: struct_init_name_list.append(ii.split('/')[-1]) struct_output_name = path_to_work.split('/')[-2] assert struct_output_name in struct_init_name_list path_to_equi = os.path.abspath( os.path.join(self.parameter['start_confs_path'], struct_output_name, 'relaxation', 'relax_task')) task_list = [] cwd = os.getcwd() if self.reprod: print('surface reproduce starts') if 'init_data_path' not in self.parameter: raise RuntimeError( "please provide the initial data path to reproduce") init_data_path = os.path.abspath(self.parameter['init_data_path']) task_list = make_repro( init_data_path, self.init_from_suffix, path_to_work, self.parameter.get('reprod_last_frame', True)) os.chdir(cwd) else: if refine: print('surface refine starts') task_list = make_refine(self.parameter['init_from_suffix'], self.parameter['output_suffix'], path_to_work) os.chdir(cwd) # record miller init_from_path = re.sub( self.parameter['output_suffix'][::-1], self.parameter['init_from_suffix'][::-1], path_to_work[::-1], count=1)[::-1] task_list_basename = list(map(os.path.basename, task_list)) for ii in task_list_basename: init_from_task = os.path.join(init_from_path, ii) output_task = os.path.join(path_to_work, ii) os.chdir(output_task) if os.path.isfile('miller.json'): os.remove('miller.json') if os.path.islink('miller.json'): os.remove('miller.json') os.symlink( os.path.relpath( os.path.join(init_from_task, 'miller.json')), 'miller.json') os.chdir(cwd) else: equi_contcar = os.path.join(path_to_equi, 'CONTCAR') if not os.path.exists(equi_contcar): raise RuntimeError("please do relaxation first") ptypes = vasp.get_poscar_types(equi_contcar) # gen structure ss = Structure.from_file(equi_contcar) # gen slabs all_slabs = generate_all_slabs(ss, self.miller, self.min_slab_size, self.min_vacuum_size) os.chdir(path_to_work) if os.path.isfile('POSCAR'): os.remove('POSCAR') if os.path.islink('POSCAR'): os.remove('POSCAR') os.symlink(os.path.relpath(equi_contcar), 'POSCAR') # task_poscar = os.path.join(output, 'POSCAR') for ii in range(len(all_slabs)): output_task = os.path.join(path_to_work, 'task.%06d' % ii) os.makedirs(output_task, exist_ok=True) os.chdir(output_task) for jj in [ 'INCAR', 'POTCAR', 'POSCAR', 'conf.lmp', 'in.lammps' ]: if os.path.exists(jj): os.remove(jj) task_list.append(output_task) print("# %03d generate " % ii, output_task, " \t %d atoms" % len(all_slabs[ii].sites)) # make confs all_slabs[ii].to('POSCAR', 'POSCAR.tmp') vasp.regulate_poscar('POSCAR.tmp', 'POSCAR') vasp.sort_poscar('POSCAR', 'POSCAR', ptypes) vasp.perturb_xz('POSCAR', 'POSCAR', self.pert_xz) # record miller dumpfn(all_slabs[ii].miller_index, 'miller.json') os.chdir(cwd) return task_list
def __init__( self, structure: Structure, overwrite_magmom_mode: Union[OverwriteMagmomMode, str] = "none", round_magmoms: bool = False, detect_valences: bool = False, make_primitive: bool = True, default_magmoms: dict = None, set_net_positive: bool = True, threshold: float = 0.00, threshold_nonmag: float = 0.1, ): r""" If magnetic moments are not defined, moments will be taken either from default_magmoms.yaml (similar to the default magmoms in MPRelaxSet, with a few extra definitions) or from a specie:magmom dict provided by the default_magmoms kwarg. Input magmoms can be replaced using the 'overwrite_magmom_mode' kwarg. This can be: * "none" to do nothing, * "respect_sign" which will overwrite existing magmoms with those from default_magmoms but will keep sites with positive magmoms positive, negative magmoms negative and zero magmoms zero, * "respect_zeros", which will give a ferromagnetic structure (all positive magmoms from default_magmoms) but still keep sites with zero magmoms as zero, * "replace_all" which will try to guess initial magmoms for all sites in the structure irrespective of input structure (this is most suitable for an initial DFT calculation), * "replace_all_if_undefined" is the same as "replace_all" but only if no magmoms are defined in input structure, otherwise it will respect existing magmoms. * "normalize" will normalize magmoms to unity, but will respect sign (used for comparing orderings), magmoms < theshold will be set to zero Args: structure: input Structure object overwrite_magmom_mode: "respect_sign", "respect_zeros", "replace_all", "replace_all_if_undefined", "normalize" (default "none") round_magmoms: will round input magmoms to specified number of decimal places if integer is supplied, if set to a float will try and group magmoms together using a kernel density estimator of provided width, and extracting peaks of the estimator detect_valences: if True, will attempt to assign valences to input structure make_primitive: if True, will transform to primitive magnetic cell default_magmoms: (optional) dict specifying default magmoms set_net_positive: if True, will change sign of magnetic moments such that the net magnetization is positive. Argument will be ignored if mode "respect_sign" is used. threshold: number (in Bohr magnetons) below which magmoms will be rounded to zero threshold_nonmag: number (in Bohr magneton) below which nonmagnetic ions (with no magmom specified in default_magmoms) will be rounded to zero """ if default_magmoms: self.default_magmoms = default_magmoms else: self.default_magmoms = DEFAULT_MAGMOMS structure = structure.copy() # check for disorder if not structure.is_ordered: raise NotImplementedError( "Not implemented for disordered structures, make ordered approximation first." ) if detect_valences: trans = AutoOxiStateDecorationTransformation() try: structure = trans.apply_transformation(structure) except ValueError: warnings.warn( f"Could not assign valences for {structure.composition.reduced_formula}" ) # check to see if structure has magnetic moments # on site properties or species spin properties, # prioritize site properties has_magmoms = bool(structure.site_properties.get("magmom", False)) has_spin = False for comp in structure.species_and_occu: for sp, occu in comp.items(): if getattr(sp, "spin", False): has_spin = True # perform input sanitation ... # rest of class will assume magnetic moments # are stored on site properties: # this is somewhat arbitrary, arguments can # be made for both approaches if has_magmoms and has_spin: raise ValueError("Structure contains magnetic moments on both " "magmom site properties and spin species " "properties. This is ambiguous. Remove one or " "the other.") if has_magmoms: if None in structure.site_properties["magmom"]: warnings.warn("Be careful with mixing types in your magmom " "site properties. Any 'None' magmoms have been " "replaced with zero.") magmoms = [ m if m else 0 for m in structure.site_properties["magmom"] ] elif has_spin: magmoms = [getattr(sp, "spin", 0) for sp in structure.species] structure.remove_spin() else: # no magmoms present, add zero magmoms for now magmoms = [0] * len(structure) # and overwrite magmoms with default magmoms later unless otherwise stated if overwrite_magmom_mode == "replace_all_if_undefined": overwrite_magmom_mode = "replace_all" # test to see if input structure has collinear magmoms self.is_collinear = Magmom.are_collinear(magmoms) if not self.is_collinear: warnings.warn( "This class is not designed to be used with " "non-collinear structures. If your structure is " "only slightly non-collinear (e.g. canted) may still " "give useful results, but use with caution.") # this is for collinear structures only, make sure magmoms # are all floats magmoms = list(map(float, magmoms)) # set properties that should be done /before/ we process input magmoms self.total_magmoms = sum(magmoms) self.magnetization = sum(magmoms) / structure.volume # round magmoms on magnetic ions below threshold to zero # and on non magnetic ions below threshold_nonmag magmoms = [ m if abs(m) > threshold and a.species_string in self.default_magmoms else m if abs(m) > threshold_nonmag and a.species_string not in self.default_magmoms else 0 for (m, a) in zip(magmoms, structure.sites) ] # overwrite existing magmoms with default_magmoms if overwrite_magmom_mode not in ( "none", "respect_sign", "respect_zeros", "replace_all", "replace_all_if_undefined", "normalize", ): raise ValueError("Unsupported mode.") for idx, site in enumerate(structure): if site.species_string in self.default_magmoms: # look for species first, e.g. Fe2+ default_magmom = self.default_magmoms[site.species_string] elif isinstance(site.specie, Species) and str( site.specie.element) in self.default_magmoms: # look for element, e.g. Fe default_magmom = self.default_magmoms[str(site.specie.element)] else: default_magmom = 0 # overwrite_magmom_mode = "respect_sign" will change magnitude of # existing moments only, and keep zero magmoms as # zero: it will keep the magnetic ordering intact if overwrite_magmom_mode == "respect_sign": set_net_positive = False if magmoms[idx] > 0: magmoms[idx] = default_magmom elif magmoms[idx] < 0: magmoms[idx] = -default_magmom # overwrite_magmom_mode = "respect_zeros" will give a ferromagnetic # structure but will keep zero magmoms as zero elif overwrite_magmom_mode == "respect_zeros": if magmoms[idx] != 0: magmoms[idx] = default_magmom # overwrite_magmom_mode = "replace_all" will ignore input magmoms # and give a ferromagnetic structure with magnetic # moments on *all* atoms it thinks could be magnetic elif overwrite_magmom_mode == "replace_all": magmoms[idx] = default_magmom # overwrite_magmom_mode = "normalize" set magmoms magnitude to 1 elif overwrite_magmom_mode == "normalize": if magmoms[idx] != 0: magmoms[idx] = int(magmoms[idx] / abs(magmoms[idx])) # round magmoms, used to smooth out computational data magmoms = self._round_magmoms( magmoms, round_magmoms) if round_magmoms else magmoms # type: ignore if set_net_positive: sign = np.sum(magmoms) if sign < 0: magmoms = [-x for x in magmoms] structure.add_site_property("magmom", magmoms) if make_primitive: structure = structure.get_primitive_structure(use_site_props=True) self.structure = structure
from jarvis.vasp.joptb88vdw import smart_converge from pymatgen.io.vasp.inputs import Poscar from pymatgen.core.structure import Structure import os, time s = Structure.from_file('POSCAR') p = Poscar(s) p.comment = 'bulk@ATAT' en, final = smart_converge(mat=p, elast_prop=False) print "en,finel", en, final
def load_from_cif(self, filename, oxidations, cation="Li"): s = Structure.from_file(os.path.join(module_dir, filename)) s.add_oxidation_state_by_element(oxidations) return BatteryAnalyzer(s, cation)
def get_sg_info(ss): finder = SpacegroupAnalyzer(Structure.from_sites(ss), self.symm_prec) return finder.get_space_group_number()
def set_structure(self, structure, reset_camera=True, to_unit_cell=True): """ Add a structure to the visualizer. Args: structure: structure to visualize reset_camera: Set to True to reset the camera to a default determined based on the structure. to_unit_cell: Whether or not to fall back sites into the unit cell. """ self.ren.RemoveAllViewProps() has_lattice = hasattr(structure, "lattice") if has_lattice: s = Structure.from_sites(structure, to_unit_cell=to_unit_cell) s.make_supercell(self.supercell, to_unit_cell=to_unit_cell) else: s = structure inc_coords = [] for site in s: self.add_site(site) inc_coords.append(site.coords) count = 0 labels = ["a", "b", "c"] colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] if has_lattice: matrix = s.lattice.matrix if self.show_unit_cell and has_lattice: #matrix = s.lattice.matrix self.add_text([0, 0, 0], "o") for vec in matrix: self.add_line((0, 0, 0), vec, colors[count]) self.add_text(vec, labels[count], colors[count]) count += 1 for (vec1, vec2) in itertools.permutations(matrix, 2): self.add_line(vec1, vec1 + vec2) for (vec1, vec2, vec3) in itertools.permutations(matrix, 3): self.add_line(vec1 + vec2, vec1 + vec2 + vec3) if self.show_bonds or self.show_polyhedron: elements = sorted(s.composition.elements, key=lambda a: a.X) anion = elements[-1] def contains_anion(site): for sp in site.species.keys(): if sp.symbol == anion.symbol: return True return False anion_radius = anion.average_ionic_radius for site in s: exclude = False max_radius = 0 color = np.array([0, 0, 0]) for sp, occu in site.species.items(): if sp.symbol in self.excluded_bonding_elements \ or sp == anion: exclude = True break max_radius = max(max_radius, sp.average_ionic_radius) color = color + \ occu * np.array(self.el_color_mapping.get(sp.symbol, [0, 0, 0])) if not exclude: max_radius = (1 + self.poly_radii_tol_factor) * \ (max_radius + anion_radius) nn = structure.get_neighbors(site, float(max_radius)) nn_sites = [] for nnsite, dist in nn: if contains_anion(nnsite): nn_sites.append(nnsite) if not in_coord_list(inc_coords, nnsite.coords): self.add_site(nnsite) if self.show_bonds: self.add_bonds(nn_sites, site) if self.show_polyhedron: color = [i / 255 for i in color] self.add_polyhedron(nn_sites, site, color) if self.show_help: self.helptxt_actor = vtk.vtkActor2D() self.helptxt_actor.VisibilityOn() self.helptxt_actor.SetMapper(self.helptxt_mapper) self.ren.AddActor(self.helptxt_actor) self.display_help() camera = self.ren.GetActiveCamera() if reset_camera: if has_lattice: #Adjust the camera for best viewing lengths = s.lattice.abc pos = (matrix[1] + matrix[2]) * 0.5 + \ matrix[0] * max(lengths) / lengths[0] * 3.5 camera.SetPosition(pos) camera.SetViewUp(matrix[2]) camera.SetFocalPoint((matrix[0] + matrix[1] + matrix[2]) * 0.5) else: origin = s.center_of_mass max_site = max( s, key=lambda site: site.distance_from_point(origin)) camera.SetPosition(origin + 5 * (max_site.coords - origin)) camera.SetFocalPoint(s.center_of_mass) self.structure = structure self.title = s.composition.formula
def test_is_laue(self): s = Structure.from_spacegroup("Fm-3m", np.eye(3) * 3, ["Cu"], [[0, 0, 0]]) a = SpacegroupAnalyzer(s) self.assertTrue(a.is_laue())
def test_primitive(self): s = Structure.from_spacegroup("Fm-3m", np.eye(3) * 3, ["Cu"], [[0, 0, 0]]) a = SpacegroupAnalyzer(s) self.assertEqual(len(s), 4) self.assertEqual(len(a.find_primitive()), 1)
def from_dict(cls, d): """ Returns CompleteCohp object from dict representation. """ cohp_dict = {} efermi = d["efermi"] energies = d["energies"] structure = Structure.from_dict(d["structure"]) if "bonds" in d.keys(): bonds = { bond: { "length": d["bonds"][bond]["length"], "sites": tuple( PeriodicSite.from_dict(site) for site in d["bonds"][bond]["sites"]), } for bond in d["bonds"] } else: bonds = None for label in d["COHP"]: cohp = { Spin(int(spin)): np.array(d["COHP"][label][spin]) for spin in d["COHP"][label] } try: icohp = { Spin(int(spin)): np.array(d["ICOHP"][label][spin]) for spin in d["ICOHP"][label] } except KeyError: icohp = None if label == "average": avg_cohp = Cohp(efermi, energies, cohp, icohp=icohp) else: cohp_dict[label] = Cohp(efermi, energies, cohp, icohp=icohp) if "orb_res_cohp" in d.keys(): orb_cohp = {} for label in d["orb_res_cohp"]: orb_cohp[label] = {} for orb in d["orb_res_cohp"][label]: cohp = { Spin(int(s)): np.array(d["orb_res_cohp"][label][orb]["COHP"][s], dtype=float) for s in d["orb_res_cohp"][label][orb]["COHP"] } try: icohp = { Spin(int(s)): np.array(d["orb_res_cohp"][label][orb]["ICOHP"][s], dtype=float) for s in d["orb_res_cohp"][label][orb]["ICOHP"] } except KeyError: icohp = None orbitals = [ tuple((int(o[0]), Orbital[o[1]])) for o in d["orb_res_cohp"][label][orb]["orbitals"] ] orb_cohp[label][orb] = { "COHP": cohp, "ICOHP": icohp, "orbitals": orbitals, } # If no total COHPs are present, calculate the total # COHPs from the single-orbital populations. Total COHPs # may not be present when the cohpgenerator keyword is used # in LOBSTER versions 2.2.0 and earlier. if label not in d["COHP"] or d["COHP"][label] is None: cohp = { Spin.up: np.sum( np.array([ orb_cohp[label][orb]["COHP"][Spin.up] for orb in orb_cohp[label] ]), axis=0, ) } try: cohp[Spin.down] = np.sum( np.array([ orb_cohp[label][orb]["COHP"][Spin.down] for orb in orb_cohp[label] ]), axis=0, ) except KeyError: pass orb_res_icohp = None in [ orb_cohp[label][orb]["ICOHP"] for orb in orb_cohp[label] ] if (label not in d["ICOHP"] or d["ICOHP"][label] is None) and orb_res_icohp: icohp = { Spin.up: np.sum( np.array([ orb_cohp[label][orb]["ICOHP"][Spin.up] for orb in orb_cohp[label] ]), axis=0, ) } try: icohp[Spin.down] = np.sum( np.array([ orb_cohp[label][orb]["ICOHP"][Spin.down] for orb in orb_cohp[label] ]), axis=0, ) except KeyError: pass else: orb_cohp = None if "average" not in d["COHP"].keys(): # calculate average cohp = np.array([np.array(c) for c in d["COHP"].values()]).mean(axis=0) try: icohp = np.array([np.array(c) for c in d["ICOHP"].values()]).mean(axis=0) except KeyError: icohp = None avg_cohp = Cohp(efermi, energies, cohp, icohp=icohp) return CompleteCohp( structure, avg_cohp, cohp_dict, bonds=bonds, are_coops=d["are_coops"], orb_res_cohp=orb_cohp, )
def _generate_transformations( self, structure: Structure) -> Dict[str, MagOrderingTransformation]: """The central problem with trying to enumerate magnetic orderings is that we have to enumerate orderings that might plausibly be magnetic ground states, while not enumerating orderings that are physically implausible. The problem is that it is not always obvious by e.g. symmetry arguments alone which orderings to prefer. Here, we use a variety of strategies (heuristics) to enumerate plausible orderings, and later discard any duplicates that might be found by multiple strategies. This approach is not ideal, but has been found to be relatively robust over a wide range of magnetic structures. Args: structure: A sanitized input structure (_sanitize_input_structure) Returns: A dict of a transformation class instance (values) and name of enumeration strategy (keys) Returns: dict of Transformations keyed by strategy """ formula = structure.composition.reduced_formula transformations: Dict[str, MagOrderingTransformation] = {} # analyzer is used to obtain information on sanitized input analyzer = CollinearMagneticStructureAnalyzer( structure, default_magmoms=self.default_magmoms, overwrite_magmom_mode="replace_all", ) if not analyzer.is_magnetic: raise ValueError( "Not detected as magnetic, add a new default magmom for the element you believe may be magnetic?" ) # now we can begin to generate our magnetic orderings self.logger.info(f"Generating magnetic orderings for {formula}") mag_species_spin = analyzer.magnetic_species_and_magmoms types_mag_species = sorted( analyzer.types_of_magnetic_species, key=lambda sp: analyzer.default_magmoms.get(str(sp), 0), reverse=True, ) num_mag_sites = analyzer.number_of_magnetic_sites num_unique_sites = analyzer.number_of_unique_magnetic_sites() # enumerations become too slow as number of unique sites (and thus # permutations) increase, 8 is a soft limit, this can be increased # but do so with care if num_unique_sites > self.max_unique_sites: raise ValueError( "Too many magnetic sites to sensibly perform enumeration.") # maximum cell size to consider: as a rule of thumb, if the primitive cell # contains a large number of magnetic sites, perhaps we only need to enumerate # within one cell, whereas on the other extreme if the primitive cell only # contains a single magnetic site, we have to create larger supercells if "max_cell_size" not in self.transformation_kwargs: # TODO: change to 8 / num_mag_sites ? self.transformation_kwargs["max_cell_size"] = max( 1, int(4 / num_mag_sites)) self.logger.info( f"Max cell size set to {self.transformation_kwargs['max_cell_size']}" ) # when enumerating ferrimagnetic structures, it's useful to detect # symmetrically distinct magnetic sites, since different # local environments can result in different magnetic order # (e.g. inverse spinels) # initially, this was done by co-ordination number, but is # now done by a full symmetry analysis sga = SpacegroupAnalyzer(structure) structure_sym = sga.get_symmetrized_structure() wyckoff = ["n/a"] * len(structure) for indices, symbol in zip(structure_sym.equivalent_indices, structure_sym.wyckoff_symbols): for index in indices: wyckoff[index] = symbol is_magnetic_sites = [ site.specie in types_mag_species for site in structure ] # we're not interested in sites that we don't think are magnetic, # set these symbols to None to filter them out later wyckoff = [ symbol if is_magnetic_site else "n/a" for symbol, is_magnetic_site in zip(wyckoff, is_magnetic_sites) ] structure.add_site_property("wyckoff", wyckoff) wyckoff_symbols = set(wyckoff) - {"n/a"} # if user doesn't specifically request ferrimagnetic orderings, # we apply a heuristic as to whether to attempt them or not if self.automatic: if ("ferrimagnetic_by_motif" not in self.strategies and len(wyckoff_symbols) > 1 and len(types_mag_species) == 1): self.strategies += ["ferrimagnetic_by_motif"] if ("antiferromagnetic_by_motif" not in self.strategies and len(wyckoff_symbols) > 1 and len(types_mag_species) == 1): self.strategies += ["antiferromagnetic_by_motif"] if "ferrimagnetic_by_species" not in self.strategies and len( types_mag_species) > 1: self.strategies += ["ferrimagnetic_by_species"] # we start with a ferromagnetic ordering if "ferromagnetic" in self.strategies: # TODO: remove 0 spins ! fm_structure = analyzer.get_ferromagnetic_structure() # store magmom as spin property, to be consistent with output from # other transformations fm_structure.add_spin_by_site( fm_structure.site_properties["magmom"]) fm_structure.remove_site_property("magmom") # we now have our first magnetic ordering... self.ordered_structures.append(fm_structure) self.ordered_structure_origins.append("fm") # we store constraint(s) for each strategy first, # and then use each to perform a transformation later all_constraints: Dict[str, Any] = {} # ...to which we can add simple AFM cases first... if "antiferromagnetic" in self.strategies: constraint = MagOrderParameterConstraint( 0.5, # TODO: update MagOrderParameterConstraint in # pymatgen to take types_mag_species directly species_constraints=list(map(str, types_mag_species)), ) all_constraints["afm"] = [constraint] # allows for non-magnetic sublattices if len(types_mag_species) > 1: for sp in types_mag_species: constraints = [ MagOrderParameterConstraint( 0.5, species_constraints=str(sp)) ] all_constraints[f"afm_by_{sp}"] = constraints # ...and then we also try ferrimagnetic orderings by motif if a # single magnetic species is present... if "ferrimagnetic_by_motif" in self.strategies and len( wyckoff_symbols) > 1: # these orderings are AFM on one local environment, and FM on the rest for symbol in wyckoff_symbols: constraints = [ MagOrderParameterConstraint(0.5, site_constraint_name="wyckoff", site_constraints=symbol), MagOrderParameterConstraint( 1.0, site_constraint_name="wyckoff", site_constraints=list(wyckoff_symbols - {symbol}), ), ] all_constraints[f"ferri_by_motif_{symbol}"] = constraints # and also try ferrimagnetic when there are multiple magnetic species if "ferrimagnetic_by_species" in self.strategies: sp_list = [str(site.specie) for site in structure] num_sp = {sp: sp_list.count(str(sp)) for sp in types_mag_species} total_mag_sites = sum(num_sp.values()) for sp in types_mag_species: # attempt via a global order parameter all_constraints[ f"ferri_by_{sp}"] = num_sp[sp] / total_mag_sites # attempt via afm on sp, fm on remaining species constraints = [ MagOrderParameterConstraint(0.5, species_constraints=str(sp)), MagOrderParameterConstraint( 1.0, species_constraints=list( map(str, set(types_mag_species) - {sp})), ), ] all_constraints[f"ferri_by_{sp}_afm"] = constraints # ...and finally, we can try orderings that are AFM on one local # environment, and non-magnetic on the rest -- this is less common # but unless explicitly attempted, these states are unlikely to be found if "antiferromagnetic_by_motif" in self.strategies: for symbol in wyckoff_symbols: constraints = [ MagOrderParameterConstraint(0.5, site_constraint_name="wyckoff", site_constraints=symbol) ] all_constraints[f"afm_by_motif_{symbol}"] = constraints # and now construct all our transformations for each strategy transformations = {} for name, constraints in all_constraints.items(): trans = MagOrderingTransformation(mag_species_spin, order_parameter=constraints, **self.transformation_kwargs) transformations[name] = trans return transformations
ase_slab = surface(ase_atoms, i, layers) ase_slab.center(vacuum=vacuum, axis=2) slab_pymatgen = AseAtomsAdaptor().get_structure(ase_slab) slab_pymatgen.sort() surf_name = "_".join(map(str, i)) pos = Poscar(slab_pymatgen) try: pos.comment = (str("Surf-") + str(surf_name) + str("@") + str("vac") + str(vacuum) + str("@") + str("layers") + str(layers)) except: pass if write_file == True: pos.write_file(filename=str("POSCAR-") + str("Surf-") + str(surf_name) + str(".vasp")) structures.append(pos) return structures if __name__ == "__main__": pos = str( os.path.join( os.path.dirname(__file__), "../vasp/examples/SiOptb88/MAIN-MBJ-bulk@mp_149/POSCAR", )) mat = Structure.from_file(pos) x = pmg_surfer(mat=mat, write_file=False) print(x) y = surfer(mat=mat, write_file=False)
def get_high_accuracy_voronoi_nodes(structure, rad_dict, probe_rad=0.1): """ Analyze the void space in the input structure using high accuracy voronoi decomposition. Calls Zeo++ for Voronoi decomposition. Args: structure: pymatgen.core.structure.Structure rad_dict (optional): Dictionary of radii of elements in structure. If not given, Zeo++ default values are used. Note: Zeo++ uses atomic radii of elements. For ionic structures, pass rad_dict with ionic radii probe_rad (optional): Sampling probe radius in Angstroms. Default is 0.1 A Returns: voronoi nodes as pymatgen.core.structure.Structure within the unit cell defined by the lattice of input structure voronoi face centers as pymatgen.core.structure.Structure within the unit cell defined by the lattice of input structure """ with ScratchDir("."): name = "temp_zeo1" zeo_inp_filename = name + ".cssr" ZeoCssr(structure).write_file(zeo_inp_filename) rad_flag = True rad_file = name + ".rad" with open(rad_file, "w+") as fp: for el in rad_dict.keys(): print(f"{el} {rad_dict[el].real}", file=fp) atmnet = AtomNetwork.read_from_CSSR(zeo_inp_filename, rad_flag=rad_flag, rad_file=rad_file) # vornet, vor_edge_centers, vor_face_centers = \ # atmnet.perform_voronoi_decomposition() red_ha_vornet = prune_voronoi_network_close_node(atmnet) # generate_simplified_highaccuracy_voronoi_network(atmnet) # get_nearest_largest_diameter_highaccuracy_vornode(atmnet) red_ha_vornet.analyze_writeto_XYZ(name, probe_rad, atmnet) voro_out_filename = name + "_voro.xyz" voro_node_mol = ZeoVoronoiXYZ.from_file(voro_out_filename).molecule species = ["X"] * len(voro_node_mol.sites) coords = [] prop = [] for site in voro_node_mol.sites: coords.append(list(site.coords)) prop.append(site.properties["voronoi_radius"]) lattice = Lattice.from_parameters(*structure.lattice.parameters) vor_node_struct = Structure( lattice, species, coords, coords_are_cartesian=True, to_unit_cell=True, site_properties={"voronoi_radius": prop}, ) return vor_node_struct
dest="delta", default=1.0, type='float', help="step length, default: 0.08", metavar="R_bin") parser.add_option( "-o", "--output", dest="mstyle", default='bmh', help= "matplotlib style, fivethirtyeight, bmh, grayscale, dark_background, ggplot", metavar="mstyle") (options, args) = parser.parse_args() if options.structure.find('cif') > 0: fileformat = 'cif' else: fileformat = 'poscar' plt.style.use(options.mstyle) test = Structure.from_file(options.structure) adf = DDF(crystal=test) print(adf.DDF) test.make_supercell([1, 1, 2]) adf = DDF(crystal=test) print(adf.DDF) #test.make_supercell([2, 2, 2]) #adf = DDF(crystal=test) #print(adf.DDF)
def from_file(cls, fmt, filename=None, structure_file=None, are_coops=False): """ Creates a CompleteCohp object from an output file of a COHP calculation. Valid formats are either LMTO (for the Stuttgart LMTO-ASA code) or LOBSTER (for the LOBSTER code). Args: cohp_file: Name of the COHP output file. Defaults to COPL for LMTO and COHPCAR.lobster/COOPCAR.lobster for LOBSTER. are_coops: Indicates whether the populations are COOPs or COHPs. Defaults to False for COHPs. fmt: A string for the code that was used to calculate the COHPs so that the output file can be handled correctly. Can take the values "LMTO" or "LOBSTER". structure_file: Name of the file containing the structure. If no file name is given, use CTRL for LMTO and POSCAR for LOBSTER. Returns: A CompleteCohp object. """ fmt = fmt.upper() if fmt == "LMTO": # LMTO COOPs and orbital-resolved COHP cannot be handled yet. are_coops = False orb_res_cohp = None if structure_file is None: structure_file = "CTRL" if filename is None: filename = "COPL" cohp_file = LMTOCopl(filename=filename, to_eV=True) elif fmt == "LOBSTER": if structure_file is None: structure_file = "POSCAR" if filename is None: filename = "COOPCAR.lobster" if are_coops else "COHPCAR.lobster" warnings.warn( "The bond labels are currently consistent with ICOHPLIST.lobster/ICOOPLIST.lobster, not with " "COHPCAR.lobster/COOPCAR.lobster. Please be aware!") cohp_file = Cohpcar(filename=filename, are_coops=are_coops) orb_res_cohp = cohp_file.orb_res_cohp else: raise ValueError("Unknown format %s. Valid formats are LMTO " "and LOBSTER." % fmt) structure = Structure.from_file(structure_file) efermi = cohp_file.efermi cohp_data = cohp_file.cohp_data energies = cohp_file.energies # Lobster shifts the energies so that the Fermi energy is at zero. # Shifting should be done by the plotter object though. spins = [Spin.up, Spin.down ] if cohp_file.is_spin_polarized else [Spin.up] if fmt == "LOBSTER": energies += efermi if orb_res_cohp is not None: # If no total COHPs are present, calculate the total # COHPs from the single-orbital populations. Total COHPs # may not be present when the cohpgenerator keyword is used # in LOBSTER versions 2.2.0 and earlier. # TODO: Test this more extensively # pylint: disable=E1133,E1136 for label in orb_res_cohp: if cohp_file.cohp_data[label]["COHP"] is None: # print(label) cohp_data[label]["COHP"] = { sp: np.sum( [ orb_res_cohp[label][orbs]["COHP"][sp] for orbs in orb_res_cohp[label] ], axis=0, ) for sp in spins } if cohp_file.cohp_data[label]["ICOHP"] is None: cohp_data[label]["ICOHP"] = { sp: np.sum( [ orb_res_cohp[label][orbs]["ICOHP"][sp] for orbs in orb_res_cohp[label] ], axis=0, ) for sp in spins } if fmt == "LMTO": # Calculate the average COHP for the LMTO file to be # consistent with LOBSTER output. avg_data = {"COHP": {}, "ICOHP": {}} for i in avg_data: for spin in spins: rows = np.array( [cohp_data[label][i][spin] for label in cohp_data]) avg = np.average(rows, axis=0) # LMTO COHPs have 5 significant figures avg_data[i].update({ spin: np.array([round_to_sigfigs(a, 5) for a in avg], dtype=float) }) avg_cohp = Cohp(efermi, energies, avg_data["COHP"], icohp=avg_data["ICOHP"]) else: avg_cohp = Cohp( efermi, energies, cohp_data["average"]["COHP"], icohp=cohp_data["average"]["COHP"], are_coops=are_coops, ) del cohp_data["average"] cohp_dict = { label: Cohp( efermi, energies, cohp_data[label]["COHP"], icohp=cohp_data[label]["ICOHP"], are_coops=are_coops, ) for label in cohp_data } bond_dict = { label: { "length": cohp_data[label]["length"], "sites": [structure.sites[site] for site in cohp_data[label]["sites"]], } for label in cohp_data } return CompleteCohp( structure, avg_cohp, cohp_dict, bonds=bond_dict, are_coops=are_coops, orb_res_cohp=orb_res_cohp, )
def make_confs(self, path_to_work, path_to_equi, refine=False): path_to_work = os.path.abspath(path_to_work) path_to_equi = os.path.abspath(path_to_equi) task_list = [] cwd = os.getcwd() print('gen interstitial with supercell ' + str(self.supercell) + ' with element ' + str(self.insert_ele)) equi_contcar = os.path.join(path_to_equi, 'CONTCAR') if not os.path.exists(equi_contcar): raise RuntimeError("please do relaxation first") ss = Structure.from_file(equi_contcar) # gen defects dss = [] for ii in self.insert_ele: vds = InterstitialGenerator(ss, ii) for jj in vds: temp = jj.generate_defect_structure(self.supercell) smallest_distance = list(set(temp.distance_matrix.ravel()))[1] if 'conf_filters' in self.parameter and 'min_dist' in self.parameter[ 'conf_filters']: min_dist = self.parameter['conf_filters']['min_dist'] if smallest_distance >= min_dist: dss.append(temp) else: dss.append(temp) # dss.append(jj.generate_defect_structure(self.supercell)) if refine: task_list = make_refine(self.parameter['init_from_suffix'], self.parameter['output_suffix'], path_to_work, len(dss)) for ii in task_list: os.chdir(ii) np.savetxt('supercell.out', self.supercell, fmt='%d') os.chdir(cwd) if self.reprod: if 'vasp_path' not in self.parameter: raise RuntimeError( "please provide the vasp_path for reproduction") vasp_path = os.path.abspath(self.parameter['vasp_path']) task_list = reproduce.make_repro(vasp_path, path_to_work) os.chdir(cwd) else: os.chdir(path_to_work) if os.path.isfile('POSCAR'): os.remove('POSCAR') os.symlink(os.path.relpath(equi_contcar), 'POSCAR') # task_poscar = os.path.join(output, 'POSCAR') for ii in range(len(dss)): output_task = os.path.join(path_to_work, 'task.%06d' % ii) os.makedirs(output_task, exist_ok=True) os.chdir(output_task) for jj in [ 'INCAR', 'POTCAR', 'POSCAR', 'conf.lmp', 'in.lammps' ]: if os.path.exists(jj): os.remove(jj) task_list.append(output_task) dss[ii].to('POSCAR', 'POSCAR') np.savetxt('supercell.out', self.supercell, fmt='%d') os.chdir(cwd) return task_list
def get_voronoi_nodes(structure, rad_dict=None, probe_rad=0.1): """ Analyze the void space in the input structure using voronoi decomposition Calls Zeo++ for Voronoi decomposition. Args: structure: pymatgen.core.structure.Structure rad_dict (optional): Dictionary of radii of elements in structure. If not given, Zeo++ default values are used. Note: Zeo++ uses atomic radii of elements. For ionic structures, pass rad_dict with ionic radii probe_rad (optional): Sampling probe radius in Angstroms. Default is 0.1 A Returns: voronoi nodes as pymatgen.core.structure.Structure within the unit cell defined by the lattice of input structure voronoi face centers as pymatgen.core.structure.Structure within the unit cell defined by the lattice of input structure """ with ScratchDir("."): name = "temp_zeo1" zeo_inp_filename = name + ".cssr" ZeoCssr(structure).write_file(zeo_inp_filename) rad_file = None rad_flag = False if rad_dict: rad_file = name + ".rad" rad_flag = True with open(rad_file, "w+") as fp: for el in rad_dict.keys(): fp.write(f"{el} {rad_dict[el].real}\n") atmnet = AtomNetwork.read_from_CSSR(zeo_inp_filename, rad_flag=rad_flag, rad_file=rad_file) ( vornet, vor_edge_centers, vor_face_centers, ) = atmnet.perform_voronoi_decomposition() vornet.analyze_writeto_XYZ(name, probe_rad, atmnet) voro_out_filename = name + "_voro.xyz" voro_node_mol = ZeoVoronoiXYZ.from_file(voro_out_filename).molecule species = ["X"] * len(voro_node_mol.sites) coords = [] prop = [] for site in voro_node_mol.sites: coords.append(list(site.coords)) prop.append(site.properties["voronoi_radius"]) lattice = Lattice.from_parameters(*structure.lattice.parameters) vor_node_struct = Structure( lattice, species, coords, coords_are_cartesian=True, to_unit_cell=True, site_properties={"voronoi_radius": prop}, ) # PMG-Zeo c<->a transformation for voronoi face centers rot_face_centers = [(center[1], center[2], center[0]) for center in vor_face_centers] rot_edge_centers = [(center[1], center[2], center[0]) for center in vor_edge_centers] species = ["X"] * len(rot_face_centers) prop = [0.0] * len(rot_face_centers) # Vor radius not evaluated for fc vor_facecenter_struct = Structure( lattice, species, rot_face_centers, coords_are_cartesian=True, to_unit_cell=True, site_properties={"voronoi_radius": prop}, ) species = ["X"] * len(rot_edge_centers) prop = [0.0] * len(rot_edge_centers) # Vor radius not evaluated for fc vor_edgecenter_struct = Structure( lattice, species, rot_edge_centers, coords_are_cartesian=True, to_unit_cell=True, site_properties={"voronoi_radius": prop}, ) return vor_node_struct, vor_edgecenter_struct, vor_facecenter_struct
def _parse_sqs_path(path) -> Sqs: """ Private function to parse mcsqs output directory Args: path: directory to perform parsing Returns: Tuple of Pymatgen structure SQS of the input structure, the mcsqs objective function, list of all SQS structures, and the directory where calculations are run """ path = Path(path) # detected instances will be 0 if mcsqs was run in series, or number of instances detected_instances = len(list(path.glob("bestsqs*[0-9]*.out"))) # Convert best SQS structure to cif file and pymatgen Structure with Popen("str2cif < bestsqs.out > bestsqs.cif", shell=True, cwd=path) as p: p.communicate() with warnings.catch_warnings(): warnings.simplefilter("ignore") bestsqs = Structure.from_file(path / "bestsqs.out") # Get best SQS objective function with open(path / "bestcorr.out") as f: lines = f.readlines() objective_function_str = lines[-1].split("=")[-1].strip() objective_function: Union[float, str] if objective_function_str != "Perfect_match": objective_function = float(objective_function_str) else: objective_function = "Perfect_match" # Get all SQS structures and objective functions allsqs = [] for i in range(detected_instances): sqs_out = f"bestsqs{i + 1}.out" sqs_cif = f"bestsqs{i + 1}.cif" corr_out = f"bestcorr{i + 1}.out" with Popen(f"str2cif < {sqs_out} > {sqs_cif}", shell=True, cwd=path) as p: p.communicate() with warnings.catch_warnings(): warnings.simplefilter("ignore") sqs = Structure.from_file(path / sqs_out) with open(path / corr_out) as f: lines = f.readlines() objective_function_str = lines[-1].split("=")[-1].strip() obj: Union[float, str] if objective_function_str != "Perfect_match": obj = float(objective_function_str) else: obj = "Perfect_match" allsqs.append({"structure": sqs, "objective_function": obj}) clusters = _parse_clusters(path / "clusters.out") return Sqs( bestsqs=bestsqs, objective_function=objective_function, allsqs=allsqs, directory=str(path.resolve()), clusters=clusters, )
def get_primitive_standard_structure(self): """ Gives a structure with a primitive cell according to certain standards the standards are defined in Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 Returns: The structure in a primitive standardized cell """ conv = self.get_conventional_standard_structure() lattice = self.get_lattice_type() if "P" in self.get_spacegroup_symbol() or lattice == "hexagonal": return conv if lattice == "rhombohedral": conv = self.get_refined_structure() lengths, angles = conv.lattice.lengths_and_angles a = lengths[0] alpha = math.pi * angles[0] / 180 new_matrix = [ [a * cos(alpha / 2), -a * sin(alpha / 2), 0], [a * cos(alpha / 2), a * sin(alpha / 2), 0], [a * cos(alpha) / cos(alpha / 2), 0, a * math.sqrt(1 - (cos(alpha) ** 2 / (cos(alpha / 2) ** 2)))]] new_sites = [] latt = Lattice(new_matrix) for s in conv: new_s = PeriodicSite( s.specie, s.frac_coords, latt, to_unit_cell=True, properties=s.properties) if not any(map(new_s.is_periodic_image, new_sites)): new_sites.append(new_s) return Structure.from_sites(new_sites) if "I" in self.get_spacegroup_symbol(): transf = np.array([[-1, 1, 1], [1, -1, 1], [1, 1, -1]], dtype=np.float) / 2 elif "F" in self.get_spacegroup_symbol(): transf = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]], dtype=np.float) / 2 elif "C" in self.get_spacegroup_symbol(): if self.get_crystal_system() == "monoclinic": transf = np.array([[1, 1, 0], [-1, 1, 0], [0, 0, 2]], dtype=np.float) / 2 else: transf = np.array([[1, -1, 0], [1, 1, 0], [0, 0, 2]], dtype=np.float) / 2 else: transf = np.eye(3) new_sites = [] latt = Lattice(np.dot(transf, conv.lattice.matrix)) for s in conv: new_s = PeriodicSite( s.specie, s.coords, latt, to_unit_cell=True, coords_are_cartesian=True, properties=s.properties) if not any(map(new_s.is_periodic_image, new_sites)): new_sites.append(new_s) return Structure.from_sites(new_sites)
def test_to_from_dict(self): d = self.structure.as_dict() s2 = Structure.from_dict(d) self.assertEqual(type(s2), Structure)
class StructureTest(PymatgenTest): def setUp(self): coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) self.structure = Structure(lattice, ["Si", "Si"], coords) def test_mutable_sequence_methods(self): s = self.structure s[0] = "Fe" self.assertEqual(s.formula, "Fe1 Si1") s[0] = "Fe", [0.5, 0.5, 0.5] self.assertEqual(s.formula, "Fe1 Si1") self.assertArrayAlmostEqual(s[0].frac_coords, [0.5, 0.5, 0.5]) s.reverse() self.assertEqual(s[0].specie, Element("Si")) self.assertArrayAlmostEqual(s[0].frac_coords, [0.75, 0.5, 0.75]) s[0] = {"Mn": 0.5} self.assertEqual(s.formula, "Mn0.5 Fe1") del s[1] self.assertEqual(s.formula, "Mn0.5") s[0] = "Fe", [0.9, 0.9, 0.9], {"magmom": 5} self.assertEqual(s.formula, "Fe1") self.assertEqual(s[0].magmom, 5) def test_non_hash(self): self.assertRaises(TypeError, dict, [(self.structure, 1)]) def test_sort(self): s = self.structure s[0] = "F" s.sort() self.assertEqual(s[0].species_string, "Si") self.assertEqual(s[1].species_string, "F") s.sort(key=lambda site: site.species_string) self.assertEqual(s[0].species_string, "F") self.assertEqual(s[1].species_string, "Si") s.sort(key=lambda site: site.species_string, reverse=True) self.assertEqual(s[0].species_string, "Si") self.assertEqual(s[1].species_string, "F") def test_append_insert_remove_replace(self): s = self.structure s.insert(1, "O", [0.5, 0.5, 0.5]) self.assertEqual(s.formula, "Si2 O1") self.assertTrue(s.ntypesp == 2) self.assertTrue(s.symbol_set == ("Si", "O")) self.assertTrue(s.indices_from_symbol("Si") == (0, 2)) self.assertTrue(s.indices_from_symbol("O") == (1, )) del s[2] self.assertEqual(s.formula, "Si1 O1") self.assertTrue(s.indices_from_symbol("Si") == (0, )) self.assertTrue(s.indices_from_symbol("O") == (1, )) s.append("N", [0.25, 0.25, 0.25]) self.assertEqual(s.formula, "Si1 N1 O1") self.assertTrue(s.ntypesp == 3) self.assertTrue(s.symbol_set == ("Si", "O", "N")) self.assertTrue(s.indices_from_symbol("Si") == (0, )) self.assertTrue(s.indices_from_symbol("O") == (1, )) self.assertTrue(s.indices_from_symbol("N") == (2, )) s[0] = "Ge" self.assertEqual(s.formula, "Ge1 N1 O1") self.assertTrue(s.symbol_set == ("Ge", "O", "N")) s.replace_species({"Ge": "Si"}) self.assertEqual(s.formula, "Si1 N1 O1") self.assertTrue(s.ntypesp == 3) s.replace_species({"Si": {"Ge": 0.5, "Si": 0.5}}) self.assertEqual(s.formula, "Si0.5 Ge0.5 N1 O1") #this should change the .5Si .5Ge sites to .75Si .25Ge s.replace_species({"Ge": {"Ge": 0.5, "Si": 0.5}}) self.assertEqual(s.formula, "Si0.75 Ge0.25 N1 O1") # In this case, s.ntypesp is ambiguous. # for the time being, we raise AttributeError. with self.assertRaises(AttributeError): s.ntypesp s.remove_species(["Si"]) self.assertEqual(s.formula, "Ge0.25 N1 O1") s.remove_sites([1, 2]) self.assertEqual(s.formula, "Ge0.25") def test_add_site_property(self): s = self.structure s.add_site_property("charge", [4.1, -5]) self.assertEqual(s[0].charge, 4.1) self.assertEqual(s[1].charge, -5) s.add_site_property("magmom", [3, 2]) self.assertEqual(s[0].charge, 4.1) self.assertEqual(s[0].magmom, 3) def test_propertied_structure(self): #Make sure that site properties are set to None for missing values. s = self.structure s.add_site_property("charge", [4.1, -5]) s.append("Li", [0.3, 0.3, 0.3]) self.assertEqual(len(s.site_properties["charge"]), 3) def test_perturb(self): d = 0.1 pre_perturbation_sites = self.structure.sites[:] self.structure.perturb(distance=d) post_perturbation_sites = self.structure.sites for i, x in enumerate(pre_perturbation_sites): self.assertAlmostEqual(x.distance(post_perturbation_sites[i]), d, 3, "Bad perturbation distance") def test_add_oxidation_states(self): oxidation_states = {"Si": -4} self.structure.add_oxidation_state_by_element(oxidation_states) for site in self.structure: for k in site.species_and_occu.keys(): self.assertEqual(k.oxi_state, oxidation_states[k.symbol], "Wrong oxidation state assigned!") oxidation_states = {"Fe": 2} self.assertRaises(ValueError, self.structure.add_oxidation_state_by_element, oxidation_states) self.structure.add_oxidation_state_by_site([2, -4]) self.assertEqual(self.structure[0].specie.oxi_state, 2) self.assertRaises(ValueError, self.structure.add_oxidation_state_by_site, [1]) def test_remove_oxidation_states(self): co_elem = Element("Co") o_elem = Element("O") co_specie = Specie("Co", 2) o_specie = Specie("O", -2) coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = Lattice.cubic(10) s_elem = Structure(lattice, [co_elem, o_elem], coords) s_specie = Structure(lattice, [co_specie, o_specie], coords) s_specie.remove_oxidation_states() self.assertEqual(s_elem, s_specie, "Oxidation state remover " "failed") def test_apply_operation(self): op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 90) s = self.structure.copy() s.apply_operation(op) self.assertArrayAlmostEqual( s.lattice.matrix, [[0.000000, 3.840198, 0.000000], [-3.325710, 1.920099, 0.000000], [2.217138, -0.000000, 3.135509]], 5) op = SymmOp([[1, 1, 0, 0.5], [1, 0, 0, 0.5], [0, 0, 1, 0.5], [0, 0, 0, 1]]) s = self.structure.copy() s.apply_operation(op, fractional=True) self.assertArrayAlmostEqual( s.lattice.matrix, [[5.760297, 3.325710, 0.000000], [3.840198, 0.000000, 0.000000], [0.000000, -2.217138, 3.135509]], 5) def test_apply_strain(self): s = self.structure initial_coord = s[1].coords s.apply_strain(0.01) self.assertAlmostEqual( s.lattice.abc, (3.8785999130369997, 3.878600984287687, 3.8785999130549516)) self.assertArrayAlmostEqual(s[1].coords, initial_coord * 1.01) a1, b1, c1 = s.lattice.abc s.apply_strain([0.1, 0.2, 0.3]) a2, b2, c2 = s.lattice.abc self.assertAlmostEqual(a2 / a1, 1.1) self.assertAlmostEqual(b2 / b1, 1.2) self.assertAlmostEqual(c2 / c1, 1.3) def test_scale_lattice(self): initial_coord = self.structure[1].coords self.structure.scale_lattice(self.structure.volume * 1.01**3) self.assertArrayAlmostEqual( self.structure.lattice.abc, (3.8785999130369997, 3.878600984287687, 3.8785999130549516)) self.assertArrayAlmostEqual(self.structure[1].coords, initial_coord * 1.01) def test_translate_sites(self): self.structure.translate_sites([0, 1], [0.5, 0.5, 0.5], frac_coords=True) self.assertArrayEqual(self.structure.frac_coords[0], [0.5, 0.5, 0.5]) self.structure.translate_sites([0], [0.5, 0.5, 0.5], frac_coords=False) self.assertArrayAlmostEqual(self.structure.cart_coords[0], [3.38014845, 1.05428585, 2.06775453]) self.structure.translate_sites([0], [0.5, 0.5, 0.5], frac_coords=True, to_unit_cell=False) self.assertArrayAlmostEqual(self.structure.frac_coords[0], [1.00187517, 1.25665291, 1.15946374]) def test_mul(self): self.structure *= [2, 1, 1] self.assertEqual(self.structure.formula, "Si4") s = [2, 1, 1] * self.structure self.assertEqual(s.formula, "Si8") self.assertIsInstance(s, Structure) s = self.structure * [[1, 0, 0], [2, 1, 0], [0, 0, 2]] self.assertEqual(s.formula, "Si8") self.assertArrayAlmostEqual(s.lattice.abc, [7.6803959, 17.5979979, 7.6803959]) def test_make_supercell(self): self.structure.make_supercell([2, 1, 1]) self.assertEqual(self.structure.formula, "Si4") self.structure.make_supercell([[1, 0, 0], [2, 1, 0], [0, 0, 1]]) self.assertEqual(self.structure.formula, "Si4") self.structure.make_supercell(2) self.assertEqual(self.structure.formula, "Si32") self.assertArrayAlmostEqual(self.structure.lattice.abc, [15.360792, 35.195996, 7.680396], 5) def test_disordered_supercell_primitive_cell(self): l = Lattice.cubic(2) f = [[0.5, 0.5, 0.5]] sp = [{'Si': 0.54738}] s = Structure(l, sp, f) #this supercell often breaks things s.make_supercell([[0, -1, 1], [-1, 1, 0], [1, 1, 1]]) self.assertEqual(len(s.get_primitive_structure()), 1) def test_another_supercell(self): #this is included b/c for some reason the old algo was failing on it s = self.structure.copy() s.make_supercell([[0, 2, 2], [2, 0, 2], [2, 2, 0]]) self.assertEqual(s.formula, "Si32") s = self.structure.copy() s.make_supercell([[0, 2, 0], [1, 0, 0], [0, 0, 1]]) self.assertEqual(s.formula, "Si4") def test_to_from_dict(self): d = self.structure.as_dict() s2 = Structure.from_dict(d) self.assertEqual(type(s2), Structure) def test_to_from_file_string(self): for fmt in ["cif", "json", "poscar", "cssr", "yaml", "xsf"]: s = self.structure.to(fmt=fmt) self.assertIsNotNone(s) ss = Structure.from_str(s, fmt=fmt) self.assertArrayAlmostEqual( ss.lattice.lengths_and_angles, self.structure.lattice.lengths_and_angles, decimal=5) self.assertArrayAlmostEqual(ss.frac_coords, self.structure.frac_coords) self.assertIsInstance(ss, Structure) self.structure.to(filename="POSCAR.testing") self.assertTrue(os.path.exists("POSCAR.testing")) os.remove("POSCAR.testing") self.structure.to(filename="structure_testing.json") self.assertTrue(os.path.exists("structure_testing.json")) s = Structure.from_file("structure_testing.json") self.assertEqual(s, self.structure) os.remove("structure_testing.json") def test_from_spacegroup(self): s1 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) self.assertEqual(s1.formula, "Li8 O4") s2 = Structure.from_spacegroup(225, Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) self.assertEqual(s1, s2) s2 = Structure.from_spacegroup(225, Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]], site_properties={"charge": [1, -2]}) self.assertEqual(sum(s2.site_properties["charge"]), 0) s = Structure.from_spacegroup("Pm-3m", Lattice.cubic(3), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) self.assertEqual(s.formula, "Cs1 Cl1") self.assertRaises(ValueError, Structure.from_spacegroup, "Pm-3m", Lattice.tetragonal(1, 3), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) def test_merge_sites(self): species = [{ 'Ag': 0.5 }, { 'Cl': 0.25 }, { 'Cl': 0.1 }, { 'Ag': 0.5 }, { 'F': 0.15 }, { 'F': 0.1 }] coords = [[0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0, 0, 0], [0.5, 0.5, 1.501], [0.5, 0.5, 1.501]] s = Structure(Lattice.cubic(1), species, coords) s.merge_sites() self.assertEqual(s[0].specie.symbol, 'Ag') self.assertEqual(s[1].species_and_occu, Composition({ 'Cl': 0.35, 'F': 0.25 })) self.assertArrayAlmostEqual(s[1].frac_coords, [.5, .5, .5005]) def test_properties(self): self.assertEqual(self.structure.num_sites, len(self.structure)) self.structure.make_supercell(2) self.structure[1] = "C" sites = list(self.structure.group_by_types()) self.assertEqual(sites[-1].specie.symbol, "C") self.structure.add_oxidation_state_by_element({"Si": 4, "C": 2}) self.assertEqual(self.structure.charge, 62) def test_init_error(self): self.assertRaises(StructureError, Structure, Lattice.cubic(3), ["Si"], [[0, 0, 0], [0.5, 0.5, 0.5]]) def test_from_sites(self): self.structure.add_site_property("hello", [1, 2]) s = Structure.from_sites(self.structure, to_unit_cell=True) self.assertEqual(s.site_properties["hello"][1], 2) def test_magic(self): s = Structure.from_sites(self.structure) self.assertEqual(s, self.structure) self.assertNotEqual(s, None) s.apply_strain(0.5) self.assertNotEqual(s, self.structure) self.assertNotEqual(self.structure * 2, self.structure)
def test_from_sites(self): self.structure.add_site_property("hello", [1, 2]) s = Structure.from_sites(self.structure, to_unit_cell=True) self.assertEqual(s.site_properties["hello"][1], 2)
def run_mcsqs( structure: Structure, clusters: Dict[int, float], scaling: Union[int, List[int]] = 1, search_time: float = 60, directory: Optional[str] = None, instances: Optional[int] = None, temperature: Union[int, float] = 1, wr: float = 1, wn: float = 1, wd: float = 0.5, tol: float = 1e-3, ) -> Sqs: """ Helper function for calling mcsqs with different arguments Args: structure (Structure): Disordered pymatgen Structure object clusters (dict): Dictionary of cluster interactions with entries in the form number of atoms: cutoff in angstroms scaling (int or list): Scaling factor to determine supercell. Two options are possible: a. (preferred) Scales number of atoms, e.g., for a structure with 8 atoms, scaling=4 would lead to a 32 atom supercell b. A sequence of three scaling factors, e.g., [2, 1, 1], which specifies that the supercell should have dimensions 2a x b x c Defaults to 1. search_time (float): Time spent looking for the ideal SQS in minutes (default: 60) directory (str): Directory to run mcsqs calculation and store files (default: None runs calculations in a temp directory) instances (int): Specifies the number of parallel instances of mcsqs to run (default: number of cpu cores detected by Python) temperature (int or float): Monte Carlo temperature (default: 1), "T" in atat code wr (int or float): Weight assigned to range of perfect correlation match in objective function (default = 1) wn (int or float): Multiplicative decrease in weight per additional point in cluster (default: 1) wd (int or float): Exponent of decay in weight as function of cluster diameter (default: 0.5) tol (int or float): Tolerance for matching correlations (default: 1e-3) Returns: Tuple of Pymatgen structure SQS of the input structure, the mcsqs objective function, list of all SQS structures, and the directory where calculations are run """ num_atoms = len(structure) if structure.is_ordered: raise ValueError("Pick a disordered structure") if instances is None: # os.cpu_count() can return None if detection fails instances = os.cpu_count() original_directory = os.getcwd() if not directory: directory = tempfile.mkdtemp() os.chdir(directory) if isinstance(scaling, (int, float)): if scaling % 1: raise ValueError(f"Scaling should be an integer, not {scaling}") mcsqs_find_sqs_cmd = ["mcsqs", f"-n {scaling * num_atoms}"] else: # Set supercell to identity (will make supercell with pymatgen) with open("sqscell.out", "w") as f: f.write("1\n1 0 0\n0 1 0\n0 0 1\n") structure = structure * scaling mcsqs_find_sqs_cmd = ["mcsqs", "-rc", f"-n {num_atoms}"] structure.to(filename="rndstr.in") # Generate clusters mcsqs_generate_clusters_cmd = ["mcsqs"] for num in clusters: mcsqs_generate_clusters_cmd.append("-" + str(num) + "=" + str(clusters[num])) # Run mcsqs to find clusters with Popen(mcsqs_generate_clusters_cmd) as p: p.communicate() # Generate SQS structures add_ons = [ f"-T {temperature}", f"-wr {wr}", f"-wn {wn}", f"-wd {wd}", f"-tol {tol}", ] mcsqs_find_sqs_processes = [] if instances and instances > 1: # if multiple instances, run a range of commands using "-ip" for i in range(instances): instance_cmd = [f"-ip {i + 1}"] cmd = mcsqs_find_sqs_cmd + add_ons + instance_cmd p = Popen(cmd) # pylint: disable=R1732 mcsqs_find_sqs_processes.append(p) else: # run normal mcsqs command cmd = mcsqs_find_sqs_cmd + add_ons p = Popen(cmd) # pylint: disable=R1732 mcsqs_find_sqs_processes.append(p) try: for idx, p in enumerate(mcsqs_find_sqs_processes): p.communicate(timeout=search_time * 60) if instances and instances > 1: p = Popen(["mcsqs", "-best"]) # pylint: disable=R1732 p.communicate() if os.path.exists("bestsqs.out") and os.path.exists("bestcorr.out"): return _parse_sqs_path(".") raise RuntimeError("mcsqs exited before timeout reached") except TimeoutExpired: for p in mcsqs_find_sqs_processes: p.kill() p.communicate() # Find the best sqs structures if instances and instances > 1: if not os.path.exists("bestcorr1.out"): raise RuntimeError( "mcsqs did not generate output files, " "is search_time sufficient or are number of instances too high?" ) p = Popen(["mcsqs", "-best"]) # pylint: disable=R1732 p.communicate() if os.path.exists("bestsqs.out") and os.path.exists("bestcorr.out"): sqs = _parse_sqs_path(".") return sqs os.chdir(original_directory) raise TimeoutError("Cluster expansion took too long.")
def get_relaxed_structure(gout): """ Args: gout (): Returns: (Structure) relaxed structure. """ # Find the structure lines structure_lines = [] cell_param_lines = [] output_lines = gout.split("\n") no_lines = len(output_lines) i = 0 # Compute the input lattice parameters while i < no_lines: line = output_lines[i] if "Full cell parameters" in line: i += 2 line = output_lines[i] a = float(line.split()[8]) alpha = float(line.split()[11]) line = output_lines[i + 1] b = float(line.split()[8]) beta = float(line.split()[11]) line = output_lines[i + 2] c = float(line.split()[8]) gamma = float(line.split()[11]) i += 3 break if "Cell parameters" in line: i += 2 line = output_lines[i] a = float(line.split()[2]) alpha = float(line.split()[5]) line = output_lines[i + 1] b = float(line.split()[2]) beta = float(line.split()[5]) line = output_lines[i + 2] c = float(line.split()[2]) gamma = float(line.split()[5]) i += 3 break i += 1 while i < no_lines: line = output_lines[i] if "Final fractional coordinates of atoms" in line: # read the site coordinates in the following lines i += 6 line = output_lines[i] while line[0:2] != "--": structure_lines.append(line) i += 1 line = output_lines[i] # read the cell parameters i += 9 line = output_lines[i] if "Final cell parameters" in line: i += 3 for del_i in range(6): line = output_lines[i + del_i] cell_param_lines.append(line) break i += 1 # Process the structure lines if structure_lines: sp = [] coords = [] for line in structure_lines: fields = line.split() if fields[2] == "c": sp.append(fields[1]) coords.append(list(float(x) for x in fields[3:6])) else: raise IOError("No structure found") if cell_param_lines: a = float(cell_param_lines[0].split()[1]) b = float(cell_param_lines[1].split()[1]) c = float(cell_param_lines[2].split()[1]) alpha = float(cell_param_lines[3].split()[1]) beta = float(cell_param_lines[4].split()[1]) gamma = float(cell_param_lines[5].split()[1]) latt = Lattice.from_parameters(a, b, c, alpha, beta, gamma) return Structure(latt, sp, coords)