Beispiel #1
0
    def operate_magmom(self, magmom):
        """
        Apply time reversal operator on the magnetic moment. Note that
        magnetic moments transform as axial vectors, not polar vectors.

        See 'Symmetry and magnetic structures', Rodríguez-Carvajal and
        Bourée for a good discussion. DOI: 10.1051/epjconf/20122200010

        Args:
            magmom: Magnetic moment as electronic_structure.core.Magmom
            class or as list or np array-like

        Returns:
            Magnetic moment after operator applied as Magmom class
        """

        magmom = Magmom(magmom)  # type casting to handle lists as input

        transformed_moment = (self.apply_rotation_only(magmom.global_moment) *
                              np.linalg.det(self.rotation_matrix) *
                              self.time_reversal)

        # retains input spin axis if different from default
        return Magmom.from_global_moment_and_saxis(transformed_moment,
                                                   magmom.saxis)
Beispiel #2
0
 def _unique_coords(self, coords_in, magmoms_in=None):
     """
     Generate unique coordinates using coord and symmetry positions
     and also their corresponding magnetic moments, if supplied.
     """
     coords = []
     if magmoms_in:
         magmoms = []
         magmoms_in = [Magmom(magmom) for magmom in magmoms_in]
         if len(magmoms_in) != len(coords_in):
             raise ValueError
         for tmp_coord, tmp_magmom in zip(coords_in, magmoms_in):
             for op in self.symmetry_operations:
                 coord = op.operate(tmp_coord)
                 coord = np.array([i - math.floor(i) for i in coord])
                 if isinstance(op, MagSymmOp):
                     magmom = Magmom(op.operate_magmom(tmp_magmom.moment))
                 else:
                     magmom = tmp_magmom
                 if not in_coord_list_pbc(
                         coords, coord, atol=self._site_tolerance):
                     coords.append(coord)
                     magmoms.append(magmom)
         return coords, magmoms
     else:
         for tmp_coord in coords_in:
             for op in self.symmetry_operations:
                 coord = op.operate(tmp_coord)
                 coord = np.array([i - math.floor(i) for i in coord])
                 if not in_coord_list_pbc(
                         coords, coord, atol=self._site_tolerance):
                     coords.append(coord)
         return coords, [Magmom(0)] * len(coords)  # return dummy magmoms
Beispiel #3
0
    def test_get_moments(self):

        # simple cases
        magmom_along_x = Magmom([1, 0, 0])
        self.assertTrue(np.allclose(magmom_along_x.get_moment(saxis=[1, 0, 0]), [0, 0, 1]))

        magmom_along_y = Magmom([0, 1, 0])
        self.assertTrue(np.allclose(magmom_along_y.get_moment(saxis=[0, 1, 0]), [0, 0, 1]))

        # test transformations
        magmoms = [[0, 0, 0],
                   [0, 0, 1],
                   [0, 0, -1],
                   [1, 2, 3],
                   [-1, 2, 3],
                   [-1, -2, -3]]

        for magmom in magmoms:
            magmom1 = Magmom(magmom)
            # transform to non-default saxis
            magmom2 = magmom1.get_00t_magmom_with_xyz_saxis()
            # and back to default saxis
            magmom3 = magmom2.get_xyz_magmom_with_001_saxis()
            self.assertTrue(np.allclose(magmom1.moment, magmom))
            self.assertTrue(np.allclose(magmom1.saxis, [0, 0, 1]))
            self.assertTrue(np.allclose(magmom1.get_moment(saxis=magmom1.saxis), magmom1.moment))
            self.assertTrue(np.allclose(magmom1.get_moment(saxis=magmom2.saxis), magmom2.moment))
            self.assertTrue(np.allclose(magmom2.get_moment(saxis=[0, 0, 1]), magmom1.moment))
            self.assertTrue(np.allclose(magmom2.get_moment(saxis=magmom2.saxis), magmom2.moment))
            self.assertTrue(np.allclose(magmom3.moment, magmom1.moment))
Beispiel #4
0
 def test_as_dict_and_from_dict(self):
     d = self.incar.as_dict()
     incar2 = Incar.from_dict(d)
     self.assertEqual(self.incar, incar2)
     d["MAGMOM"] = [Magmom([1, 2, 3]).as_dict()]
     incar3 = Incar.from_dict(d)
     self.assertEqual(incar3["MAGMOM"], [Magmom([1, 2, 3])])
Beispiel #5
0
 def test_have_consistent_saxis(self):
     magmom1 = Magmom([1, 2, 3])
     magmom2 = Magmom([1, 2, 3])
     magmom3 = Magmom([1, 2, 3], saxis=[0, 0, -1])
     magmom4 = Magmom([1, 2, 3], saxis=[1, 2, 3])
     self.assertTrue(Magmom.have_consistent_saxis([magmom1, magmom2]))
     self.assertFalse(Magmom.have_consistent_saxis([magmom1, magmom3]))
     self.assertFalse(Magmom.have_consistent_saxis([magmom1, magmom4]))
Beispiel #6
0
    def test_get_consistent_set_and_saxis(self):
        magmoms = [1, 1, 2, 2, 0, 0, 2]
        magmoms, saxis = Magmom.get_consistent_set_and_saxis(magmoms)
        self.assertTrue(np.allclose(saxis, [0, 0, 1]))

        magmoms = [[0, 0, 0], [1, 1, 1], [2, 2, 2]]
        magmoms, saxis = Magmom.get_consistent_set_and_saxis(magmoms)
        self.assertTrue(np.allclose(saxis, [np.sqrt(1 / 3.0)] * 3))
Beispiel #7
0
 def test_is_collinear(self):
     magmoms_list = [[0, 0, 0], [1, 1, 1], [[0, 0, 0], [0, 0, 0], [0, 0,
                                                                   0]],
                     [[0, 0, 1], [0, 0, 1], [0, 0, 1]],
                     [[0, 0, -1], [0, 0, 1], [0, 0, 1]],
                     [[2, 2, 2], [-2, -2, -2], [2, 2, 2]]]
     for magmoms in magmoms_list:
         self.assertEqual(Magmom.are_collinear(magmoms), True)
     ncl_magmoms = [[[0, 0, 1], [0, 0, 1], [1, 2, 3]]]
     self.assertEqual(Magmom.are_collinear(ncl_magmoms), False)
Beispiel #8
0
    def test_get_consistent_set_and_saxis(self):
        magmoms = [1, 1, 2, 2, 0, 0, 2]
        magmoms, saxis = Magmom.get_consistent_set_and_saxis(magmoms)
        self.assertTrue(np.allclose(saxis, [0, 0, 1]))

        magmoms = [[0, 0, 0],
                   [1, 1, 1],
                   [2, 2, 2]]
        magmoms, saxis = Magmom.get_consistent_set_and_saxis(magmoms)
        self.assertTrue(np.allclose(saxis, [np.sqrt(1/3.)]*3))
