예제 #1
0
def perturb_structure(
        structure: Structure, center: List[float],
        cutoff: float) -> Tuple[Structure, Tuple[PerturbedSite, ...]]:
    """ structure perturbation

    Args:
        structure: pmg Structure class object
        center: Fractional coordinates of a central position.
        cutoff: Radius of a sphere in which atoms are perturbed.
    """
    p_structure = structure.copy()
    perturbed_sites = []
    cartesian_coords = structure.lattice.get_cartesian_coords(center)
    neighboring_atoms: List[PeriodicNeighbor] = structure.get_sites_in_sphere(
        pt=cartesian_coords, r=cutoff, include_index=True)

    for p_neighbor in neighboring_atoms:
        vector, distance = random_3d_vector(defaults.displace_distance)
        p_structure.translate_sites(p_neighbor.index,
                                    vector,
                                    frac_coords=False)
        s = PerturbedSite(element=str(p_neighbor.specie),
                          distance=p_neighbor.nn_distance,
                          initial_coords=tuple(p_neighbor.frac_coords),
                          perturbed_coords=tuple(
                              p_structure[p_neighbor.index].frac_coords),
                          displacement=distance)
        perturbed_sites.append(s)

    return p_structure, tuple(perturbed_sites)
    def __init__(
            self,
            initial_structure: Structure,
            task: Task,
            kpt_mode: Union[KpointsMode, str, None] = None,  # None for default
            kpt_density: Optional[float] = None,  # in Å
            gamma_centered: Optional[bool] = None,  # Vasp definition
            only_even_num_kpts: bool = False,  # If ceil kpt numbers to be even.
            num_kpt_factor: Optional[int] = None,  # Set NKRED to this as well.
            band_ref_dist: float = defaults.band_mesh_distance,
            symprec: float = defaults.symmetry_length_tolerance,
            angle_tolerance: float = defaults.symmetry_angle_tolerance,
            is_magnetization: bool = False):  # Whether the system is magnetic.

        if kpt_mode:
            kpt_mode = KpointsMode.from_string(str(kpt_mode))

        self._initial_structure = initial_structure.copy()
        self._task = task
        self._kpt_density = kpt_density or defaults.kpoint_density
        logger.info(f"kpoint density is set to {self._kpt_density}")
        self._is_magnetization = is_magnetization
        self._num_kpt_factor = num_kpt_factor or self._task.default_kpt_factor
        if self._num_kpt_factor != 1:
            logger.info(f"kpoint factor is set to {self._num_kpt_factor}")
        self._symmetrizer = \
            StructureSymmetrizer(structure=self._initial_structure,
                                 symprec=symprec,
                                 angle_tolerance=angle_tolerance,
                                 band_mesh_distance=band_ref_dist,
                                 time_reversal=(not self._is_magnetization))
        # overwrite options fixed by other options
        self._gamma_centered = task.requisite_gamma_centered or gamma_centered
        self._adjust_only_even_num_kpts(only_even_num_kpts)
        self._adjust_kpt_mode(kpt_mode)
예제 #3
0
def refine_defect_structure(structure: Structure,
                            anchor_atom_index: int = None,
                            anchor_atom_coords: np.ndarray = None):
    symmetrizer = StructureSymmetrizer(structure,
                                       defaults.symmetry_length_tolerance,
                                       defaults.symmetry_angle_tolerance)
    result = structure.copy()
    spglib_data = symmetrizer.spglib_sym_data

    origin_shift = spglib_data["origin_shift"]
    inv_trans_mat = inv(spglib_data["transformation_matrix"])
    coords = spglib_data["std_positions"]
    # This transformation is key for refinement.
    new_coords = np.array([
        np.dot(inv_trans_mat, (coords[i] - origin_shift))
        for i in range(len(result))
    ])
    if anchor_atom_index:
        offset = new_coords[anchor_atom_index] - anchor_atom_coords
        new_coords = new_coords - offset
    for i, coords in zip(result, new_coords):
        i.frac_coords = coords % 1

    if result != structure:
        logger.info(f"Structure is refined to the one with point group "
                    f"{symmetrizer.point_group}.")
        return result
    else:
        logger.info(f"Refined structure is same as before, so do nothing.")
예제 #4
0
    def get_111_struct(self):
        lattice = Lattice( self.lattconst * np.identity(3))
        species = [self.eltA, self.eltB, self.eltC,
                   self.eltC, self.eltC]
        coords = [[0., 0., 0.], [0.5, 0.5, 0.5], [0.5, 0.5, 0.],
                  [0.5, 0., 0.5], [0., 0.5, 0.5]]

        struct = Structure(lattice, species, coords,
                                coords_are_cartesian=False)
        return struct.copy()
