Example #1
0
    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)
Example #3
0
    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")
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
 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
Example #8
0
    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
Example #9
0
    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
Example #10
0
 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)
Example #11
0
 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)
Example #12
0
    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
Example #14
0
    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')
Example #15
0
    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]
Example #16
0
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"
Example #18
0
 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)
Example #19
0
 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})
Example #20
0
 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)
Example #21
0
 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]])
Example #22
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)])
Example #23
0
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()}
Example #24
0
    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)
Example #25
0
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
Example #26
0
    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])
Example #29
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])
Example #30
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]])
Example #31
0
 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
Example #32
0
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
Example #33
0
    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")
Example #34
0
    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
    ]
Example #36
0
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
Example #37
0
    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)
Example #38
0
    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
Example #39
0
    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
Example #40
0
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
Example #41
0
 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()
Example #43
0
    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
Example #44
0
 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())
Example #45
0
 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)
Example #46
0
    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,
        )
Example #47
0
    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
Example #48
0
        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)
Example #49
0
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
Example #50
0
                      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)
Example #51
0
    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,
        )
Example #52
0
    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
Example #53
0
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
Example #54
0
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,
    )
Example #55
0
    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)
Example #56
0
 def test_to_from_dict(self):
     d = self.structure.as_dict()
     s2 = Structure.from_dict(d)
     self.assertEqual(type(s2), Structure)
Example #57
0
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)
Example #58
0
 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)
Example #59
0
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)