Beispiel #9
0
 def test_is_collinear(self):
     magmoms_list = [[0, 0, 0],
                     [1, 1, 1],
                     [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
                     [[0, 0, 1], [0, 0, 1], [0, 0, 1]],
                     [[0, 0, -1], [0, 0, 1], [0, 0, 1]],
                     [[2, 2, 2], [-2, -2, -2], [2, 2, 2]]]
     for magmoms in magmoms_list:
         self.assertEqual(Magmom.are_collinear(magmoms), True)
     ncl_magmoms = [[[0, 0, 1], [0, 0, 1], [1, 2, 3]]]
     self.assertEqual(Magmom.are_collinear(ncl_magmoms), False)
Beispiel #10
0
    def test_get_structures(self):

        # incommensurate structures not currently supported
        self.assertRaises(NotImplementedError, self.mcif_incom.get_structures)

        # disordered magnetic structures not currently supported
        self.assertRaises(NotImplementedError, self.mcif_disord.get_structures)

        # taken from self.mcif_ncl, removing explicit magnetic symmops
        # so that MagneticSymmetryGroup() has to be invoked
        magcifstr = """
data_5yOhtAoR

_space_group.magn_name_BNS     "P 4/m' b' m' "
_cell_length_a                 7.1316
_cell_length_b                 7.1316
_cell_length_c                 4.0505
_cell_angle_alpha              90.00
_cell_angle_beta               90.00
_cell_angle_gamma              90.00

loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Gd1 Gd 0.31746 0.81746 0.00000 1
B1 B 0.00000 0.00000 0.20290 1
B2 B 0.17590 0.03800 0.50000 1
B3 B 0.08670 0.58670 0.50000 1

loop_
_atom_site_moment_label
_atom_site_moment_crystalaxis_x
_atom_site_moment_crystalaxis_y
_atom_site_moment_crystalaxis_z
Gd1 5.05 5.05 0.0"""

        s = self.mcif.get_structures(primitive=False)[0]
        self.assertEqual(s.formula, "Ni32 O32")
        self.assertTrue(Magmom.are_collinear(s.site_properties['magmom']))

        # example with non-collinear spin
        s_ncl = self.mcif_ncl.get_structures(primitive=False)[0]
        s_ncl_from_msg = CifParser.from_string(magcifstr).get_structures(
            primitive=False)[0]
        self.assertEqual(s_ncl.formula, "Gd4 B16")
        self.assertFalse(Magmom.are_collinear(s_ncl.site_properties['magmom']))

        self.assertTrue(s_ncl.matches(s_ncl_from_msg))
Beispiel #11
0
    def test_get_structures(self):

        # incommensurate structures not currently supported
        self.assertRaises(NotImplementedError, self.mcif_incom.get_structures)

        # disordered magnetic structures not currently supported
        self.assertRaises(NotImplementedError, self.mcif_disord.get_structures)

        # taken from self.mcif_ncl, removing explicit magnetic symmops
        # so that MagneticSymmetryGroup() has to be invoked
        magcifstr = """
data_5yOhtAoR

_space_group.magn_name_BNS     "P 4/m' b' m' "
_cell_length_a                 7.1316
_cell_length_b                 7.1316
_cell_length_c                 4.0505
_cell_angle_alpha              90.00
_cell_angle_beta               90.00
_cell_angle_gamma              90.00

loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Gd1 Gd 0.31746 0.81746 0.00000 1
B1 B 0.00000 0.00000 0.20290 1
B2 B 0.17590 0.03800 0.50000 1
B3 B 0.08670 0.58670 0.50000 1

loop_
_atom_site_moment_label
_atom_site_moment_crystalaxis_x
_atom_site_moment_crystalaxis_y
_atom_site_moment_crystalaxis_z
Gd1 5.05 5.05 0.0"""

        s = self.mcif.get_structures(primitive=False)[0]
        self.assertEqual(s.formula, "Ni32 O32")
        self.assertTrue(Magmom.are_collinear(s.site_properties['magmom']))

        # example with non-collinear spin
        s_ncl = self.mcif_ncl.get_structures(primitive=False)[0]
        s_ncl_from_msg = CifParser.from_string(magcifstr).get_structures(primitive=False)[0]
        self.assertEqual(s_ncl.formula, "Gd4 B16")
        self.assertFalse(Magmom.are_collinear(s_ncl.site_properties['magmom']))

        self.assertTrue(s_ncl.matches(s_ncl_from_msg))
Beispiel #12
0
    def from_dict(cls, d: dict) -> "ViseIncar":
        if d.get("MAGMOM") and isinstance(d["MAGMOM"][0], dict):
            d["MAGMOM"] = [Magmom.from_dict(m) for m in d["MAGMOM"]]

        return cls(
            {k: v
             for k, v in d.items() if k not in ("@module", "@class")})
Beispiel #13
0
    def get_orbit(self, p, m, tol=1e-5):
        """
        Returns the orbit for a point and its associated magnetic moment.

        Args:
            p: Point as a 3x1 array.
            m: A magnetic moment, compatible with
            :class:`pymatgen.electronic_structure.core.Magmom`
            tol: Tolerance for determining if sites are the same. 1e-5 should
                be sufficient for most purposes. Set to 0 for exact matching
                (and also needed for symbolic orbits).

        Returns:
            (([array], [array])) Tuple of orbit for point and magnetic moments for orbit.
        """
        orbit = []
        orbit_magmoms = []
        m = Magmom(m)
        for o in self.symmetry_ops:
            pp = o.operate(p)
            pp = np.mod(np.round(pp, decimals=10), 1)
            mm = o.operate_magmom(m)
            if not in_array_list(orbit, pp, tol=tol):
                orbit.append(pp)
                orbit_magmoms.append(mm)
        return orbit, orbit_magmoms
Beispiel #14
0
    def test_operate_magmom(self):

        # all test magmoms are the same
        magmoms = [
            Magmom([1, 2, 3]),  # as Magmom
            [1, 2, 3],  # as list
            Magmom([-3, 2, 1], saxis=[1, 0, 0]),
        ]  # as Magmom with non-default saxis

        xyzt_strings = ["x, y, z, +1", "x, y, z, -1", "x, -y, z, -1", "-x, -y, z, -1"]

        transformed_magmoms = [[1, 2, 3], [-1, -2, -3], [1, -2, 3], [1, 2, -3]]

        for xyzt_string, transformed_magmom in zip(xyzt_strings, transformed_magmoms):
            for magmom in magmoms:
                op = MagSymmOp.from_xyzt_string(xyzt_string)
                self.assertTrue(np.allclose(transformed_magmom, op.operate_magmom(magmom).global_moment))
Beispiel #15
0
    def from_dict(cls, d: Dict[str, Any]) -> "ViseIncar":
        kwargs = deepcopy(d)
        if kwargs.get("MAGMOM") and isinstance(kwargs["MAGMOM"][0], dict):
            kwargs["MAGMOM"] = [Magmom.from_dict(m) for m in kwargs["MAGMOM"]]

        return cls(
            {k: v
             for k, v in d.items() if k not in ("@module", "@class")})
Beispiel #16
0
 def test_init(self):
     # backwards compatibility for scalar-like magmoms
     magmom = Magmom(2.0)
     self.assertEqual(float(magmom), 2.0)
     # backwards compatibility for list-like magmoms
     magmom2 = Magmom([1, 2, 3])
     self.assertEqual(list(magmom2), [1, 2, 3])
     self.assertEqual(magmom2.global_moment.tolist(), [1, 2, 3])
     # non-default saxis, normalized internally
     magmom3 = Magmom([1, 2, 3], saxis=[1, 1, 1])
     self.assertTrue(np.allclose(magmom3.saxis, [np.sqrt(1/3.)]*3))
     # test construction from known global moment and desired, non-default saxis
     magmom4 = Magmom.from_global_moment_and_saxis([1, 2, 3], saxis=[1, 0, 0])
     self.assertTrue(np.allclose(magmom4.moment, [-3, 2, 1]))
     # test global moments with non-default saxis
     magmom5 = Magmom([-3, 2, 1], saxis=[1, 0, 0])
     self.assertTrue(np.allclose(magmom5.global_moment, [1, 2, 3]))
Beispiel #17
0
    def _unique_coords(self, coords_in, magmoms_in=None, lattice=None):
        """
        Generate unique coordinates using coord and symmetry positions
        and also their corresponding magnetic moments, if supplied.
        """
        coords = []
        coords_num = []

        if magmoms_in:
            magmoms = []
            if len(magmoms_in) != len(coords_in):
                raise ValueError
            for tmp_coord, tmp_magmom in zip(coords_in, magmoms_in):
                count = 0
                for op in self.symmetry_operations:
                    coord = op.operate(tmp_coord)
                    coord = np.array([i - math.floor(i) for i in coord])
                    if isinstance(op, MagSymmOp):
                        # Up to this point, magmoms have been defined relative
                        # to crystal axis. Now convert to Cartesian and into
                        # a Magmom object.
                        magmom = Magmom.from_moment_relative_to_crystal_axes(
                            op.operate_magmom(tmp_magmom), lattice=lattice)
                    else:
                        magmom = Magmom(tmp_magmom)
                    if not in_coord_list_pbc(
                            coords, coord, atol=self._site_tolerance):
                        coords.append(coord)
                        magmoms.append(magmom)
                        count = count + 1
                coords_num.append(count)
            return coords, magmoms, coords_num
        else:
            for tmp_coord in coords_in:
                count = 0
                for op in self.symmetry_operations:
                    coord = op.operate(tmp_coord)
                    coord = np.array([i - math.floor(i) for i in coord])
                    if not in_coord_list_pbc(
                            coords, coord, atol=self._site_tolerance):
                        coords.append(coord)
                        count = count + 1
                coords_num.append(count)
            return coords, [Magmom(0)
                            ] * len(coords), coords_num  # return dummy magmoms
Beispiel #18
0
    def test_get_moments(self):

        # simple cases
        magmom_along_x = Magmom([1, 0, 0])
        self.assertTrue(np.allclose(magmom_along_x.get_moment(saxis=[1, 0, 0]), [0, 0, 1]))

        magmom_along_y = Magmom([0, 1, 0])
        self.assertTrue(np.allclose(magmom_along_y.get_moment(saxis=[0, 1, 0]), [0, 0, 1]))

        # test transformations
        magmoms = [[0, 0, 0],
                   [0, 0, 1],
                   [0, 0, -1],
                   [1, 2, 3],
                   [-1, 2, 3],
                   [-1, -2, -3]]

        for magmom in magmoms:
            magmom1 = Magmom(magmom)
            # transform to non-default saxis
            magmom2 = magmom1.get_00t_magmom_with_xyz_saxis()
            # and back to default saxis
            magmom3 = magmom2.get_xyz_magmom_with_001_saxis()
            self.assertTrue(np.allclose(magmom1.moment, magmom))
            self.assertTrue(np.allclose(magmom1.saxis, [0, 0, 1]))
            self.assertTrue(np.allclose(magmom1.get_moment(saxis=magmom1.saxis), magmom1.moment))
            self.assertTrue(np.allclose(magmom1.get_moment(saxis=magmom2.saxis), magmom2.moment))
            self.assertTrue(np.allclose(magmom2.get_moment(saxis=[0, 0, 1]), magmom1.moment))
            self.assertTrue(np.allclose(magmom2.get_moment(saxis=magmom2.saxis), magmom2.moment))
            self.assertTrue(np.allclose(magmom3.moment, magmom1.moment))
Beispiel #19
0
 def test_relative_to_crystal_axes(self):
     lattice = Lattice.from_parameters(5, 10, 5, 90, 110, 90)
     moment = [1, 0, 2]
     magmom = Magmom.from_moment_relative_to_crystal_axes(moment, lattice)
     self.assertTrue(
         np.allclose(magmom.moment, [0.93969262, 0.0, 1.65797986]))
     self.assertTrue(
         np.allclose(magmom.get_moment_relative_to_crystal_axes(lattice),
                     moment))
Beispiel #20
0
 def setUp(self):
     self.ordered_site = Site("Fe", [0.25, 0.35, 0.45])
     self.disordered_site = Site({"Fe": 0.5, "Mn": 0.5},
                                 [0.25, 0.35, 0.45])
     self.propertied_site = Site("Fe2+", [0.25, 0.35, 0.45],
                                 {'magmom': 5.1, 'charge': 4.2})
     self.propertied_magmomvector_site = Site("Fe2+", [0.25, 0.35, 0.45],
                                              {'magmom': Magmom([2.6, 2.6, 3.5]), 'charge': 4.2})
     self.dummy_site = Site("X", [0, 0, 0])
Beispiel #21
0
    def test_lsorbit_magmom(self):
        magmom1 = [[0.0, 0.0, 3.0], [0, 1, 0], [2, 1, 2]]
        magmom2 = [-1, -1, -1, 0, 0, 0, 0, 0]
        magmom4 = [Magmom([1.0, 2.0, 2.0])]

        ans_string1 = (
            "LANGEVIN_GAMMA = 10 10 10\nLSORBIT = True\n"
            "MAGMOM = 0.0 0.0 3.0 0 1 0 2 1 2\n"
        )
        ans_string2 = "LANGEVIN_GAMMA = 10\nLSORBIT = True\n" "MAGMOM = 3*3*-1 3*5*0\n"
        ans_string3 = "LSORBIT = False\nMAGMOM = 2*-1 2*9\n"
        ans_string4_nolsorbit = "LANGEVIN_GAMMA = 10\nLSORBIT = False\nMAGMOM = 1*3.0\n"
        ans_string4_lsorbit = (
            "LANGEVIN_GAMMA = 10\nLSORBIT = True\nMAGMOM = 1.0 2.0 2.0\n"
        )

        incar = Incar({})
        incar["MAGMOM"] = magmom1
        incar["LSORBIT"] = "T"
        incar["LANGEVIN_GAMMA"] = [10, 10, 10]
        self.assertEqual(ans_string1, str(incar))

        incar["MAGMOM"] = magmom2
        incar["LSORBIT"] = "T"
        incar["LANGEVIN_GAMMA"] = 10
        self.assertEqual(ans_string2, str(incar))

        incar["MAGMOM"] = magmom4
        incar["LSORBIT"] = "F"
        self.assertEqual(ans_string4_nolsorbit, str(incar))
        incar["LSORBIT"] = "T"
        self.assertEqual(ans_string4_lsorbit, str(incar))

        incar = Incar.from_string(ans_string1)
        self.assertEqual(incar["MAGMOM"], [[0.0, 0.0, 3.0], [0, 1, 0], [2, 1, 2]])
        self.assertEqual(incar["LANGEVIN_GAMMA"], [10, 10, 10])

        incar = Incar.from_string(ans_string2)
        self.assertEqual(
            incar["MAGMOM"],
            [
                [-1, -1, -1],
                [-1, -1, -1],
                [-1, -1, -1],
                [0, 0, 0],
                [0, 0, 0],
                [0, 0, 0],
                [0, 0, 0],
                [0, 0, 0],
            ],
        )
        self.assertEqual(incar["LANGEVIN_GAMMA"], [10])

        incar = Incar.from_string(ans_string3)
        self.assertFalse(incar["LSORBIT"])
        self.assertEqual(incar["MAGMOM"], [-1, -1, 9, 9])
Beispiel #22
0
 def test_have_consistent_saxis(self):
     magmom1 = Magmom([1, 2, 3])
     magmom2 = Magmom([1, 2, 3])
     magmom3 = Magmom([1, 2, 3], saxis=[0, 0, -1])
     magmom4 = Magmom([1, 2, 3], saxis=[1, 2, 3])
     self.assertTrue(Magmom.have_consistent_saxis([magmom1, magmom2]))
     self.assertFalse(Magmom.have_consistent_saxis([magmom1, magmom3]))
     self.assertFalse(Magmom.have_consistent_saxis([magmom1, magmom4]))
def check_and_force_collinear(mypatstructure, angle_tolerance=10.0):
    """Make sure the list of magmoms use the same spin axis by taking the
    largest magnetic moments as a reference axis for collinear calculations
    
    Parameters:
    -----------
    mypatstructure: Structure
                    structure object
    angle_tolerance: float
                     the minimum angle to consider between the reference spin axis and
                     magnetic moments vector. Default=10.
    Returns:
    --------
           all_true : boolean, if True the angle is within the threshold
    """
    from pymatgen.electronic_structure.core import Magmom
    from pymatgen.util.coord import get_angle

    magmoms = mypatstructure.site_properties['magmom']
    cif_moments, direction = Magmom.get_consistent_set_and_saxis(magmoms)
    #print("direction", direction)
    magmoms_ = np.empty([0,3])
    for comp in magmoms:
        magmoms_ = np.vstack([magmoms_, [comp[0], comp[1], comp[2]]])
        #print("comp", comp[0], comp[1], comp[2])

    # filter non zero moments
    magmoms_nzr = [m for m in magmoms_ if abs(np.any(m))]
    true_or_false = Magmom.are_collinear(magmoms)
    #if true_or_false == False:

    angles = np.empty([0, 1])
    for m in magmoms_nzr:
        angles = np.vstack([angles, get_angle(unit_vector(m), direction, units="degrees")])
        #print("angles", angles)

    store_check = []
    for angle in angles:
        store_check.append(check_angle_bool(angle, angle_tolerance))

    all_true = np.allclose(True, store_check)
    return all_true
Beispiel #24
0
 def test_to_from_dict(self):
     d = self.disordered_site.as_dict()
     site = Site.from_dict(d)
     self.assertEqual(site, self.disordered_site)
     self.assertNotEqual(site, self.ordered_site)
     d = self.propertied_site.as_dict()
     site = Site.from_dict(d)
     self.assertEqual(site.properties["magmom"], 5.1)
     self.assertEqual(site.properties["charge"], 4.2)
     d = self.propertied_magmomvector_site.as_dict()
     site = Site.from_dict(d)
     self.assertEqual(site.properties["magmom"], Magmom([2.6, 2.6, 3.5]))
     self.assertEqual(site.properties["charge"], 4.2)
     d = self.dummy_site.as_dict()
     site = Site.from_dict(d)
     self.assertEqual(site.species, self.dummy_site.species)
Beispiel #25
0
 def test_init(self):
     # backwards compatibility for scalar-like magmoms
     magmom = Magmom(2.0)
     self.assertEqual(float(magmom), 2.0)
     # backwards compatibility for list-like magmoms
     magmom2 = Magmom([1, 2, 3])
     self.assertEqual(list(magmom2), [1, 2, 3])
     self.assertEqual(magmom2.global_moment.tolist(), [1, 2, 3])
     # non-default saxis, normalized internally
     magmom3 = Magmom([1, 2, 3], saxis=[1, 1, 1])
     self.assertTrue(np.allclose(magmom3.saxis, [np.sqrt(1/3.)]*3))
     # test construction from known global moment and desired, non-default saxis
     magmom4 = Magmom.from_global_moment_and_saxis([1, 2, 3], saxis=[1, 0, 0])
     self.assertTrue(np.allclose(magmom4.moment, [-3, 2, 1]))
     # test global moments with non-default saxis
     magmom5 = Magmom([-3, 2, 1], saxis=[1, 0, 0])
     self.assertTrue(np.allclose(magmom5.global_moment, [1, 2, 3]))
Beispiel #26
0
 def parse_magmoms(self, data, lattice=None):
     """
     Parse atomic magnetic moments from data dictionary
     """
     if lattice is None:
         raise Exception(
             'Magmoms given in terms of crystal axes in magCIF spec.')
     try:
         magmoms = {
             data["_atom_site_moment_label"][i]:
             Magmom.from_moment_relative_to_crystal_axes([
                 str2float(data["_atom_site_moment_crystalaxis_x"][i]),
                 str2float(data["_atom_site_moment_crystalaxis_y"][i]),
                 str2float(data["_atom_site_moment_crystalaxis_z"][i])
             ], lattice)
             for i in range(len(data["_atom_site_moment_label"]))
         }
     except (ValueError, KeyError):
         return None
     return magmoms
Beispiel #27
0
 def _pristine_magnetic_moments_and_direction(self):
     """Magnetic moments of and collinear axis direction
     and separates it into the directions of each moments and 
     collinear spin axis.
     
     Returns
     -------
     moments : numpy.ndarray
     direction : numpy.ndarray
     """
     structure = self.pristine_structure_supercell
     magmoms = structure.site_properties['magmom']
     moments, direction = Magmom.get_consistent_set_and_saxis(magmoms)
     # append for muon
     if self.mu_f==-1:
         moments.append(np.array([0.,0.,0.]))
     else:
         moments.insert(self_mu_f, np.array([0.,0.,0.]))
     if len(moments) != self.n_atoms:
         # to check if calculated structure is compatible to supercell size 
         raise EstimateFieldContributionsError('Invalid inputs in Structure magmoms')
     return moments, direction
Beispiel #28
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)
Beispiel #29
0
    def operate_magmom(self, magmom):
        """
        Apply time reversal operator on the magnetic moment. Note that
        magnetic moments transform as axial vectors, not polar vectors. 

        See 'Symmetry and magnetic structures', Rodríguez-Carvajal and
        Bourée for a good discussion. DOI: 10.1051/epjconf/20122200010

        Args:
            magmom: Magnetic moment as electronic_structure.core.Magmom
            class or as list or np array-like

        Returns:
            Magnetic moment after operator applied as Magmom class
        """

        magmom = Magmom(magmom)  # type casting to handle lists as input

        transformed_moment = self.apply_rotation_only(magmom.global_moment) * \
            np.linalg.det(self.rotation_matrix) * self.time_reversal

        # retains input spin axis if different from default
        return Magmom.from_global_moment_and_saxis(transformed_moment, magmom.saxis)