예제 #5
0
def test_coloring_with_fixed_species():
    lattice = Lattice(3.945 * np.eye(3))
    species = ["Sr", "Ti", "O", "O", "O"]
    frac_coords = np.array([[0, 0, 0], [0.5, 0.5, 0.5], [0.0, 0.5, 0.5],
                            [0.5, 0.0, 0.5], [0.5, 0.5, 0.0]])
    aristo = Structure(lattice, species, frac_coords)
    base_structure = aristo.copy()
    base_structure.remove_species(["Sr", "Ti"])
    additional_species = species[:2]
    additional_frac_coords = frac_coords[:2]

    mapping_color_species = [DummySpecie("X"), "O"]
    num_types = len(mapping_color_species)
    index = 2

    se = StructureEnumerator(
        base_structure,
        index,
        num_types,
        mapping_color_species=mapping_color_species,
        color_exchange=False,
        leave_superperiodic=False,
        use_all_colors=False,
    )
    list_dstructs = se.generate(additional_species=additional_species,
                                additional_frac_coords=additional_frac_coords)

    # with base_site_constraints
    mapping_color_species2 = [DummySpecie("X"), "O", "Sr", "Ti"]
    num_types2 = len(mapping_color_species2)
    base_site_constraints = [
        [2],  # Sr site
        [3],  # Cu site
        [0, 1],  # O or V
        [0, 1],  # O or V
        [0, 1],  # O or V
    ]
    se2 = StructureEnumerator(
        aristo,
        index,
        num_types2,
        mapping_color_species=mapping_color_species2,
        base_site_constraints=base_site_constraints,
        color_exchange=False,
        leave_superperiodic=False,
        use_all_colors=False,
    )
    list_dstructs2 = se2.generate()

    # check uniqueness by StructureMatcher
    stm = StructureMatcher(ltol=1e-4, stol=1e-4)
    grouped = stm.group_structures(list_dstructs + list_dstructs2)
    assert len(grouped) == len(list_dstructs)
    assert all([(len(matched) == 2) for matched in grouped])
예제 #6
0
    def structure(self):
        strained_lattice = perform_strain( self.base.lattice.matrix, self.strain_tensor)
        species, coords = [], []
        for ind, perturb in enumerate(self.atomic_perturbations):
            species.append( self.base.species[ind])
            coords.append( self.base.cart_coords[ind] + np.array(perturb) )

        struct = Structure(strained_lattice, species, coords,
                           coords_are_cartesian=True)

        return struct.copy()
예제 #7
0
def test_coloring_with_fixed_species(sto_perovskite: Structure):
    base_structure = sto_perovskite.copy()
    base_structure.remove_species(["Sr", "Ti"])
    additional_species = sto_perovskite.species[:2]
    additional_frac_coords = sto_perovskite.frac_coords[:2]

    mapping_color_species = [DummySpecie("X"), "O"]
    num_types = len(mapping_color_species)
    index = 2

    se = StructureEnumerator(
        base_structure,
        index,
        num_types,
        mapping_color_species=mapping_color_species,
        color_exchange=False,
        remove_superperiodic=True,
        remove_incomplete=False,
    )
    list_dstructs = se.generate(additional_species=additional_species,
                                additional_frac_coords=additional_frac_coords)

    # with base_site_constraints
    mapping_color_species2 = [DummySpecie("X"), "O", "Sr", "Ti"]
    num_types2 = len(mapping_color_species2)
    base_site_constraints = [
        [2],  # Sr site
        [3],  # Cu site
        [0, 1],  # O or V
        [0, 1],  # O or V
        [0, 1],  # O or V
    ]
    se2 = StructureEnumerator(
        sto_perovskite,
        index,
        num_types2,
        mapping_color_species=mapping_color_species2,
        base_site_constraints=base_site_constraints,
        color_exchange=False,
        remove_superperiodic=True,
        remove_incomplete=False,
    )
    list_dstructs2 = se2.generate()

    # check uniqueness by StructureMatcher
    stm = StructureMatcher(ltol=1e-4, stol=1e-4)
    grouped = stm.group_structures(list_dstructs +
                                   list_dstructs2)  # type: ignore
    assert len(grouped) == len(list_dstructs)
    assert all([(len(matched) == 2) for matched in grouped])
예제 #8
0
    def __init__(self,
                 perfect: Structure,
                 initial: Structure,
                 final: Structure,
                 symprec: float,
                 dist_tol: float,
                 neighbor_cutoff_factor: float = None):

        self.cutoff = neighbor_cutoff_factor or defaults.cutoff_distance_factor
        self.symprec = symprec
        self.perfect, self.initial, self.final = perfect, initial, final
        self.dist_tol = dist_tol

        assert perfect.lattice == initial.lattice == final.lattice
        self.lattice = perfect.lattice

        self._orig_comp = DefectStructureComparator(final, perfect, dist_tol)
        self._orig_center = self._orig_comp.defect_center_coord
        self._calc_drift()

        self.shifted_final = final.copy()
        self.center = tuple(self._orig_center - self._drift_vector)
        for site in self.shifted_final:
            site.frac_coords -= np.array(self._drift_vector)

        self.comp_w_perf = DefectStructureComparator(self.shifted_final,
                                                     perfect, dist_tol)
        self.comp_w_init = DefectStructureComparator(self.shifted_final,
                                                     initial, dist_tol)

        self.defect_structure_info = DefectStructureInfo(
            shifted_final_structure=self.shifted_final,
            initial_site_sym=self.initial_site_sym,
            final_site_sym=self.final_site_sym,
            site_diff=self.comp_w_perf.make_site_diff(),
            site_diff_from_initial=self.comp_w_init.make_site_diff(),
            symprec=symprec,
            dist_tol=dist_tol,
            anchor_atom_idx=self._anchor_atom_idx,
            neighbor_atom_indices=self._neighbor_atom_indices,
            neighbor_cutoff_factor=self.cutoff,
            drift_vector=self._drift_vector,
            drift_dist=self._drift_distance,
            center=self.center,
            displacements=self.calc_displacements())
