def test_ferrimagnetic(self): trans = MagOrderingTransformation({"Fe": 5}, 0.75, max_cell_size=1) p = Poscar.from_file(os.path.join(test_dir, 'POSCAR.LiFePO4'), check_for_POTCAR=False) s = p.structure alls = trans.apply_transformation(s, 10) self.assertEqual(len(alls), 2)
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}, 0.5) alls = trans.apply_transformation(s) # Ensure s does not have a spin property self.assertFalse('spin' in s.sites[1].specie._properties) # ensure sites are assigned a spin property in alls self.assertTrue('spin' in alls.sites[1].specie._properties)
def test_as_from_dict(self): trans = MagOrderingTransformation({"Fe": 5}, 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}, 0.5) alls = trans.apply_transformation(s) #Ensure s does not have a spin property self.assertFalse('spin' in s.sites[0].specie._properties) #ensure sites are assigned a spin property in alls self.assertTrue('spin' in alls.sites[0].specie._properties)
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_zero_spin_case(self): #ensure that zero spin case maintains sites and formula from pymatgen.io.smartio import read_structure s = read_structure(os.path.join(test_dir, 'Li2O.cif')) trans = MagOrderingTransformation({"Li+": 0.0}, 0.5) alls = trans.apply_transformation(s) #compositions will not be equal due to spin assignment #structure representations will be the same self.assertEqual(str(s), str(alls)) #Ensure s does not have a spin property self.assertFalse('spin' in s.sites[0].specie._properties) #ensure sites are assigned a spin property in alls self.assertTrue('spin' in alls.sites[0].specie._properties)
def test_apply_transformation(self): trans = MagOrderingTransformation({"Fe": 5}) p = Poscar.from_file(os.path.join(test_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.assertEqual(self.NiO_AFM_111.lattice, alls[0]["structure"].lattice) self.assertEqual(self.NiO_AFM_001.lattice, alls[1]["structure"].lattice)
def test_apply_transformation(self): trans = MagOrderingTransformation({"Fe": 5}) p = Poscar.from_file(os.path.join(test_dir, 'POSCAR.LiFePO4'), check_for_POTCAR=False) s = p.structure alls = trans.apply_transformation(s, 10) self.assertEqual(len(alls), 3) f = SymmetryFinder(alls[0]["structure"], 0.1) self.assertEqual(f.get_spacegroup_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"]) from pymatgen.io.smartio import read_structure s = read_structure(os.path.join(test_dir, 'Li2O.cif')) #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) self.assertEqual(len(alls), 10)
def test_apply_transformation(self): trans = MagOrderingTransformation({"Fe": 5}) p = Poscar.from_file(os.path.join(test_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_spacegroup_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) self.assertEqual(len(alls), 10)
def test_apply_transformation(self): trans = MagOrderingTransformation({"Fe": 5}) p = Poscar.from_file(os.path.join(test_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_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)
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)
def _generate_transformations(self, structure): """ The central problem with trying to enumerate magnetic orderings is that we have to enumerate orderings that might plausibly be magnetic ground states, while not enumerating orderings that are physically implausible. The problem is that it is not always obvious by e.g. symmetry arguments alone which orderings to prefer. Here, we use a variety of strategies (heuristics) to enumerate plausible orderings, and later discard any duplicates that might be found by multiple strategies. This approach is not ideal, but has been found to be relatively robust over a wide range of magnetic structures. Args: structure: A sanitized input structure (_sanitize_input_structure) Returns: A dict of a transformation class instance (values) and name of enumeration strategy (keys) """ formula = structure.composition.reduced_formula transformations = {} # analyzer is used to obtain information on sanitized input analyzer = CollinearMagneticStructureAnalyzer( structure, default_magmoms=self.default_magmoms, overwrite_magmom_mode="replace_all", ) if not analyzer.is_magnetic: raise ValueError( "Not detected as magnetic, add a new default magmom for the " "element you believe may be magnetic?") # now we can begin to generate our magnetic orderings self.logger.info( "Generating magnetic orderings for {}".format(formula)) mag_species_spin = analyzer.magnetic_species_and_magmoms types_mag_species = sorted( analyzer.types_of_magnetic_specie, key=lambda sp: analyzer.default_magmoms.get(str(sp), 0), reverse=True, ) num_mag_sites = analyzer.number_of_magnetic_sites num_unique_sites = analyzer.number_of_unique_magnetic_sites() # enumerations become too slow as number of unique sites (and thus # permutations) increase, 8 is a soft limit, this can be increased # but do so with care if num_unique_sites > self.max_unique_sites: raise ValueError( "Too many magnetic sites to sensibly perform enumeration.") # maximum cell size to consider: as a rule of thumb, if the primitive cell # contains a large number of magnetic sites, perhaps we only need to enumerate # within one cell, whereas on the other extreme if the primitive cell only # contains a single magnetic site, we have to create larger supercells if "max_cell_size" not in self.transformation_kwargs: # TODO: change to 8 / num_mag_sites ? self.transformation_kwargs["max_cell_size"] = max( 1, int(4 / num_mag_sites)) self.logger.info("Max cell size set to {}".format( self.transformation_kwargs["max_cell_size"])) # when enumerating ferrimagnetic structures, it's useful to detect # symmetrically distinct magnetic sites, since different # local environments can result in different magnetic order # (e.g. inverse spinels) # initially, this was done by co-ordination number, but is # now done by a full symmetry analysis sga = SpacegroupAnalyzer(structure) structure_sym = sga.get_symmetrized_structure() wyckoff = ["n/a"] * len(structure) for indices, symbol in zip(structure_sym.equivalent_indices, structure_sym.wyckoff_symbols): for index in indices: wyckoff[index] = symbol is_magnetic_sites = [ True if site.specie in types_mag_species else False for site in structure ] # we're not interested in sites that we don't think are magnetic, # set these symbols to None to filter them out later wyckoff = [ symbol if is_magnetic_site else "n/a" for symbol, is_magnetic_site in zip(wyckoff, is_magnetic_sites) ] structure.add_site_property("wyckoff", wyckoff) wyckoff_symbols = set(wyckoff) - {"n/a"} # if user doesn't specifically request ferrimagnetic_Cr2NiO4 orderings, # we apply a heuristic as to whether to attempt them or not if self.automatic: if ("ferrimagnetic_by_motif" not in self.strategies and len(wyckoff_symbols) > 1 and len(types_mag_species) == 1): self.strategies += ("ferrimagnetic_by_motif", ) if ("antiferromagnetic_by_motif" not in self.strategies and len(wyckoff_symbols) > 1 and len(types_mag_species) == 1): self.strategies += ("antiferromagnetic_by_motif", ) if ("ferrimagnetic_by_species" not in self.strategies and len(types_mag_species) > 1): self.strategies += ("ferrimagnetic_by_species", ) # we start with a ferromagnetic ordering if "ferromagnetic" in self.strategies: # TODO: remove 0 spins ! fm_structure = analyzer.get_ferromagnetic_structure() # store magmom as spin property, to be consistent with output from # other transformations fm_structure.add_spin_by_site( fm_structure.site_properties["magmom"]) fm_structure.remove_site_property("magmom") # we now have our first magnetic ordering... self.ordered_structures.append(fm_structure) self.ordered_structure_origins.append("fm") # we store constraint(s) for each strategy first, # and then use each to perform a transformation later all_constraints = {} # ...to which we can add simple AFM cases first... if "antiferromagnetic" in self.strategies: constraint = MagOrderParameterConstraint( 0.5, # TODO: update MagOrderParameterConstraint in pymatgen to take types_mag_species directly species_constraints=list(map(str, types_mag_species)), ) all_constraints["afm"] = [constraint] # allows for non-magnetic sublattices if len(types_mag_species) > 1: for sp in types_mag_species: constraints = [ MagOrderParameterConstraint( 0.5, species_constraints=str(sp)) ] all_constraints["afm_by_{}".format(sp)] = constraints # ...and then we also try ferrimagnetic orderings by motif if a # single magnetic species is present... if "ferrimagnetic_by_motif" in self.strategies and len( wyckoff_symbols) > 1: # these orderings are AFM on one local environment, and FM on the rest for symbol in wyckoff_symbols: constraints = [ MagOrderParameterConstraint(0.5, site_constraint_name="wyckoff", site_constraints=symbol), MagOrderParameterConstraint( 1.0, site_constraint_name="wyckoff", site_constraints=list(wyckoff_symbols - {symbol}), ), ] all_constraints["ferri_by_motif_{}".format( symbol)] = constraints # and also try ferrimagnetic when there are multiple magnetic species if "ferrimagnetic_by_species" in self.strategies: sp_list = [str(site.specie) for site in structure] num_sp = {sp: sp_list.count(str(sp)) for sp in types_mag_species} total_mag_sites = sum(num_sp.values()) for sp in types_mag_species: # attempt via a global order parameter all_constraints["ferri_by_{}".format( sp)] = num_sp[sp] / total_mag_sites # attempt via afm on sp, fm on remaining species constraints = [ MagOrderParameterConstraint(0.5, species_constraints=str(sp)), MagOrderParameterConstraint( 1.0, species_constraints=list( map(str, set(types_mag_species) - {sp})), ), ] all_constraints["ferri_by_{}_afm".format(sp)] = constraints # ...and finally, we can try orderings that are AFM on one local # environment, and non-magnetic on the rest -- this is less common # but unless explicitly attempted, these states are unlikely to be found if "antiferromagnetic_by_motif" in self.strategies: for symbol in wyckoff_symbols: constraints = [ MagOrderParameterConstraint(0.5, site_constraint_name="wyckoff", site_constraints=symbol) ] all_constraints["afm_by_motif_{}".format(symbol)] = constraints # and now construct all our transformations for each strategy transformations = {} for name, constraints in all_constraints.items(): trans = MagOrderingTransformation(mag_species_spin, order_parameter=constraints, **self.transformation_kwargs) transformations[name] = trans return transformations