Beispiel #30
0
    def __init__(self, structure,
                 overwrite_magmom_mode="none",
                 round_magmoms=False,
                 detect_valences=False,
                 make_primitive=True,
                 default_magmoms=None,
                 threshold=0.1):
        """
        A class which provides a few helpful methods to analyze
        collinear magnetic structures.

        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.

        :param structure: Structure object
        :param overwrite_magmom_mode (str): default "none"
        :param round_magmoms (int): will round input magmoms to
        specified number of decimal places, suggest value of 1 or False
        for typical DFT calculations depending on application
        :param detect_valences (bool): if True, will attempt to assign valences
        to input structure
        :param make_primitive (bool): if True, will transform to primitive
        magnetic cell
        :param default_magmoms (dict): (optional) dict specifying default magmoms
        :param threshold (float): number (in Bohr magnetons) below which magmoms
        will be rounded to zero, default of 0.1 can probably be increased for many
        magnetic systems, depending on your application
        """

        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()
            bva = BVAnalyzer()
            try:
                structure = trans.apply_transformation(structure)
            except ValueError:
                warnings.warn("Could not assign valences "
                              "for {}".format(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.")
        elif 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 below threshold to zero
        magmoms = [m if abs(m) > threshold else 0 for m in magmoms]

        # overwrite existing magmoms with default_magmoms
        if overwrite_magmom_mode not in ("none", "respect_sign",
                                         "respect_zeros", "replace_all",
                                         "replace_all_if_undefined"):
            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, Specie) 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":
                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

        # round magmoms to specified number of
        # decimal places, used to smooth out
        # computational data
        # TODO: be a bit smarter about rounding magmoms!
        if round_magmoms:
            magmoms = np.around(structure.site_properties['magmom'],
                                decimals=round_magmoms)
            structure.add_site_property(magmoms)

        structure.add_site_property('magmom', magmoms)

        if make_primitive:
            structure = structure.get_primitive_structure(use_site_props=True)

        self.structure = structure
Beispiel #31
0
 def test_negative(self):
     self.assertEqual(-Magmom([1, 2, 3]), Magmom([-1, -2, -3]))
Beispiel #32
0
 def test_equality(self):
     self.assertTrue(Magmom([1, 1, 1]) == Magmom([1, 1, 1]))
     self.assertFalse(Magmom([1, 1, 2]) == Magmom([1, 1, 1]))
     self.assertTrue(Magmom([0, 0, 10]) == 10)
    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: bool = None,
        set_net_positive: bool = True,
        threshold: float = 0.1,
    ):
        """
        A class which provides a few helpful methods to analyze
        collinear magnetic structures.
        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
        :param structure: Structure object
        :param overwrite_magmom_mode (str): default "none"
        :param round_magmoms (int or bool): 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
        :param detect_valences (bool): if True, will attempt to assign valences
        to input structure
        :param make_primitive (bool): if True, will transform to primitive
        magnetic cell
        :param default_magmoms (dict): (optional) dict specifying default magmoms
        :param set_net_positive (bool): 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.
        :param threshold (float): number (in Bohr magnetons) below which magmoms
        will be rounded to zero, default of 0.1 can probably be increased for many
        magnetic systems, depending on your application
        """

        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()
            bva = BVAnalyzer()
            try:
                structure = trans.apply_transformation(structure)
            except ValueError:
                warnings.warn("Could not assign valences "
                              "for {}".format(
                                  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.")
        elif 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 below threshold to zero
        magmoms = [m if abs(m) > threshold else 0 for m in magmoms]

        # 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, Specie)
                  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)

        if set_net_positive:
            sign = np.sum(magmoms)
            if sign < 0:
                magmoms = -np.array(magmoms)

        structure.add_site_property("magmom", magmoms)

        if make_primitive:
            structure = structure.get_primitive_structure(use_site_props=True)

        self.structure = structure
Beispiel #34
0
def get_site_scene(
    self,
    connected_sites: List[ConnectedSite] = None,
    # connected_site_metadata: None,
    # connected_sites_to_draw,
    connected_sites_not_drawn: List[ConnectedSite] = None,
    hide_incomplete_edges: bool = False,
    incomplete_edge_length_scale: Optional[float] = 1.0,
    connected_sites_colors: Optional[List[str]] = None,
    connected_sites_not_drawn_colors: Optional[List[str]] = None,
    origin: Optional[List[float]] = None,
    draw_polyhedra: bool = True,
    explicitly_calculate_polyhedra_hull: bool = False,
    bond_radius: float = 0.1,
    draw_magmoms: bool = True,
    magmom_scale: float = 1.0,
    legend: Optional[Legend] = None,
) -> Scene:
    """

    Args:
        connected_sites:
        connected_sites_not_drawn:
        hide_incomplete_edges:
        incomplete_edge_length_scale:
        connected_sites_colors:
        connected_sites_not_drawn_colors:
        origin:
        explicitly_calculate_polyhedra_hull:
        legend:

    Returns:

    """

    atoms = []
    bonds = []
    polyhedron = []
    magmoms = []

    legend = legend or Legend(self)

    # for disordered structures
    is_ordered = self.is_ordered
    phiStart, phiEnd = None, None
    occu_start = 0.0

    position = self.coords.tolist()

    radii = [legend.get_radius(sp, site=self) for sp in self.species.keys()]
    max_radius = float(min(radii))

    for idx, (sp, occu) in enumerate(self.species.items()):

        if isinstance(sp, DummySpecie):

            cube = Cubes(
                positions=[position], color=legend.get_color(sp, site=self), width=0.4
            )
            atoms.append(cube)

        else:

            color = legend.get_color(sp, site=self)
            radius = legend.get_radius(sp, site=self)

            # TODO: make optional/default to None
            # in disordered structures, we fractionally color-code spheres,
            # drawing a sphere segment from phi_end to phi_start
            # (think a sphere pie chart)
            if not is_ordered:
                phi_frac_end = occu_start + occu
                phi_frac_start = occu_start
                occu_start = phi_frac_end
                phiStart = phi_frac_start * np.pi * 2
                phiEnd = phi_frac_end * np.pi * 2

            name = str(sp)
            if occu != 1.0:
                name += " ({}% occupancy)".format(occu)
            name += f" ({position[0]:.3f}, {position[1]:.3f}, {position[2]:.3f})"

            if self.properties:
                for k, v in self.properties.items():
                    name += f" ({k} = {v})"

            sphere = Spheres(
                positions=[position],
                color=color,
                radius=radius,
                phiStart=phiStart,
                phiEnd=phiEnd,
                clickable=True,
                tooltip=name,
            )
            atoms.append(sphere)

        # Add magmoms
        if draw_magmoms:
            if magmom := self.properties.get("magmom"):
                # enforce type
                magmom = np.array(Magmom(magmom).get_moment())
                magmom = 2 * magmom_scale * max_radius * magmom
                tail = np.array(position) - 0.5 * np.array(magmom)
                head = np.array(position) + 0.5 * np.array(magmom)

                arrow = Arrows(
                    positionPairs=[[tail, head]],
                    color="red",
                    radius=0.20,
                    headLength=0.5,
                    headWidth=0.4,
                    clickable=True,
                )
                magmoms.append(arrow)
Beispiel #35
0
    def _get_structure(self, data, primitive):
        """
        Generate structure from part of the cif.
        """
        def parse_symbol(sym):
            # Common representations for elements/water in cif files
            # TODO: fix inconsistent handling of water
            special = {
                "D": "D",
                "Hw": "H",
                "Ow": "O",
                "Wat": "O",
                "wat": "O",
                "OH": "",
                "OH2": ""
            }
            m = re.findall(r"w?[A-Z][a-z]*", sym)
            if m and m != "?":
                if sym in special:
                    v = special[sym]
                else:
                    v = special.get(m[0], m[0])
                if len(m) > 1 or (m[0] in special):
                    warnings.warn("{} parsed as {}".format(sym, v))
                return v

        lattice = self.get_lattice(data)

        # if magCIF, get magnetic symmetry moments and magmoms
        # else standard CIF, and use empty magmom dict
        if self.feature_flags["magcif_incommensurate"]:
            raise NotImplementedError(
                "Incommensurate structures not currently supported.")
        elif self.feature_flags["magcif"]:
            self.symmetry_operations = self.get_magsymops(data)
            magmoms = self.parse_magmoms(data, lattice=lattice)
        else:
            self.symmetry_operations = self.get_symops(data)
            magmoms = {}

        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()
        coord_to_magmoms = OrderedDict()

        def get_matching_coord(coord):
            keys = list(coord_to_species.keys())
            coords = np.array(keys)
            for op in self.symmetry_operations:
                c = op.operate(coord)
                inds = find_in_coord_list_pbc(coords,
                                              c,
                                              atol=self._site_tolerance)
                # cant use if inds, because python is dumb and np.array([0]) evaluates
                # to False
                if len(inds):
                    return keys[inds[0]]
            return False

        for i in range(len(data["_atom_site_label"])):
            try:
                # If site type symbol exists, use it. Otherwise, we use the
                # label.
                symbol = parse_symbol(data["_atom_site_type_symbol"][i])
            except KeyError:
                symbol = parse_symbol(data["_atom_site_label"][i])
            if not symbol:
                continue

            if oxi_states is not None:
                o_s = oxi_states.get(symbol, 0)
                # use _atom_site_type_symbol if possible for oxidation state
                if "_atom_site_type_symbol" in data.data.keys():
                    oxi_symbol = data["_atom_site_type_symbol"][i]
                    o_s = oxi_states.get(oxi_symbol, o_s)
                try:
                    el = Specie(symbol, o_s)
                except:
                    el = DummySpecie(symbol, o_s)
            else:
                el = get_el_sp(symbol)

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            magmom = magmoms.get(data["_atom_site_label"][i], Magmom(0))

            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                if not match:
                    coord_to_species[coord] = Composition({el: occu})
                    coord_to_magmoms[coord] = magmom
                else:
                    coord_to_species[match] += {el: occu}
                    coord_to_magmoms[
                        match] = None  # disordered magnetic not currently supported

        sum_occu = [sum(c.values()) for c in coord_to_species.values()]
        if any([o > 1 for o in sum_occu]):
            warnings.warn(
                "Some occupancies (%s) sum to > 1! If they are within "
                "the tolerance, they will be rescaled." % str(sum_occu))

        allspecies = []
        allcoords = []
        allmagmoms = []

        # check to see if magCIF file is disordered
        if self.feature_flags["magcif"]:
            for k, v in coord_to_magmoms.items():
                if v is None:
                    # Proposed solution to this is to instead store magnetic moments
                    # as Specie 'spin' property, instead of site property, but this
                    # introduces ambiguities for end user (such as unintended use of
                    # `spin` and Specie will have fictious oxidation state).
                    raise NotImplementedError(
                        'Disordered magnetic structures not currently supported.'
                    )

        if coord_to_species.items():
            for species, group in groupby(sorted(list(
                    coord_to_species.items()),
                                                 key=lambda x: x[1]),
                                          key=lambda x: x[1]):
                tmp_coords = [site[0] for site in group]
                tmp_magmom = [
                    coord_to_magmoms[tmp_coord] for tmp_coord in tmp_coords
                ]

                if self.feature_flags["magcif"]:
                    coords, magmoms = self._unique_coords(
                        tmp_coords, tmp_magmom)
                else:
                    coords, magmoms = self._unique_coords(tmp_coords)

                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])
                allmagmoms.extend(magmoms)

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords) and len(
                allspecies) == len(allmagmoms):

            if self.feature_flags["magcif"]:
                struct = Structure(lattice,
                                   allspecies,
                                   allcoords,
                                   site_properties={"magmom": allmagmoms})
            else:
                struct = Structure(lattice, allspecies, allcoords)

            struct = struct.get_sorted_structure()

            if primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            return struct