예제 #9
0
    def __init__(self,
                 structure: Structure,
                 symprec: float = defaults.symmetry_length_tolerance,
                 angle_tolerance: float = defaults.symmetry_angle_tolerance,
                 time_reversal: bool = True,
                 band_mesh_distance: float = defaults.band_mesh_distance):
        """Get full information of seekpath band path.

        Note: site properties such as magmom are removed.

        The structures of aP (SG:1, 2), mC (5, 8, 9, 12, 15) and
        oA (38, 39, 40, 41) can be different between spglib and seekpath.
        see Y. Hinuma et al. Comput. Mater. Sci. 128 (2017) 140–184
        -- spglib mC
         6.048759 -3.479491 0.000000
         6.048759  3.479491 0.000000
        -4.030758  0.000000 6.044512
        -- seekpath mC
         6.048759  3.479491  0.000000
        -6.048759  3.479491  0.000000
        -4.030758  0.000000  6.044512
        """
        self.structure = structure.copy()
        if structure.site_properties:
            logger.warning(
                f"Site property {structure.site_properties.keys()} "
                f"removed in primitive and conventional structures.")
        self.symprec = symprec
        self.angle_tolerance = angle_tolerance
        self.time_reversal = time_reversal
        self.ref_distance = band_mesh_distance
        lattice_matrix = structure.lattice.matrix
        positions = structure.frac_coords.tolist()
        atomic_numbers = [i.specie.number for i in structure.sites]
        self.cell = (lattice_matrix, positions, atomic_numbers)
        # evaluated lazily
        self._spglib_sym_data = None
        self._conventional = None
        self._primitive = None
        self._second_primitive = None
        self._seekpath_data = None
        self._band_primitive = None
        self._irreducible_kpoints = None
예제 #10
0
def quick_view(
    structure: Structure,
    *args,
    conventional: bool = False,
    supercell: list = [1, 1, 1],
    symprec: float = 0.01,
    angle_tolerance: float = 5.0
) -> JsmolView:
    """A function to visualize pymatgen Structure objects in jupyter notebook using jupyter_jsmol package.

    Args:
        structure: pymatgen Structure object.
        *args: Extra arguments for JSmol's load command. Eg. "{2 2 2}", "packed"
        conventional: use conventional cell. Defaults to False.
        supercell: can be used to make supercells with pymatgen.Structure.make_supercell method.
        symprec: If not none, finds the symmetry of the structure
            and writes the cif with symmetry information. Passes symprec
            to the SpacegroupAnalyzer.
        angle_tolerance: Angle tolerance for symmetry finding. Passes
            angle_tolerance to the SpacegroupAnalyzer. Used only if symprec
            is not None.

    Returns:
        A jupyter widget object.
    """

    s = structure.copy()
    if conventional:
        spga = SpacegroupAnalyzer(s, symprec=symprec, angle_tolerance=angle_tolerance)
        s = spga.get_conventional_standard_structure()

    cif = CifWriter(
        s, symprec=symprec, angle_tolerance=angle_tolerance, refine_struct=False
    )

    supercell_str = "{" + " ".join(map(str, supercell)) + "}"

    return JsmolView.from_str(str(cif), supercell_str, *args)
예제 #11
0
    def get_sqrt2_1_struct(self):
        """Setup a sqrt 2 x sqrt 2 x 1 structure"""

        orig_scaled_mat = np.array([[self.lattconst * np.sqrt(2), 0., 0.],
                                    [0., self.lattconst * np.sqrt(2), 0.],
                                    [0., 0., self.lattconst]])
        rotation = np.array([[np.cos(45. * np.pi / 180.), -np.sin(45. * np.pi / 180.), 0],
                             [np.sin(45. * np.pi / 180.), np.cos(45. * np.pi / 180.), 0],
                             [0., 0., 1.]])
        rotated_mat = np.dot(orig_scaled_mat, rotation)
        lattice = Lattice(rotated_mat)
        species = [self.eltA, self.eltA,
                   self.eltB, self.eltB,
                   self.eltC, self.eltC, self.eltC,
                   self.eltC, self.eltC, self.eltC]
        coords = [[0.5, 0., 0.5], [0., 0.5, 0.5],
                  [0., 0., 0.], [0.5, 0.5, 0.],
                  [0.25, 0.25, 0.], [0.75, 0.75, 0.], [0.25, 0.75, 0.],
                  [0.75, 0.25, 0.], [0., 0., 0.5], [0.5, 0.5, 0.5]]

        struct = Structure(lattice, species, coords,
                           coords_are_cartesian=False)

        return struct.copy()
