class MagOrderingTransformationTest(PymatgenTest):
    def setUp(self):
        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)
        self.NiO_AFM_111.add_spin_by_site([-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)
        self.NiO_AFM_001.add_spin_by_site([-5, 5, 0, 0])

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

        parser = CifParser(os.path.join(PymatgenTest.TEST_FILES_DIR, "Li8Fe2NiCoO8.cif"))
        self.Li8Fe2NiCoO8 = parser.get_structures()[0]
        self.Li8Fe2NiCoO8.remove_oxidation_states()
        warnings.simplefilter("ignore")

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

    def test_apply_transformation(self):
        trans = MagOrderingTransformation({"Fe": 5})
        p = Poscar.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "POSCAR.LiFePO4"), check_for_POTCAR=False)
        s = p.structure
        alls = trans.apply_transformation(s, 10)
        self.assertEqual(len(alls), 3)
        f = SpacegroupAnalyzer(alls[0]["structure"], 0.1)
        self.assertEqual(f.get_space_group_number(), 31)

        model = IsingModel(5, 5)
        trans = MagOrderingTransformation({"Fe": 5}, energy_model=model)
        alls2 = trans.apply_transformation(s, 10)
        # Ising model with +J penalizes similar neighbor magmom.
        self.assertNotEqual(alls[0]["structure"], alls2[0]["structure"])
        self.assertEqual(alls[0]["structure"], alls2[2]["structure"])

        s = self.get_structure("Li2O")
        # Li2O doesn't have magnetism of course, but this is to test the
        # enumeration.
        trans = MagOrderingTransformation({"Li+": 1}, max_cell_size=3)
        alls = trans.apply_transformation(s, 100)
        # TODO: check this is correct, unclear what len(alls) should be
        self.assertEqual(len(alls), 12)

        trans = MagOrderingTransformation({"Ni": 5})
        alls = trans.apply_transformation(self.NiO.get_primitive_structure(), return_ranked_list=10)

        self.assertArrayAlmostEqual(self.NiO_AFM_111.lattice.parameters, alls[0]["structure"].lattice.parameters)
        self.assertArrayAlmostEqual(self.NiO_AFM_001.lattice.parameters, alls[1]["structure"].lattice.parameters)

    def test_ferrimagnetic(self):
        trans = MagOrderingTransformation({"Fe": 5}, order_parameter=0.75, max_cell_size=1)
        p = Poscar.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "POSCAR.LiFePO4"), check_for_POTCAR=False)
        s = p.structure
        a = SpacegroupAnalyzer(s, 0.1)
        s = a.get_refined_structure()
        alls = trans.apply_transformation(s, 10)
        self.assertEqual(len(alls), 1)

    def test_as_from_dict(self):
        trans = MagOrderingTransformation({"Fe": 5}, order_parameter=0.75)
        d = trans.as_dict()
        # Check json encodability
        s = json.dumps(d)
        trans = MagOrderingTransformation.from_dict(d)
        self.assertEqual(trans.mag_species_spin, {"Fe": 5})
        from pymatgen.analysis.energy_models import SymmetryModel

        self.assertIsInstance(trans.energy_model, SymmetryModel)

    def test_zero_spin_case(self):
        # ensure that zero spin case maintains sites and formula
        s = self.get_structure("Li2O")
        trans = MagOrderingTransformation({"Li+": 0.0}, order_parameter=0.5)
        alls = trans.apply_transformation(s)
        Li_site = alls.indices_from_symbol("Li")[0]
        # Ensure s does not have a spin property
        self.assertFalse("spin" in s.sites[Li_site].specie._properties)
        # ensure sites are assigned a spin property in alls
        self.assertTrue("spin" in alls.sites[Li_site].specie._properties)
        self.assertEqual(alls.sites[Li_site].specie._properties["spin"], 0)

    def test_advanced_usage(self):
        # test spin on just one oxidation state
        magtypes = {"Fe2+": 5}
        trans = MagOrderingTransformation(magtypes)
        alls = trans.apply_transformation(self.Fe3O4_oxi)
        self.assertIsInstance(alls, Structure)
        self.assertEqual(str(alls[0].specie), "Fe2+,spin=5")
        self.assertEqual(str(alls[2].specie), "Fe3+")

        # test multiple order parameters
        # this should only order on Fe3+ site, but assign spin to both
        magtypes = {"Fe2+": 5, "Fe3+": 5}
        order_parameters = [
            MagOrderParameterConstraint(1, species_constraints="Fe2+"),
            MagOrderParameterConstraint(0.5, species_constraints="Fe3+"),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4_oxi)
        # using this 'sorted' syntax because exact order of sites in first
        # returned structure varies between machines: we just want to ensure
        # that the order parameter is accurate
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(0, 2)),
            sorted(["Fe2+,spin=5", "Fe2+,spin=5"]),
        )
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(2, 6)),
            sorted(["Fe3+,spin=5", "Fe3+,spin=5", "Fe3+,spin=-5", "Fe3+,spin=-5"]),
        )
        self.assertEqual(str(alls[0].specie), "Fe2+,spin=5")

        # this should give same results as previously
        # but with opposite sign on Fe2+ site
        magtypes = {"Fe2+": -5, "Fe3+": 5}
        order_parameters = [
            MagOrderParameterConstraint(1, species_constraints="Fe2+"),
            MagOrderParameterConstraint(0.5, species_constraints="Fe3+"),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4_oxi)
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(0, 2)),
            sorted(["Fe2+,spin=-5", "Fe2+,spin=-5"]),
        )
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(2, 6)),
            sorted(["Fe3+,spin=5", "Fe3+,spin=5", "Fe3+,spin=-5", "Fe3+,spin=-5"]),
        )

        # while this should order on both sites
        magtypes = {"Fe2+": 5, "Fe3+": 5}
        order_parameters = [
            MagOrderParameterConstraint(0.5, species_constraints="Fe2+"),
            MagOrderParameterConstraint(0.25, species_constraints="Fe3+"),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4_oxi)
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(0, 2)),
            sorted(["Fe2+,spin=5", "Fe2+,spin=-5"]),
        )
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(2, 6)),
            sorted(["Fe3+,spin=5", "Fe3+,spin=-5", "Fe3+,spin=-5", "Fe3+,spin=-5"]),
        )

        # add coordination numbers to our test case
        # don't really care what these are for the test case
        cns = [6, 6, 6, 6, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0]
        self.Fe3O4.add_site_property("cn", cns)

        # this should give FM ordering on cn=4 sites, and AFM ordering on cn=6 sites
        magtypes = {"Fe": 5}
        order_parameters = [
            MagOrderParameterConstraint(
                0.5,
                species_constraints="Fe",
                site_constraint_name="cn",
                site_constraints=6,
            ),
            MagOrderParameterConstraint(
                1.0,
                species_constraints="Fe",
                site_constraint_name="cn",
                site_constraints=4,
            ),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4)
        alls.sort(key=lambda x: x.properties["cn"], reverse=True)
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(0, 4)),
            sorted(["Fe,spin=-5", "Fe,spin=-5", "Fe,spin=5", "Fe,spin=5"]),
        )
        self.assertEqual(
            sorted(str(alls[idx].specie) for idx in range(4, 6)),
            sorted(["Fe,spin=5", "Fe,spin=5"]),
        )

        # now ordering on both sites, equivalent to order_parameter = 0.5
        magtypes = {"Fe2+": 5, "Fe3+": 5}
        order_parameters = [
            MagOrderParameterConstraint(0.5, species_constraints="Fe2+"),
            MagOrderParameterConstraint(0.5, species_constraints="Fe3+"),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4_oxi, return_ranked_list=10)
        struct = alls[0]["structure"]
        self.assertEqual(
            sorted(str(struct[idx].specie) for idx in range(0, 2)),
            sorted(["Fe2+,spin=5", "Fe2+,spin=-5"]),
        )
        self.assertEqual(
            sorted(str(struct[idx].specie) for idx in range(2, 6)),
            sorted(["Fe3+,spin=5", "Fe3+,spin=-5", "Fe3+,spin=-5", "Fe3+,spin=5"]),
        )
        self.assertEqual(len(alls), 4)

        # now mixed orderings where neither are equal or 1
        magtypes = {"Fe2+": 5, "Fe3+": 5}
        order_parameters = [
            MagOrderParameterConstraint(0.5, species_constraints="Fe2+"),
            MagOrderParameterConstraint(0.25, species_constraints="Fe3+"),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4_oxi, return_ranked_list=100)
        struct = alls[0]["structure"]
        self.assertEqual(
            sorted(str(struct[idx].specie) for idx in range(0, 2)),
            sorted(["Fe2+,spin=5", "Fe2+,spin=-5"]),
        )
        self.assertEqual(
            sorted(str(struct[idx].specie) for idx in range(2, 6)),
            sorted(["Fe3+,spin=5", "Fe3+,spin=-5", "Fe3+,spin=-5", "Fe3+,spin=-5"]),
        )
        self.assertEqual(len(alls), 2)

        # now order on multiple species
        magtypes = {"Fe2+": 5, "Fe3+": 5}
        order_parameters = [
            MagOrderParameterConstraint(0.5, species_constraints=["Fe2+", "Fe3+"]),
        ]
        trans = MagOrderingTransformation(magtypes, order_parameter=order_parameters)
        alls = trans.apply_transformation(self.Fe3O4_oxi, return_ranked_list=10)
        struct = alls[0]["structure"]
        self.assertEqual(
            sorted(str(struct[idx].specie) for idx in range(0, 2)),
            sorted(["Fe2+,spin=5", "Fe2+,spin=-5"]),
        )
        self.assertEqual(
            sorted(str(struct[idx].specie) for idx in range(2, 6)),
            sorted(["Fe3+,spin=5", "Fe3+,spin=-5", "Fe3+,spin=-5", "Fe3+,spin=5"]),
        )
        self.assertEqual(len(alls), 6)