Beispiel #36
0
 def test_relative_to_crystal_axes(self):
     lattice = Lattice.from_parameters(5, 10, 5, 90, 110, 90)
     moment = [1, 0, 2]
     magmom = Magmom.from_moment_relative_to_crystal_axes(moment, lattice)
     self.assertTrue(np.allclose(magmom.moment, [0.93969262, 0.0, 1.65797986]))
     self.assertTrue(np.allclose(magmom.get_moment_relative_to_crystal_axes(lattice), moment))
Beispiel #37
0
 def from_dict(cls, d):
     if d.get("MAGMOM") and isinstance(d["MAGMOM"][0], dict):
         d["MAGMOM"] = [Magmom.from_dict(m) for m in d["MAGMOM"]]
     return Incar({k: v for k, v in d.items() if k not in ("@module","@class",'comment')}),\
            [v for k, v in d.items() if k in ('comment')][0]
Beispiel #38
0
    def __init__(self, struct, symprec=None, write_magmoms=False):
        """
        A wrapper around CifFile to write CIF files from pymatgen structures.

        Args:
            struct (Structure): structure to write
            symprec (float): If not none, finds the symmetry of the structure
                and writes the cif with symmetry information. Passes symprec
                to the SpacegroupAnalyzer
            write_magmoms (bool): If True, will write magCIF file. Incompatible
                with symprec
        """

        if write_magmoms and symprec:
            warnings.warn(
                "Magnetic symmetry cannot currently be detected by pymatgen.")
            symprec = None

        format_str = "{:.8f}"

        block = OrderedDict()
        loops = []
        spacegroup = ("P 1", 1)
        if symprec is not None:
            sf = SpacegroupAnalyzer(struct, symprec)
            spacegroup = (sf.get_space_group_symbol(),
                          sf.get_space_group_number())
            # Needs the refined struture when using symprec. This converts
            # primitive to conventional structures, the standard for CIF.
            struct = sf.get_refined_structure()

        latt = struct.lattice
        comp = struct.composition
        no_oxi_comp = comp.element_composition
        block["_symmetry_space_group_name_H-M"] = spacegroup[0]
        for cell_attr in ['a', 'b', 'c']:
            block["_cell_length_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        for cell_attr in ['alpha', 'beta', 'gamma']:
            block["_cell_angle_" + cell_attr] = format_str.format(
                getattr(latt, cell_attr))
        block["_symmetry_Int_Tables_number"] = spacegroup[1]
        block["_chemical_formula_structural"] = no_oxi_comp.reduced_formula
        block["_chemical_formula_sum"] = no_oxi_comp.formula
        block["_cell_volume"] = latt.volume.__str__()

        reduced_comp, fu = no_oxi_comp.get_reduced_composition_and_factor()
        block["_cell_formula_units_Z"] = str(int(fu))

        if symprec is None:
            block["_symmetry_equiv_pos_site_id"] = ["1"]
            block["_symmetry_equiv_pos_as_xyz"] = ["x, y, z"]
        else:
            sf = SpacegroupAnalyzer(struct, symprec)

            symmops = []
            for op in sf.get_symmetry_operations():
                v = op.translation_vector
                symmops.append(
                    SymmOp.from_rotation_and_translation(
                        op.rotation_matrix, v))

            ops = [op.as_xyz_string() for op in symmops]
            block["_symmetry_equiv_pos_site_id"] = \
                ["%d" % i for i in range(1, len(ops) + 1)]
            block["_symmetry_equiv_pos_as_xyz"] = ops

        loops.append(
            ["_symmetry_equiv_pos_site_id", "_symmetry_equiv_pos_as_xyz"])

        contains_oxidation = True
        try:
            symbol_to_oxinum = OrderedDict([(el.__str__(), float(el.oxi_state))
                                            for el in sorted(comp.elements)])
        except AttributeError:
            symbol_to_oxinum = OrderedDict([(el.symbol, 0)
                                            for el in sorted(comp.elements)])
            contains_oxidation = False
        if contains_oxidation:
            block["_atom_type_symbol"] = symbol_to_oxinum.keys()
            block["_atom_type_oxidation_number"] = symbol_to_oxinum.values()
            loops.append(["_atom_type_symbol", "_atom_type_oxidation_number"])

        atom_site_type_symbol = []
        atom_site_symmetry_multiplicity = []
        atom_site_fract_x = []
        atom_site_fract_y = []
        atom_site_fract_z = []
        atom_site_label = []
        atom_site_occupancy = []
        atom_site_moment_label = []
        atom_site_moment_crystalaxis_x = []
        atom_site_moment_crystalaxis_y = []
        atom_site_moment_crystalaxis_z = []
        count = 1
        if symprec is None:
            for site in struct:
                for sp, occu in sorted(site.species_and_occu.items()):
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("1")
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())

                    magmom = site.properties.get('magmom', Magmom(0))
                    moment = Magmom.get_moment_relative_to_crystal_axes(
                        magmom, latt)
                    if write_magmoms and abs(magmom) > 0:
                        atom_site_moment_label.append("{}{}".format(
                            sp.symbol, count))
                        atom_site_moment_crystalaxis_x.append(moment[0])
                        atom_site_moment_crystalaxis_y.append(moment[1])
                        atom_site_moment_crystalaxis_z.append(moment[2])

                    count += 1
        else:
            # The following just presents a deterministic ordering.
            unique_sites = [
                (sorted(sites,
                        key=lambda s: tuple([abs(x)
                                             for x in s.frac_coords]))[0],
                 len(sites))
                for sites in sf.get_symmetrized_structure().equivalent_sites
            ]
            for site, mult in sorted(unique_sites,
                                     key=lambda t:
                                     (t[0].species_and_occu.average_electroneg,
                                      -t[1], t[0].a, t[0].b, t[0].c)):
                for sp, occu in site.species_and_occu.items():
                    atom_site_type_symbol.append(sp.__str__())
                    atom_site_symmetry_multiplicity.append("%d" % mult)
                    atom_site_fract_x.append("{0:f}".format(site.a))
                    atom_site_fract_y.append("{0:f}".format(site.b))
                    atom_site_fract_z.append("{0:f}".format(site.c))
                    atom_site_label.append("{}{}".format(sp.symbol, count))
                    atom_site_occupancy.append(occu.__str__())
                    count += 1

        block["_atom_site_type_symbol"] = atom_site_type_symbol
        block["_atom_site_label"] = atom_site_label
        block["_atom_site_symmetry_multiplicity"] = \
            atom_site_symmetry_multiplicity
        block["_atom_site_fract_x"] = atom_site_fract_x
        block["_atom_site_fract_y"] = atom_site_fract_y
        block["_atom_site_fract_z"] = atom_site_fract_z
        block["_atom_site_occupancy"] = atom_site_occupancy
        loops.append([
            "_atom_site_type_symbol", "_atom_site_label",
            "_atom_site_symmetry_multiplicity", "_atom_site_fract_x",
            "_atom_site_fract_y", "_atom_site_fract_z", "_atom_site_occupancy"
        ])
        if write_magmoms:
            block["_atom_site_moment_label"] = atom_site_moment_label
            block[
                "_atom_site_moment_crystalaxis_x"] = atom_site_moment_crystalaxis_x
            block[
                "_atom_site_moment_crystalaxis_y"] = atom_site_moment_crystalaxis_y
            block[
                "_atom_site_moment_crystalaxis_z"] = atom_site_moment_crystalaxis_z
            loops.append([
                "_atom_site_moment_label", "_atom_site_moment_crystalaxis_x",
                "_atom_site_moment_crystalaxis_y",
                "_atom_site_moment_crystalaxis_z"
            ])
        d = OrderedDict()
        d[comp.reduced_formula] = CifBlock(block, loops, comp.reduced_formula)
        self._cf = CifFile(d)