예제 #12
0
class CollinearMagneticStructureAnalyzerTest(unittest.TestCase):
    def setUp(self):
        parser = CifParser(os.path.join(PymatgenTest.TEST_FILES_DIR, "Fe.cif"))
        self.Fe = parser.get_structures()[0]

        parser = CifParser(
            os.path.join(PymatgenTest.TEST_FILES_DIR, "LiFePO4.cif"))
        self.LiFePO4 = parser.get_structures()[0]

        parser = CifParser(
            os.path.join(PymatgenTest.TEST_FILES_DIR, "Fe3O4.cif"))
        self.Fe3O4 = parser.get_structures()[0]

        parser = CifParser(
            os.path.join(PymatgenTest.TEST_FILES_DIR,
                         "magnetic.ncl.example.GdB4.mcif"))
        self.GdB4 = parser.get_structures()[0]

        parser = CifParser(
            os.path.join(PymatgenTest.TEST_FILES_DIR,
                         "magnetic.example.NiO.mcif"))
        self.NiO_expt = parser.get_structures()[0]

        latt = Lattice.cubic(4.17)
        species = ["Ni", "O"]
        coords = [[0, 0, 0], [0.5, 0.5, 0.5]]
        self.NiO = Structure.from_spacegroup(225, latt, species, coords)

        latt = Lattice([[2.085, 2.085, 0.0], [0.0, -2.085, -2.085],
                        [-2.085, 2.085, -4.17]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0, 0.5], [0, 0, 0], [0.25, 0.5, 0.25],
                  [0.75, 0.5, 0.75]]
        self.NiO_AFM_111 = Structure(latt,
                                     species,
                                     coords,
                                     site_properties={"magmom": [-5, 5, 0, 0]})

        latt = Lattice([[2.085, 2.085, 0], [0, 0, -4.17], [-2.085, 2.085, 0]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0.5, 0.5], [0, 0, 0], [0, 0.5, 0], [0.5, 0, 0.5]]
        self.NiO_AFM_001 = Structure(latt,
                                     species,
                                     coords,
                                     site_properties={"magmom": [-5, 5, 0, 0]})

        latt = Lattice([[2.085, 2.085, 0], [0, 0, -4.17], [-2.085, 2.085, 0]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0.5, 0.5], [0, 0, 0], [0, 0.5, 0], [0.5, 0, 0.5]]
        self.NiO_AFM_001_opposite = Structure(
            latt, species, coords, site_properties={"magmom": [5, -5, 0, 0]})

        latt = Lattice([[2.085, 2.085, 0], [0, 0, -4.17], [-2.085, 2.085, 0]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0.5, 0.5], [0, 0, 0], [0, 0.5, 0], [0.5, 0, 0.5]]
        self.NiO_unphysical = Structure(
            latt, species, coords, site_properties={"magmom": [-3, 0, 0, 0]})

        warnings.simplefilter("ignore")

    def tearDown(self):
        warnings.simplefilter("default")

    def test_get_representations(self):
        # tests to convert between storing magnetic moment information
        # on site_properties or on Species 'spin' property

        # test we store magnetic moments on site properties
        self.Fe.add_site_property("magmom", [5])
        msa = CollinearMagneticStructureAnalyzer(self.Fe)
        self.assertEqual(msa.structure.site_properties["magmom"][0], 5)

        # and that we can retrieve a spin representation
        Fe_spin = msa.get_structure_with_spin()
        self.assertFalse("magmom" in Fe_spin.site_properties)
        self.assertEqual(Fe_spin[0].specie.spin, 5)

        # test we can remove magnetic moment information
        msa.get_nonmagnetic_structure()
        self.assertFalse("magmom" in Fe_spin.site_properties)

        # test with disorder on magnetic site
        self.Fe[0] = {
            Species("Fe", oxidation_state=0, properties={"spin": 5}): 0.5,
            "Ni": 0.5,
        }
        self.assertRaises(NotImplementedError,
                          CollinearMagneticStructureAnalyzer, self.Fe)

    def test_matches(self):
        self.assertTrue(self.NiO.matches(self.NiO_AFM_111))
        self.assertTrue(self.NiO.matches(self.NiO_AFM_001))

        # MSA adds magmoms to Structure, so not equal
        msa = CollinearMagneticStructureAnalyzer(
            self.NiO, overwrite_magmom_mode="replace_all")
        self.assertFalse(msa.matches_ordering(self.NiO))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_111))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_001))

        msa = CollinearMagneticStructureAnalyzer(
            self.NiO_AFM_001, overwrite_magmom_mode="respect_sign")
        self.assertFalse(msa.matches_ordering(self.NiO))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_111))
        self.assertTrue(msa.matches_ordering(self.NiO_AFM_001))
        self.assertTrue(msa.matches_ordering(self.NiO_AFM_001_opposite))

        msa = CollinearMagneticStructureAnalyzer(
            self.NiO_AFM_111, overwrite_magmom_mode="respect_sign")
        self.assertFalse(msa.matches_ordering(self.NiO))
        self.assertTrue(msa.matches_ordering(self.NiO_AFM_111))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_001))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_001_opposite))

    def test_modes(self):
        mode = "none"
        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [0, 0])

        mode = "respect_sign"
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [-5, 0, 0, 0])

        mode = "respect_zeros"
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [5, 0, 0, 0])

        mode = "replace_all"
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical,
                                                 overwrite_magmom_mode=mode,
                                                 make_primitive=False)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [5, 5, 0, 0])

        mode = "replace_all_if_undefined"
        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [5, 0])

        mode = "normalize"
        msa = CollinearMagneticStructureAnalyzer(
            msa.structure, overwrite_magmom_mode="normalize")
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [1, 0])

    def test_net_positive(self):
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [3, 0, 0, 0])

    def test_get_ferromagnetic_structure(self):
        msa = CollinearMagneticStructureAnalyzer(
            self.NiO, overwrite_magmom_mode="replace_all_if_undefined")
        s1 = msa.get_ferromagnetic_structure()
        s1_magmoms = [float(m) for m in s1.site_properties["magmom"]]
        s1_magmoms_ref = [5.0, 0.0]
        self.assertListEqual(s1_magmoms, s1_magmoms_ref)

        _ = CollinearMagneticStructureAnalyzer(
            self.NiO_AFM_111, overwrite_magmom_mode="replace_all_if_undefined")
        s2 = msa.get_ferromagnetic_structure(make_primitive=False)
        s2_magmoms = [float(m) for m in s2.site_properties["magmom"]]
        s2_magmoms_ref = [5.0, 0.0]
        self.assertListEqual(s2_magmoms, s2_magmoms_ref)

        s2_prim = msa.get_ferromagnetic_structure(make_primitive=True)
        self.assertTrue(
            CollinearMagneticStructureAnalyzer(s1).matches_ordering(s2_prim))

    def test_magnetic_properties(self):
        msa = CollinearMagneticStructureAnalyzer(self.GdB4)
        self.assertFalse(msa.is_collinear)

        msa = CollinearMagneticStructureAnalyzer(self.Fe)
        self.assertFalse(msa.is_magnetic)

        self.Fe.add_site_property("magmom", [5])

        msa = CollinearMagneticStructureAnalyzer(self.Fe)
        self.assertTrue(msa.is_magnetic)
        self.assertTrue(msa.is_collinear)
        self.assertEqual(msa.ordering, Ordering.FM)

        msa = CollinearMagneticStructureAnalyzer(
            self.NiO,
            make_primitive=False,
            overwrite_magmom_mode="replace_all_if_undefined",
        )
        self.assertEqual(msa.number_of_magnetic_sites, 4)
        self.assertEqual(msa.number_of_unique_magnetic_sites(), 1)
        self.assertEqual(msa.types_of_magnetic_species, (Element.Ni, ))
        self.assertEqual(msa.get_exchange_group_info(), ("Fm-3m", 225))

    def test_str(self):
        msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_001)

        ref_msa_str = """Structure Summary
Lattice
    abc : 2.948635277547903 4.17 2.948635277547903
 angles : 90.0 90.0 90.0
 volume : 36.2558565
      A : 2.085 2.085 0.0
      B : 0.0 0.0 -4.17
      C : -2.085 2.085 0.0
Magmoms Sites
+5.00   PeriodicSite: Ni (0.0000, 0.0000, 0.0000) [0.0000, 0.0000, 0.0000]
        PeriodicSite: O (0.0000, 0.0000, -2.0850) [0.0000, 0.5000, 0.0000]
        PeriodicSite: O (0.0000, 2.0850, 0.0000) [0.5000, 0.0000, 0.5000]
-5.00   PeriodicSite: Ni (0.0000, 2.0850, -2.0850) [0.5000, 0.5000, 0.5000]"""

        # just compare lines form 'Magmoms Sites',
        # since lattice param string can vary based on machine precision
        self.assertEqual(
            "\n".join(str(msa).split("\n")[-5:-1]),
            "\n".join(ref_msa_str.split("\n")[-5:-1]),
        )

    def test_round_magmoms(self):
        struct = self.NiO_AFM_001.copy()
        struct.add_site_property("magmom", [-5.0143, -5.02, 0.147, 0.146])

        msa = CollinearMagneticStructureAnalyzer(struct,
                                                 round_magmoms=0.001,
                                                 make_primitive=False)
        self.assertTrue(
            np.allclose(msa.magmoms, [5.0171, 5.0171, -0.1465, -0.1465]))
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"], 5.0171)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["O"], 0.1465)

        struct.add_site_property("magmom", [-5.0143, 4.5, 0.147, 0.146])
        msa = CollinearMagneticStructureAnalyzer(struct,
                                                 round_magmoms=0.001,
                                                 make_primitive=False)
        self.assertTrue(
            np.allclose(msa.magmoms, [5.0143, -4.5, -0.1465, -0.1465]))
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"][0], 4.5)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"][1],
                               5.0143)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["O"], 0.1465)