Пример #2
0
    def test_write(self):
        cw_ref_string = """# generated using pymatgen
data_GdB4
_symmetry_space_group_name_H-M   'P 1'
_cell_length_a   7.13160000
_cell_length_b   7.13160000
_cell_length_c   4.05050000
_cell_angle_alpha   90.00000000
_cell_angle_beta   90.00000000
_cell_angle_gamma   90.00000000
_symmetry_Int_Tables_number   1
_chemical_formula_structural   GdB4
_chemical_formula_sum   'Gd4 B16'
_cell_volume   206.00729003
_cell_formula_units_Z   4
loop_
 _symmetry_equiv_pos_site_id
 _symmetry_equiv_pos_as_xyz
  1  'x, y, z'
loop_
 _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
  Gd  Gd0  1  0.31746000  0.81746000  0.00000000  1.0
  Gd  Gd1  1  0.18254000  0.31746000  0.00000000  1.0
  Gd  Gd2  1  0.81746000  0.68254000  0.00000000  1.0
  Gd  Gd3  1  0.68254000  0.18254000  0.00000000  1.0
  B  B4  1  0.00000000  0.00000000  0.20290000  1.0
  B  B5  1  0.50000000  0.50000000  0.79710000  1.0
  B  B6  1  0.00000000  0.00000000  0.79710000  1.0
  B  B7  1  0.50000000  0.50000000  0.20290000  1.0
  B  B8  1  0.17590000  0.03800000  0.50000000  1.0
  B  B9  1  0.96200000  0.17590000  0.50000000  1.0
  B  B10  1  0.03800000  0.82410000  0.50000000  1.0
  B  B11  1  0.67590000  0.46200000  0.50000000  1.0
  B  B12  1  0.32410000  0.53800000  0.50000000  1.0
  B  B13  1  0.82410000  0.96200000  0.50000000  1.0
  B  B14  1  0.53800000  0.67590000  0.50000000  1.0
  B  B15  1  0.46200000  0.32410000  0.50000000  1.0
  B  B16  1  0.08670000  0.58670000  0.50000000  1.0
  B  B17  1  0.41330000  0.08670000  0.50000000  1.0
  B  B18  1  0.58670000  0.91330000  0.50000000  1.0
  B  B19  1  0.91330000  0.41330000  0.50000000  1.0
loop_
 _atom_site_moment_label
 _atom_site_moment_crystalaxis_x
 _atom_site_moment_crystalaxis_y
 _atom_site_moment_crystalaxis_z
  Gd0  5.05000000  5.05000000  0.00000000
  Gd1  -5.05000000  5.05000000  0.00000000
  Gd2  5.05000000  -5.05000000  0.00000000
  Gd3  -5.05000000  -5.05000000  0.00000000
"""
        s_ncl = self.mcif_ncl.get_structures(primitive=False)[0]

        cw = CifWriter(s_ncl, write_magmoms=True)
        self.assertEqual(cw.__str__(), cw_ref_string)

        # from list-type magmoms
        list_magmoms = [list(m) for m in s_ncl.site_properties["magmom"]]

        # float magmoms (magnitude only)
        float_magmoms = [float(m) for m in s_ncl.site_properties["magmom"]]

        s_ncl.add_site_property("magmom", list_magmoms)
        cw = CifWriter(s_ncl, write_magmoms=True)
        self.assertEqual(cw.__str__(), cw_ref_string)

        s_ncl.add_site_property("magmom", float_magmoms)
        cw = CifWriter(s_ncl, write_magmoms=True)

        cw_ref_string_magnitudes = """# generated using pymatgen
data_GdB4
_symmetry_space_group_name_H-M   'P 1'
_cell_length_a   7.13160000
_cell_length_b   7.13160000
_cell_length_c   4.05050000
_cell_angle_alpha   90.00000000
_cell_angle_beta   90.00000000
_cell_angle_gamma   90.00000000
_symmetry_Int_Tables_number   1
_chemical_formula_structural   GdB4
_chemical_formula_sum   'Gd4 B16'
_cell_volume   206.00729003
_cell_formula_units_Z   4
loop_
 _symmetry_equiv_pos_site_id
 _symmetry_equiv_pos_as_xyz
  1  'x, y, z'
loop_
 _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
  Gd  Gd0  1  0.31746000  0.81746000  0.00000000  1.0
  Gd  Gd1  1  0.18254000  0.31746000  0.00000000  1.0
  Gd  Gd2  1  0.81746000  0.68254000  0.00000000  1.0
  Gd  Gd3  1  0.68254000  0.18254000  0.00000000  1.0
  B  B4  1  0.00000000  0.00000000  0.20290000  1.0
  B  B5  1  0.50000000  0.50000000  0.79710000  1.0
  B  B6  1  0.00000000  0.00000000  0.79710000  1.0
  B  B7  1  0.50000000  0.50000000  0.20290000  1.0
  B  B8  1  0.17590000  0.03800000  0.50000000  1.0
  B  B9  1  0.96200000  0.17590000  0.50000000  1.0
  B  B10  1  0.03800000  0.82410000  0.50000000  1.0
  B  B11  1  0.67590000  0.46200000  0.50000000  1.0
  B  B12  1  0.32410000  0.53800000  0.50000000  1.0
  B  B13  1  0.82410000  0.96200000  0.50000000  1.0
  B  B14  1  0.53800000  0.67590000  0.50000000  1.0
  B  B15  1  0.46200000  0.32410000  0.50000000  1.0
  B  B16  1  0.08670000  0.58670000  0.50000000  1.0
  B  B17  1  0.41330000  0.08670000  0.50000000  1.0
  B  B18  1  0.58670000  0.91330000  0.50000000  1.0
  B  B19  1  0.91330000  0.41330000  0.50000000  1.0
loop_
 _atom_site_moment_label
 _atom_site_moment_crystalaxis_x
 _atom_site_moment_crystalaxis_y
 _atom_site_moment_crystalaxis_z
  Gd0  0.00000000  0.00000000  7.14177849
  Gd1  0.00000000  0.00000000  7.14177849
  Gd2  0.00000000  0.00000000  -7.14177849
  Gd3  0.00000000  0.00000000  -7.14177849
"""
        self.assertEqual(cw.__str__().strip(),
                         cw_ref_string_magnitudes.strip())
        # test we're getting correct magmoms in ncl case
        s_ncl2 = self.mcif_ncl2.get_structures()[0]
        list_magmoms = [list(m) for m in s_ncl2.site_properties["magmom"]]
        self.assertEqual(list_magmoms[0][0], 0.0)
        self.assertAlmostEqual(list_magmoms[0][1], 5.9160793408726366)
        self.assertAlmostEqual(list_magmoms[1][0], -5.1234749999999991)
        self.assertAlmostEqual(list_magmoms[1][1], 2.9580396704363183)

        # test creating an structure without oxidation state doesn't raise errors
        s_manual = Structure(Lattice.cubic(4.2), ["Cs", "Cl"],
                             [[0, 0, 0], [0.5, 0.5, 0.5]])
        s_manual.add_spin_by_site([1, -1])
        cw = CifWriter(s_manual, write_magmoms=True)

        # check oxidation state
        cw_manual_oxi_string = """# generated using pymatgen
data_CsCl
_symmetry_space_group_name_H-M   'P 1'
_cell_length_a   4.20000000
_cell_length_b   4.20000000
_cell_length_c   4.20000000
_cell_angle_alpha   90.00000000
_cell_angle_beta   90.00000000
_cell_angle_gamma   90.00000000
_symmetry_Int_Tables_number   1
_chemical_formula_structural   CsCl
_chemical_formula_sum   'Cs1 Cl1'
_cell_volume   74.08800000
_cell_formula_units_Z   1
loop_
 _symmetry_equiv_pos_site_id
 _symmetry_equiv_pos_as_xyz
  1  'x, y, z'
loop_
 _atom_type_symbol
 _atom_type_oxidation_number
  Cs+  1.0
  Cl+  1.0
loop_
 _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
  Cs+  Cs0  1  0.00000000  0.00000000  0.00000000  1
  Cl+  Cl1  1  0.50000000  0.50000000  0.50000000  1
loop_
 _atom_site_moment_label
 _atom_site_moment_crystalaxis_x
 _atom_site_moment_crystalaxis_y
 _atom_site_moment_crystalaxis_z
"""
        s_manual.add_oxidation_state_by_site([1, 1])
        cw = CifWriter(s_manual, write_magmoms=True)
        self.assertEqual(cw.__str__(), cw_manual_oxi_string)