Beispiel #39
0
    def test_init(self):
        for f in ['OUTCAR', 'OUTCAR.gz']:
            filepath = os.path.join(test_dir, f)
            outcar = Outcar(filepath)
            expected_mag = ({
                'd': 0.0,
                'p': 0.003,
                's': 0.002,
                'tot': 0.005
            }, {
                'd': 0.798,
                'p': 0.008,
                's': 0.007,
                'tot': 0.813
            }, {
                'd': 0.798,
                'p': 0.008,
                's': 0.007,
                'tot': 0.813
            }, {
                'd': 0.0,
                'p': -0.117,
                's': 0.005,
                'tot': -0.112
            }, {
                'd': 0.0,
                'p': -0.165,
                's': 0.004,
                'tot': -0.162
            }, {
                'd': 0.0,
                'p': -0.117,
                's': 0.005,
                'tot': -0.112
            }, {
                'd': 0.0,
                'p': -0.165,
                's': 0.004,
                'tot': -0.162
            })
            expected_chg = ({
                'p': 0.154,
                's': 0.078,
                'd': 0.0,
                'tot': 0.232
            }, {
                'p': 0.707,
                's': 0.463,
                'd': 8.316,
                'tot': 9.486
            }, {
                'p': 0.707,
                's': 0.463,
                'd': 8.316,
                'tot': 9.486
            }, {
                'p': 3.388,
                's': 1.576,
                'd': 0.0,
                'tot': 4.964
            }, {
                'p': 3.365,
                's': 1.582,
                'd': 0.0,
                'tot': 4.947
            }, {
                'p': 3.388,
                's': 1.576,
                'd': 0.0,
                'tot': 4.964
            }, {
                'p': 3.365,
                's': 1.582,
                'd': 0.0,
                'tot': 4.947
            })

            self.assertAlmostEqual(outcar.magnetization, expected_mag, 5,
                                   "Wrong magnetization read from Outcar")
            self.assertAlmostEqual(outcar.charge, expected_chg, 5,
                                   "Wrong charge read from Outcar")
            self.assertFalse(outcar.is_stopped)
            self.assertEqual(
                outcar.run_stats, {
                    'System time (sec)': 0.938,
                    'Total CPU time used (sec)': 545.142,
                    'Elapsed time (sec)': 546.709,
                    'Maximum memory used (kb)': 0.0,
                    'Average memory used (kb)': 0.0,
                    'User time (sec)': 544.204,
                    'cores': '8'
                })
            self.assertAlmostEqual(outcar.efermi, 2.0112)
            self.assertAlmostEqual(outcar.nelect, 44.9999991)
            self.assertAlmostEqual(outcar.total_mag, 0.9999998)

            self.assertIsNotNone(outcar.as_dict())

            self.assertFalse(outcar.lepsilon)

        filepath = os.path.join(test_dir, 'OUTCAR.stopped')
        outcar = Outcar(filepath)
        self.assertTrue(outcar.is_stopped)

        for f in ['OUTCAR.lepsilon', 'OUTCAR.lepsilon.gz']:
            filepath = os.path.join(test_dir, f)
            outcar = Outcar(filepath)

            self.assertTrue(outcar.lepsilon)
            self.assertAlmostEqual(outcar.dielectric_tensor[0][0], 3.716432)
            self.assertAlmostEqual(outcar.dielectric_tensor[0][1], -0.20464)
            self.assertAlmostEqual(outcar.dielectric_tensor[1][2], -0.20464)
            self.assertAlmostEqual(outcar.dielectric_ionic_tensor[0][0],
                                   0.001419)
            self.assertAlmostEqual(outcar.dielectric_ionic_tensor[0][2],
                                   0.001419)
            self.assertAlmostEqual(outcar.dielectric_ionic_tensor[2][2],
                                   0.001419)
            self.assertAlmostEqual(outcar.piezo_tensor[0][0], 0.52799)
            self.assertAlmostEqual(outcar.piezo_tensor[1][3], 0.35998)
            self.assertAlmostEqual(outcar.piezo_tensor[2][5], 0.35997)
            self.assertAlmostEqual(outcar.piezo_ionic_tensor[0][0], 0.05868)
            self.assertAlmostEqual(outcar.piezo_ionic_tensor[1][3], 0.06241)
            self.assertAlmostEqual(outcar.piezo_ionic_tensor[2][5], 0.06242)
            self.assertAlmostEqual(outcar.born[0][1][2], -0.385)
            self.assertAlmostEqual(outcar.born[1][2][0], 0.36465)

        filepath = os.path.join(test_dir, 'OUTCAR.NiO_SOC.gz')
        outcar = Outcar(filepath)
        expected_mag = ({
            's': Magmom([0.0, 0.0, -0.001]),
            'p': Magmom([0.0, 0.0, -0.003]),
            'd': Magmom([0.0, 0.0, 1.674]),
            'tot': Magmom([0.0, 0.0, 1.671])
        }, {
            's': Magmom([0.0, 0.0, 0.001]),
            'p': Magmom([0.0, 0.0, 0.003]),
            'd': Magmom([0.0, 0.0, -1.674]),
            'tot': Magmom([0.0, 0.0, -1.671])
        }, {
            's': Magmom([0.0, 0.0, 0.0]),
            'p': Magmom([0.0, 0.0, 0.0]),
            'd': Magmom([0.0, 0.0, 0.0]),
            'tot': Magmom([0.0, 0.0, 0.0])
        }, {
            's': Magmom([0.0, 0.0, 0.0]),
            'p': Magmom([0.0, 0.0, 0.0]),
            'd': Magmom([0.0, 0.0, 0.0]),
            'tot': Magmom([0.0, 0.0, 0.0])
        })
        # test note: Magmom class uses np.allclose() when testing for equality
        # so fine to use assertEqual here
        self.assertEqual(
            outcar.magnetization, expected_mag,
            "Wrong vector magnetization read from Outcar for SOC calculation")