예제 #13
0
def get_start_end_structures(
    isite: PeriodicSite,
    esite: PeriodicSite,
    base_struct: Structure,
    sc_mat: List[List[Union[int, float]]],
    vac_mode: bool,
    debug: bool = False,
) -> Tuple[Structure, Structure, Structure]:
    """
    Obtain the starting and terminating structures in a supercell for NEB calculations.

    Args:
        hop: object presenting the migration event
        base_struct: unit cell representation of the structure
        sc_mat: supercell transformation to create the simulation cell for the NEB calc

    Returns:
        initial structure, final structure, empty structure all in the supercell
    """
    def remove_site_at_pos(structure: Structure, site: PeriodicSite):
        new_struct_sites = []
        for isite in structure:
            if not vac_mode or (isite.distance(site) <= 1e-8):
                continue
            new_struct_sites.append(isite)
        return Structure.from_sites(new_struct_sites)

    base_sc = base_struct.copy() * sc_mat

    start_struct = base_struct.copy() * sc_mat
    end_struct = base_struct.copy() * sc_mat

    sc_mat_inv = np.linalg.inv(sc_mat)

    if not vac_mode:
        # insertion the endpoints
        start_struct.insert(
            0,
            esite.species_string,
            np.dot(isite.frac_coords, sc_mat_inv),
            properties={"magmom": 0},
        )
        end_struct.insert(
            0,
            esite.species_string,
            np.dot(esite.frac_coords, sc_mat_inv),
            properties={"magmom": 0},
        )
    else:
        # remove the other endpoint
        ipos_sc = np.dot(isite.frac_coords, sc_mat_inv)
        epos_sc = np.dot(esite.frac_coords, sc_mat_inv)
        if debug:
            icart = base_sc.lattice.get_cartesian_coords(ipos_sc)
            ecart = base_sc.lattice.get_cartesian_coords(epos_sc)
            assert abs(
                np.linalg.norm(icart - ecart) -
                np.linalg.norm(isite.coords - esite.coords)) < 1e-5
        i_ref_ = PeriodicSite(species=esite.species_string,
                              coords=ipos_sc,
                              lattice=base_sc.lattice)
        e_ref_ = PeriodicSite(species=esite.species_string,
                              coords=epos_sc,
                              lattice=base_sc.lattice)
        start_struct = remove_site_at_pos(start_struct, e_ref_)
        end_struct = remove_site_at_pos(end_struct, i_ref_)
    return start_struct, end_struct, base_sc
예제 #14
0
class CollinearMagneticStructureAnalyzerTest(unittest.TestCase):

    def setUp(self):
        parser = CifParser(os.path.join(test_dir, 'Fe.cif'))
        self.Fe = parser.get_structures()[0]

        parser = CifParser(os.path.join(test_dir, 'LiFePO4.cif'))
        self.LiFePO4 = parser.get_structures()[0]

        parser = CifParser(os.path.join(test_dir, 'Fe3O4.cif'))
        self.Fe3O4 = parser.get_structures()[0]

        parser = CifParser(os.path.join(test_dir, 'magnetic.ncl.example.GdB4.mcif'))
        self.GdB4 = parser.get_structures()[0]

        parser = CifParser(os.path.join(test_dir, 'magnetic.example.NiO.mcif'))
        self.NiO_expt = parser.get_structures()[0]

        latt = Lattice.cubic(4.17)
        species = ["Ni", "O"]
        coords = [[0, 0, 0],
                  [0.5, 0.5, 0.5]]
        self.NiO = Structure.from_spacegroup(225, latt, species, coords)

        latt = Lattice([[2.085, 2.085, 0.0],
                        [0.0, -2.085, -2.085],
                        [-2.085, 2.085, -4.17]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0, 0.5],
                  [0, 0, 0],
                  [0.25, 0.5, 0.25],
                  [0.75, 0.5, 0.75]]
        self.NiO_AFM_111 = Structure(latt, species, coords,
                                     site_properties={'magmom': [-5, 5, 0, 0]})

        latt = Lattice([[2.085, 2.085, 0],
                        [0, 0, -4.17],
                        [-2.085, 2.085, 0]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0.5, 0.5],
                  [0, 0, 0],
                  [0, 0.5, 0],
                  [0.5, 0, 0.5]]
        self.NiO_AFM_001 = Structure(latt, species, coords,
                                     site_properties={'magmom': [-5, 5, 0, 0]})

        latt = Lattice([[2.085, 2.085, 0],
                        [0, 0, -4.17],
                        [-2.085, 2.085, 0]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0.5, 0.5],
                  [0, 0, 0],
                  [0, 0.5, 0],
                  [0.5, 0, 0.5]]
        self.NiO_AFM_001_opposite = Structure(latt, species, coords,
                                              site_properties={'magmom': [5, -5, 0, 0]})

        latt = Lattice([[2.085, 2.085, 0],
                        [0, 0, -4.17],
                        [-2.085, 2.085, 0]])
        species = ["Ni", "Ni", "O", "O"]
        coords = [[0.5, 0.5, 0.5],
                  [0, 0, 0],
                  [0, 0.5, 0],
                  [0.5, 0, 0.5]]
        self.NiO_unphysical = Structure(latt, species, coords,
                                        site_properties={'magmom': [-3, 0, 0, 0]})

        warnings.simplefilter("ignore")

    def tearDown(self):
        warnings.simplefilter("default")

    def test_get_representations(self):

        # tests to convert between storing magnetic moment information
        # on site_properties or on Specie 'spin' property

        # test we store magnetic moments on site properties
        self.Fe.add_site_property('magmom', [5])
        msa = CollinearMagneticStructureAnalyzer(self.Fe)
        self.assertEqual(msa.structure.site_properties['magmom'][0], 5)

        # and that we can retrieve a spin representaiton
        Fe_spin = msa.get_structure_with_spin()
        self.assertFalse('magmom' in Fe_spin.site_properties)
        self.assertEqual(Fe_spin[0].specie.spin, 5)

        # test we can remove magnetic moment information
        Fe_none = msa.get_nonmagnetic_structure()
        self.assertFalse('magmom' in Fe_spin.site_properties)

        # test with disorder on magnetic site
        self.Fe[0] = {Specie('Fe', oxidation_state=0, properties={'spin': 5}): 0.5, 'Ni': 0.5}
        self.assertRaises(NotImplementedError, CollinearMagneticStructureAnalyzer, self.Fe)

    def test_matches(self):

        self.assertTrue(self.NiO.matches(self.NiO_AFM_111))
        self.assertTrue(self.NiO.matches(self.NiO_AFM_001))

        # MSA adds magmoms to Structure, so not equal
        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode="replace_all")
        self.assertFalse(msa.matches_ordering(self.NiO))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_111))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_001))

        msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_001,
                                                 overwrite_magmom_mode="respect_sign")
        self.assertFalse(msa.matches_ordering(self.NiO))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_111))
        self.assertTrue(msa.matches_ordering(self.NiO_AFM_001))
        self.assertTrue(msa.matches_ordering(self.NiO_AFM_001_opposite))

        msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_111,
                                                 overwrite_magmom_mode="respect_sign")
        self.assertFalse(msa.matches_ordering(self.NiO))
        self.assertTrue(msa.matches_ordering(self.NiO_AFM_111))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_001))
        self.assertFalse(msa.matches_ordering(self.NiO_AFM_001_opposite))

    def test_modes(self):

        mode = "none"
        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties['magmom']
        self.assertEqual(magmoms, [0, 0])

        mode = "respect_sign"
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties['magmom']
        self.assertEqual(magmoms, [-5, 0, 0, 0])

        mode = "respect_zeros"
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties['magmom']
        self.assertEqual(magmoms, [5, 0, 0, 0])

        mode = "replace_all"
        msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical,
                                                 overwrite_magmom_mode=mode,
                                                 make_primitive=False)
        magmoms = msa.structure.site_properties['magmom']
        self.assertEqual(magmoms, [5, 5, 0, 0])

        mode = "replace_all_if_undefined"
        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties['magmom']
        self.assertEqual(magmoms, [5, 0])

        mode = "normalize"
        msa = CollinearMagneticStructureAnalyzer(msa.structure,
                                                 overwrite_magmom_mode='normalize')
        magmoms = msa.structure.site_properties['magmom']
        self.assertEqual(magmoms, [1, 0])

    def test_get_ferromagnetic_structure(self):

        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode="replace_all_if_undefined")
        s1 = msa.get_ferromagnetic_structure()
        s1_magmoms = [float(m) for m in s1.site_properties['magmom']]
        s1_magmoms_ref = [5.0, 0.0]
        self.assertListEqual(s1_magmoms, s1_magmoms_ref)

        msa2 = CollinearMagneticStructureAnalyzer(self.NiO_AFM_111,
                                                 overwrite_magmom_mode="replace_all_if_undefined")
        s2 = msa.get_ferromagnetic_structure(make_primitive=False)
        s2_magmoms = [float(m) for m in s2.site_properties['magmom']]
        s2_magmoms_ref = [5.0, 0.0]
        self.assertListEqual(s2_magmoms, s2_magmoms_ref)

        s2_prim = msa.get_ferromagnetic_structure(make_primitive=True)
        self.assertTrue(CollinearMagneticStructureAnalyzer(s1).matches_ordering(s2_prim))

    def test_magnetic_properties(self):

        msa = CollinearMagneticStructureAnalyzer(self.GdB4)
        self.assertFalse(msa.is_collinear)

        msa = CollinearMagneticStructureAnalyzer(self.Fe)
        self.assertFalse(msa.is_magnetic)

        self.Fe.add_site_property('magmom', [5])

        msa = CollinearMagneticStructureAnalyzer(self.Fe)
        self.assertTrue(msa.is_magnetic)
        self.assertTrue(msa.is_collinear)
        self.assertEqual(msa.ordering, Ordering.FM)

        msa = CollinearMagneticStructureAnalyzer(self.NiO, make_primitive=False,
                                                 overwrite_magmom_mode="replace_all_if_undefined")
        self.assertEqual(msa.number_of_magnetic_sites, 4)
        self.assertEqual(msa.number_of_unique_magnetic_sites(), 1)
        self.assertEqual(msa.types_of_magnetic_specie, [Element('Ni')])
        self.assertEqual(msa.get_exchange_group_info(), ('Fm-3m', 225))

    def test_str(self):

        msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_001)

        ref_msa_str = """Structure Summary
Lattice
    abc : 2.948635277547903 4.17 2.948635277547903
 angles : 90.0 90.0 90.0
 volume : 36.2558565
      A : 2.085 2.085 0.0
      B : 0.0 0.0 -4.17
      C : -2.085 2.085 0.0
Magmoms Sites
+5.00   PeriodicSite: Ni (0.0000, 0.0000, 0.0000) [0.0000, 0.0000, 0.0000]
        PeriodicSite: O (0.0000, 0.0000, -2.0850) [0.0000, 0.5000, 0.0000]
        PeriodicSite: O (0.0000, 2.0850, 0.0000) [0.5000, 0.0000, 0.5000]
-5.00   PeriodicSite: Ni (0.0000, 2.0850, -2.0850) [0.5000, 0.5000, 0.5000]"""

        # just compare lines form 'Magmoms Sites',
        # since lattice param string can vary based on machine precision
        self.assertEqual("\n".join(str(msa).split("\n")[-5:-1]),
                         "\n".join(ref_msa_str.split("\n")[-5:-1]))

    def test_round_magmoms(self):

        struct = self.NiO_AFM_001.copy()
        struct.add_site_property('magmom', [-5.0143, -5.02, 0.147, 0.146])

        msa = CollinearMagneticStructureAnalyzer(struct, round_magmoms=0.001, make_primitive=False)
        self.assertTrue(np.allclose(msa.magmoms, [-5.0171, -5.0171, 0.1465, 0.1465]))
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms['Ni'], 5.0171)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms['O'], 0.1465)

        struct.add_site_property('magmom', [-5.0143, 4.5, 0.147, 0.146])
        msa = CollinearMagneticStructureAnalyzer(struct, round_magmoms=0.001, make_primitive=False)
        self.assertTrue(np.allclose(msa.magmoms, [-5.0143, 4.5, 0.1465, 0.1465]))
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms['Ni'][0], 4.5)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms['Ni'][1], 5.0143)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms['O'], 0.1465)
예제 #15
0
    def test_supercell_subsets(self):
        sm = StructureMatcher(ltol=0.2,
                              stol=0.3,
                              angle_tol=5,
                              primitive_cell=False,
                              scale=True,
                              attempt_supercell=True,
                              allow_subset=True,
                              supercell_size='volume')
        sm_no_s = StructureMatcher(ltol=0.2,
                                   stol=0.3,
                                   angle_tol=5,
                                   primitive_cell=False,
                                   scale=True,
                                   attempt_supercell=True,
                                   allow_subset=False,
                                   supercell_size='volume')
        l = Lattice.orthorhombic(1, 2, 3)
        s1 = Structure(l, ['Ag', 'Si', 'Si'],
                       [[.7, .4, .5], [0, 0, 0.1], [0, 0, 0.2]])
        s1.make_supercell([2, 1, 1])
        s2 = Structure(l, ['Si', 'Si', 'Ag'],
                       [[0, 0.1, -0.95], [0, 0.1, 0], [-.7, .5, .375]])

        shuffle = [0, 2, 1, 3, 4, 5]
        s1 = Structure.from_sites([s1[i] for i in shuffle])

        #test when s1 is exact supercell of s2
        result = sm.get_s2_like_s1(s1, s2)
        for a, b in zip(s1, result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species_and_occu, b.species_and_occu)

        self.assertTrue(sm.fit(s1, s2))
        self.assertTrue(sm.fit(s2, s1))
        self.assertTrue(sm_no_s.fit(s1, s2))
        self.assertTrue(sm_no_s.fit(s2, s1))

        rms = (0.048604032430991401, 0.059527539448807391)
        self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms))

        #test when the supercell is a subset of s2
        subset_supercell = s1.copy()
        del subset_supercell[0]
        result = sm.get_s2_like_s1(subset_supercell, s2)
        self.assertEqual(len(result), 6)
        for a, b in zip(subset_supercell, result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species_and_occu, b.species_and_occu)

        self.assertTrue(sm.fit(subset_supercell, s2))
        self.assertTrue(sm.fit(s2, subset_supercell))
        self.assertFalse(sm_no_s.fit(subset_supercell, s2))
        self.assertFalse(sm_no_s.fit(s2, subset_supercell))

        rms = (0.053243049896333279, 0.059527539448807336)
        self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2),
                                    rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell),
                                    rms))

        #test when s2 (once made a supercell) is a subset of s1
        s2_missing_site = s2.copy()
        del s2_missing_site[1]
        result = sm.get_s2_like_s1(s1, s2_missing_site)
        for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species_and_occu, b.species_and_occu)

        self.assertTrue(sm.fit(s1, s2_missing_site))
        self.assertTrue(sm.fit(s2_missing_site, s1))
        self.assertFalse(sm_no_s.fit(s1, s2_missing_site))
        self.assertFalse(sm_no_s.fit(s2_missing_site, s1))

        rms = (0.029763769724403633, 0.029763769724403987)
        self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
예제 #16
0
    def test_supercell_subsets(self):
        sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5,
                              primitive_cell=False, scale=True,
                              attempt_supercell=True, allow_subset=True,
                              supercell_size='volume')
        sm_no_s = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5,
                              primitive_cell=False, scale=True,
                              attempt_supercell=True, allow_subset=False,
                              supercell_size='volume')
        l = Lattice.orthorhombic(1, 2, 3)
        s1 = Structure(l, ['Ag', 'Si', 'Si'],
                       [[.7,.4,.5],[0,0,0.1],[0,0,0.2]])
        s1.make_supercell([2,1,1])
        s2 = Structure(l, ['Si', 'Si', 'Ag'],
                       [[0,0.1,-0.95],[0,0.1,0],[-.7,.5,.375]])

        shuffle = [0,2,1,3,4,5]
        s1 = Structure.from_sites([s1[i] for i in shuffle])

        #test when s1 is exact supercell of s2
        result = sm.get_s2_like_s1(s1, s2)
        for a, b in zip(s1, result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species_and_occu, b.species_and_occu)

        self.assertTrue(sm.fit(s1, s2))
        self.assertTrue(sm.fit(s2, s1))
        self.assertTrue(sm_no_s.fit(s1, s2))
        self.assertTrue(sm_no_s.fit(s2, s1))

        rms = (0.048604032430991401, 0.059527539448807391)
        self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms))

        #test when the supercell is a subset of s2
        subset_supercell = s1.copy()
        del subset_supercell[0]
        result = sm.get_s2_like_s1(subset_supercell, s2)
        self.assertEqual(len(result), 6)
        for a, b in zip(subset_supercell, result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species_and_occu, b.species_and_occu)

        self.assertTrue(sm.fit(subset_supercell, s2))
        self.assertTrue(sm.fit(s2, subset_supercell))
        self.assertFalse(sm_no_s.fit(subset_supercell, s2))
        self.assertFalse(sm_no_s.fit(s2, subset_supercell))

        rms = (0.053243049896333279, 0.059527539448807336)
        self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms))

        #test when s2 (once made a supercell) is a subset of s1
        s2_missing_site = s2.copy()
        del s2_missing_site[1]
        result = sm.get_s2_like_s1(s1, s2_missing_site)
        for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species_and_occu, b.species_and_occu)

        self.assertTrue(sm.fit(s1, s2_missing_site))
        self.assertTrue(sm.fit(s2_missing_site, s1))
        self.assertFalse(sm_no_s.fit(s1, s2_missing_site))
        self.assertFalse(sm_no_s.fit(s2_missing_site, s1))

        rms = (0.029763769724403633, 0.029763769724403987)
        self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
예제 #17
0
import numpy as np
from pymatgen.core import Lattice, Structure
from pymatgen.core.periodic_table import Specie, DummySpecie
from pymatgen.analysis.structure_matcher import StructureMatcher

from dsenum import StructureEnumerator
from dsenum.utils import write_cif, refine_and_resize_structure

if __name__ == "__main__":
    lattice = Lattice(3.945 * np.eye(3))
    species = ["Sr", "Ti", "O", "O", "O"]
    frac_coords = np.array([[0, 0, 0], [0.5, 0.5, 0.5], [0.0, 0.5, 0.5],
                            [0.5, 0.0, 0.5], [0.5, 0.5, 0.0]])
    aristo = Structure(lattice, species, frac_coords)
    base_structure = aristo.copy()
    base_structure.remove_species(["Sr", "Ti"])
    additional_species = species[:2]
    additional_frac_coords = frac_coords[:2]

    mapping_color_species = [DummySpecie("X"), "O"]
    num_types = len(mapping_color_species)
    index = 2

    se = StructureEnumerator(
        base_structure,
        index,
        num_types,
        mapping_color_species=mapping_color_species,
        color_exchange=False,
        leave_superperiodic=False,