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

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

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

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

        # test with disorder on magnetic site
        self.Fe[0] = {
            Species("Fe", oxidation_state=0, properties={"spin": 5}): 0.5,
            "Ni": 0.5,
        }
        self.assertRaises(NotImplementedError,
                          CollinearMagneticStructureAnalyzer, self.Fe)
Example #2
0
    def process_item(self, item):
        """
        Process the tasks and materials into a magnetism collection

        Args:
            item dict: a dict of material_id, structure, and tasks

        Returns:
            dict: a magnetism dictionary
        """

        struct = Structure.from_dict(item["structure"])
        total_magnetization = item["magnetism"].get(
            "total_magnetization", 0)  # not necessarily == sum(magmoms)
        msa = CollinearMagneticStructureAnalyzer(struct)

        sign = np.sign(total_magnetization)
        total_magnetization = abs(total_magnetization)
        magmoms = list(sign * np.array(msa.magmoms))

        magnetism = {
            self.magnetism.key: item[self.materials.key],
            "magnetism": {
                'ordering':
                msa.ordering.value,
                'is_magnetic':
                msa.is_magnetic,
                'exchange_symmetry':
                msa.get_exchange_group_info()[1],
                'num_magnetic_sites':
                msa.number_of_magnetic_sites,
                'num_unique_magnetic_sites':
                msa.number_of_unique_magnetic_sites(),
                'types_of_magnetic_species':
                [str(t) for t in msa.types_of_magnetic_specie],
                'magmoms':
                magmoms,
                'total_magnetization_normalized_vol':
                total_magnetization / struct.volume,
                'total_magnetization_normalized_formula_units':
                total_magnetization /
                (struct.composition.get_reduced_composition_and_factor()[1])
            },
            "pymatgen_version": pymatgen_version
        }
        return magnetism
Example #3
0
    def calc(self, item):
        """
        Process the tasks and materials into a magnetism collection

        Args:
            item dict: a dict of material_id, structure, and tasks

        Returns:
            dict: a magnetism dictionary
        """

        struct = Structure.from_dict(get(item, "output.structure"))
        struct_has_magmoms = 'magmom' in struct.site_properties
        total_magnetization = abs(
            get(item, "calcs_reversed.0.output.outcar.total_magnetization", 0))
        msa = CollinearMagneticStructureAnalyzer(struct)
        magmoms = msa.magmoms

        magnetism = {
            "magnetism": {
                'ordering':
                msa.ordering.value if struct_has_magmoms else "Unknown",
                'is_magnetic':
                msa.is_magnetic,
                'exchange_symmetry':
                msa.get_exchange_group_info()[1],
                'num_magnetic_sites':
                msa.number_of_magnetic_sites,
                'num_unique_magnetic_sites':
                msa.number_of_unique_magnetic_sites(),
                'types_of_magnetic_species':
                [str(t) for t in msa.types_of_magnetic_specie],
                'magmoms':
                magmoms,
                'total_magnetization':
                total_magnetization,
                'total_magnetization_normalized_vol':
                total_magnetization / struct.volume,
                'total_magnetization_normalized_formula_units':
                total_magnetization /
                (struct.composition.get_reduced_composition_and_factor()[1])
            },
            "pymatgen_version": pymatgen_version
        }
        return magnetism
Example #4
0
    def _get_unique_sites(structure):
        """
        Get dict that maps site indices to unique identifiers.

        Args:
            structure (Structure): ground state Structure object.

        Returns:
            unique_site_ids (dict): maps tuples of equivalent site indices to a
                unique int identifier
            wyckoff_ids (dict): maps tuples of equivalent site indices to their
                wyckoff symbols

        """

        # Get a nonmagnetic representation of the supercell geometry
        s0 = CollinearMagneticStructureAnalyzer(
            structure, make_primitive=False,
            threshold=0.0).get_nonmagnetic_structure(make_primitive=False)

        # Get unique sites and wyckoff positions
        if "wyckoff" in s0.site_properties:
            s0.remove_site_property("wyckoff")

        symm_s0 = SpacegroupAnalyzer(s0).get_symmetrized_structure()
        wyckoff = ["n/a"] * len(symm_s0)
        equivalent_indices = symm_s0.equivalent_indices
        wyckoff_symbols = symm_s0.wyckoff_symbols

        # Construct dictionaries that map sites to numerical and wyckoff
        # identifiers
        unique_site_ids = {}
        wyckoff_ids = {}

        i = 0
        for indices, symbol in zip(equivalent_indices, wyckoff_symbols):
            unique_site_ids[tuple(indices)] = i
            wyckoff_ids[i] = symbol
            i += 1
            for index in indices:
                wyckoff[index] = symbol

        return unique_site_ids, wyckoff_ids
Example #5
0
def add_magnetism(mat, mag=None):
    mag_types = {
        "NM": "Non-magnetic",
        "FiM": "Ferri",
        "AFM": "AFM",
        "FM": "FM"
    }

    struc = Structure.from_dict(mat["structure"])
    msa = CollinearMagneticStructureAnalyzer(struc)
    mat["magnetic_type"] = mag_types[msa.ordering.value]
Example #6
0
    def test_round_magmoms(self):
        struct = self.NiO_AFM_001.copy()
        struct.add_site_property("magmom", [-5.0143, -5.02, 0.147, 0.146])

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

        struct.add_site_property("magmom", [-5.0143, 4.5, 0.147, 0.146])
        msa = CollinearMagneticStructureAnalyzer(struct,
                                                 round_magmoms=0.001,
                                                 make_primitive=False)
        self.assertTrue(
            np.allclose(msa.magmoms, [5.0143, -4.5, -0.1465, -0.1465]))
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"][0], 4.5)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"][1],
                               5.0143)
        self.assertAlmostEqual(msa.magnetic_species_and_magmoms["O"], 0.1465)
Example #7
0
    def update_contents(self, new_store_contents):

        struct = self.from_data(new_store_contents)

        msa = CollinearMagneticStructureAnalyzer(struct, round_magmoms=1)
        if not msa.is_magnetic:
            # TODO: detect magnetic elements (?)
            return html.Div(
                "This structure is not magnetic or does not have "
                "magnetic information associated with it."
            )

        mag_species_and_magmoms = msa.magnetic_species_and_magmoms
        for k, v in mag_species_and_magmoms.items():
            if not isinstance(v, list):
                mag_species_and_magmoms[k] = [v]
        magnetic_atoms = "\n".join(
            [
                f"{sp} ({', '.join([f'{magmom} µB' for magmom in magmoms])})"
                for sp, magmoms in mag_species_and_magmoms.items()
            ]
        )

        magnetization_per_formula_unit = (
            msa.total_magmoms
            / msa.structure.composition.get_reduced_composition_and_factor()[1]
        )

        rows = []
        rows.append(
            (
                html.B("Total magnetization per formula unit"),
                html.Br(),
                f"{magnetization_per_formula_unit:.1f} µB",
            )
        )
        rows.append((html.B("Atoms with local magnetic moments"), html.Br(),
                     magnetic_atoms))

        data_block = html.Div([html.P([html.Span(cell) for cell in row]) for row in rows])

        viewer = StructureMoleculeComponent(
            struct,
            id=self.id("structure"), color_scheme="magmom",
            static=True
        )

        return Columns([
            Column(html.Div([viewer.struct_layout], style={"height": "60vmin"})),
            Column(data_block)
        ])
Example #8
0
    def test_magnetic_properties(self):
        msa = CollinearMagneticStructureAnalyzer(self.GdB4)
        self.assertFalse(msa.is_collinear)

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

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

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

        msa = CollinearMagneticStructureAnalyzer(
            self.NiO,
            make_primitive=False,
            overwrite_magmom_mode="replace_all_if_undefined",
        )
        self.assertEqual(msa.number_of_magnetic_sites, 4)
        self.assertEqual(msa.number_of_unique_magnetic_sites(), 1)
        self.assertEqual(msa.types_of_magnetic_species, (Element.Ni, ))
        self.assertEqual(msa.get_exchange_group_info(), ("Fm-3m", 225))
Example #9
0
    def test_get_ferromagnetic_structure(self):
        msa = CollinearMagneticStructureAnalyzer(
            self.NiO, overwrite_magmom_mode="replace_all_if_undefined")
        s1 = msa.get_ferromagnetic_structure()
        s1_magmoms = [float(m) for m in s1.site_properties["magmom"]]
        s1_magmoms_ref = [5.0, 0.0]
        self.assertListEqual(s1_magmoms, s1_magmoms_ref)

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

        s2_prim = msa.get_ferromagnetic_structure(make_primitive=True)
        self.assertTrue(
            CollinearMagneticStructureAnalyzer(s1).matches_ordering(s2_prim))
Example #10
0
    def process_item(self, item):
        """
        Process the tasks and materials into a dielectrics collection

        Args:
            item dict: a dict of material_id, structure, and tasks

        Returns:
            dict: a dieletrics dictionary  
        """

        struc = Structure.from_dict(item["structure"])
        msa = CollinearMagneticStructureAnalyzer(struc)

        magnetism = {
            self.magnetism.key: item[self.materials.key],
            "magnetism": {
                'ordering': msa.ordering.value
            }
        }

        return magnetism
Example #11
0
    def from_structure(  # type: ignore[override]
            structure: Structure,
            material_id: str,
            fields: Optional[List[str]] = None,
            **kwargs) -> "MaterialsDoc":
        """
        Builds a materials document using the minimal amount of information
        """
        meta = StructureMetadata.from_structure(structure, fields=fields)
        ordering = CollinearMagneticStructureAnalyzer(structure).ordering
        kwargs.update(**meta.dict())

        if "last_updated" not in kwargs:
            kwargs["last_updated"] = datetime.utcnow()

        if "created_at" not in kwargs:
            kwargs["created_at"] = datetime.utcnow()

        return MaterialsDoc(structure=structure,
                            material_id=material_id,
                            ordering=ordering,
                            **kwargs)
Example #12
0
    def test_str(self):
        msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_001)

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

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

    # for historical consistency
    mag_types = {
        "NM": "Non-magnetic",
        "FiM": "Ferri",
        "AFM": "AFM",
        "FM": "FM"
    }

    struc = Structure.from_dict(mat["structure"])
    msa = CollinearMagneticStructureAnalyzer(struc)
    mat["magnetic_type"] = mag_types[msa.ordering.value]

    # this should never happen for future mats, and should have been fixed
    # for older mats, but just in case
    if 'magmom' not in struc.site_properties and mat[
            'total_magnetization'] > 0.1:
        mat["magnetic_type"] = "Magnetic (unknown ordering)"

    # TODO: will deprecate the above from dedicated magnetism builder
    if magnetism:
        mat["magnetism"] = magnetism["magnetism"]
Example #14
0
    def test_modes(self):
        mode = "none"
        msa = CollinearMagneticStructureAnalyzer(self.NiO,
                                                 overwrite_magmom_mode=mode)
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [0, 0])

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

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

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

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

        mode = "normalize"
        msa = CollinearMagneticStructureAnalyzer(
            msa.structure, overwrite_magmom_mode="normalize")
        magmoms = msa.structure.site_properties["magmom"]
        self.assertEqual(magmoms, [1, 0])
Example #15
0
 def test_net_positive(self):
     msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical)
     magmoms = msa.structure.site_properties["magmom"]
     self.assertEqual(magmoms, [3, 0, 0, 0])
Example #16
0
    def collate_mp_data(struct):

        mag_tasks = {}
        min_energy = 0

        ordering_labels = {
            "Unknown": "Unknown",
            "FM": "Ferromagnetic",
            "AFM": "Antiferromagnetic",
            "FiM": "Ferrimagnetic",
            "NM": "Non-magnetic",
        }

        with MPRester(endpoint="https://zola.lbl.gov/rest/v2") as mpr:

            # find similar structures in Materials Project
            mpids = mpr.find_structure(struct)

            # and all tasks for those materials, specifically their structures
            # and total energy per atom
            for d in mpr.query({"task_id": {"$in": mpids}}, ["task_ids"]):

                for task_id in d["task_ids"]:

                    add_task = True

                    task_struct = mpr.get_task_data(
                        task_id, prop="structure")[0]["structure"]
                    have_magmoms = "magmom" in task_struct.site_properties
                    task_energy = mpr.get_task_data(
                        task_id, prop="energy_per_atom")[0]["energy_per_atom"]
                    min_energy = min([min_energy, task_energy])
                    msa = CollinearMagneticStructureAnalyzer(task_struct)

                    if not have_magmoms:
                        ordering = "Unknown"
                    else:
                        ordering = msa.ordering.value

                        # group together similar orderings, only display lowest
                        # energy task for each ordering
                        mag_tasks_to_remove = []

                        for mag_task_id, mag_task in mag_tasks.items():
                            struct_to_compare = mag_task["struct"]
                            if msa.matches_ordering(struct_to_compare):
                                # if existing task is lower in energy, keep that one
                                if mag_task["energy"] < task_energy:
                                    add_task = False
                                # else remove existing task and add this one
                                else:
                                    mag_tasks_to_remove.append(mag_task_id)

                        # remove higher energy duplicate tasks
                        for mag_task_id in mag_tasks_to_remove:
                            del mag_tasks[mag_task_id]

                    if add_task:
                        mag_tasks[task_id] = {
                            "struct": task_struct,
                            "ordering": ordering_labels[ordering],
                            "energy": task_energy,
                        }

        for k, v in mag_tasks.items():
            v["energy"] = 1000 * (v["energy"] - min_energy)
            mag_tasks[k] = v

        return mag_tasks
Example #17
0
    def _do_cleanup(structures, energies):
        """Sanitize input structures and energies.

        Takes magnetic structures and performs the following operations
        - Erases nonmagnetic ions and gives all ions ['magmom'] site prop
        - Converts total energies -> energy / magnetic ion
        - Checks for duplicate/degenerate orderings
        - Sorts by energy

        Args:
            structures (list): Structure objects with magmoms.
            energies (list): Corresponding energies.

        Returns:
            ordered_structures (list): Sanitized structures.
            ordered_energies (list): Sorted energies.

        """

        # Get only magnetic ions & give all structures site_properties['magmom']
        # zero threshold so that magnetic ions with small moments
        # are preserved
        ordered_structures = [
            CollinearMagneticStructureAnalyzer(
                s, make_primitive=False,
                threshold=0.0).get_structure_with_only_magnetic_atoms(
                    make_primitive=False) for s in structures
        ]

        # Convert to energies / magnetic ion
        energies = [e / len(s) for (e, s) in zip(energies, ordered_structures)]

        # Check for duplicate / degenerate states (sometimes different initial
        # configs relax to the same state)
        remove_list = []
        for i, e in enumerate(energies):
            e_tol = 6  # 10^-6 eV/atom tol on energies
            e = round(e, e_tol)
            if i not in remove_list:
                for i_check, e_check in enumerate(energies):
                    e_check = round(e_check, e_tol)
                    if i != i_check and i_check not in remove_list and e == e_check:
                        remove_list.append(i_check)

        # Also discard structures with small |magmoms| < 0.1 uB
        # xx - get rid of these or just bury them in the list?
        # for i, s in enumerate(ordered_structures):
        #     magmoms = s.site_properties['magmom']
        #     if i not in remove_list:
        #         if any(abs(m) < 0.1 for m in magmoms):
        #             remove_list.append(i)

        # Remove duplicates
        if len(remove_list):
            ordered_structures = [
                s for i, s in enumerate(ordered_structures)
                if i not in remove_list
            ]
            energies = [
                e for i, e in enumerate(energies) if i not in remove_list
            ]

        # Sort by energy if not already sorted
        ordered_structures = [
            s for _, s in sorted(zip(energies, ordered_structures),
                                 reverse=False)
        ]
        ordered_energies = sorted(energies, reverse=False)

        return ordered_structures, ordered_energies
Example #18
0
    def get_low_energy_orderings(self):
        """
        Find lowest energy FM and AFM orderings to compute E_AFM - E_FM.

        Returns:
            fm_struct (Structure): fm structure with 'magmom' site property
            afm_struct (Structure): afm structure with 'magmom' site property
            fm_e (float): fm energy
            afm_e (float): afm energy

        """

        fm_struct, afm_struct = None, None
        mag_min = np.inf
        mag_max = 0.001
        fm_e_min = 0
        afm_e_min = 0

        # epas = [e / len(s) for (e, s) in zip(self.energies, self.ordered_structures)]

        for s, e in zip(self.ordered_structures, self.energies):

            ordering = CollinearMagneticStructureAnalyzer(
                s, threshold=0.0, make_primitive=False).ordering
            magmoms = s.site_properties["magmom"]

            # Try to find matching orderings first
            if ordering == Ordering.FM and e < fm_e_min:
                fm_struct = s
                mag_max = abs(sum(magmoms))
                fm_e = e
                fm_e_min = e

            if ordering == Ordering.AFM and e < afm_e_min:
                afm_struct = s
                afm_e = e
                mag_min = abs(sum(magmoms))
                afm_e_min = e

        # Brute force search for closest thing to FM and AFM
        if not fm_struct or not afm_struct:
            for s, e in zip(self.ordered_structures, self.energies):
                magmoms = s.site_properties["magmom"]

                if abs(sum(magmoms)) > mag_max:  # FM ground state
                    fm_struct = s
                    fm_e = e
                    mag_max = abs(sum(magmoms))

                # AFM ground state
                if abs(sum(magmoms)) < mag_min:
                    afm_struct = s
                    afm_e = e
                    mag_min = abs(sum(magmoms))
                    afm_e_min = e
                elif abs(sum(magmoms)) == 0 and mag_min == 0:
                    if e < afm_e_min:
                        afm_struct = s
                        afm_e = e
                        afm_e_min = e

        # Convert to magnetic structures with 'magmom' site property
        fm_struct = CollinearMagneticStructureAnalyzer(
            fm_struct, make_primitive=False,
            threshold=0.0).get_structure_with_only_magnetic_atoms(
                make_primitive=False)

        afm_struct = CollinearMagneticStructureAnalyzer(
            afm_struct, make_primitive=False,
            threshold=0.0).get_structure_with_only_magnetic_atoms(
                make_primitive=False)

        return fm_struct, afm_struct, fm_e, afm_e
Example #19
0
def procure_response_dict(
    struct_final,
    num_perturb_sites,
    incar_dict,
    outcar_dict,
    inv_block_dict,
    response_dict,
    perturb_dict,
    rkey,
    keys,
    ldaul_vals,
    analyzer_gs,
):
    """
    Function to gather response data, in preparation for linear regression.
    This data is organized into `response_dict`.
    """

    # perform magnetic ordering analysis
    analyzer_output = CollinearMagneticStructureAnalyzer(struct_final,
                                                         threshold=0.61)
    magnet_order = analyzer_output.ordering.value
    if rkey == keys[0]:  # store ground state ordering
        magnet_order_gs = magnet_order

    # check if ordering matches ground state configuration
    if analyzer_gs:
        if not analyzer_gs.matches_ordering(struct_final):
            use_calc = False
            calcs_skipped.append({
                'ICHARG': incar_dict.get("ICHARG", 0),
                'ISPIN': incar_dict.get("ISPIN", 1),
                'LDAUU': incar_dict['LDAUU'].copy(),
                'LDAUJ': incar_dict['LDAUJ'].copy()
            })

    for i in range(num_perturb_sites):
        specie = struct_final[i].specie
        ldaul = ldaul_vals[i]

        orbital = inv_block_dict[str(ldaul)]
        perturb_dict.update(
            {"site" + str(i): {
                 "specie": str(specie),
                 "orbital": orbital
             }})

        # Obtain occupancy values
        n_tot = float(outcar_dict['charge'][i][orbital])
        # FIXME: Adapt for noncollinear
        m_z = float((outcar_dict['magnetization'][i][orbital]))
        n_up = 0.5 * (n_tot + m_z)
        n_dn = 0.5 * (n_tot - m_z)

        v_up = float(incar_dict['LDAUU'][i])
        v_dn = float(incar_dict['LDAUJ'][i])

        response_dict[rkey]['site' + str(i)]['Nup'].append(n_up)
        response_dict[rkey]['site' + str(i)]['Ndn'].append(n_dn)
        response_dict[rkey]['site' + str(i)]['Ntot'].append(n_tot)
        response_dict[rkey]['site' + str(i)]['Mz'].append(m_z)

        response_dict[rkey]['site' + str(i)]['Vup'].append(v_up)
        response_dict[rkey]['site' + str(i)]['Vdn'].append(v_dn)

        response_dict[rkey]['magnetic order'].append(magnet_order)
Example #20
0
 def get_mag_ordering(struc):
     # helperd function to get a label of the magnetic ordering type
     return CollinearMagneticStructureAnalyzer(struc).ordering.value
Example #21
0
    def process_item(self, item):
        docs, material_dict = item
        grouped = group_by_material_id(material_dict, docs, 'input_structure')
        formula = docs[0]['pretty_formula']
        if not grouped:
            formula = Structure.from_dict(list(
                material_dict.values())[0]).composition.reduced_formula
            logger.debug("No material match for {}".format(formula))

        # For now just do the most recent one that's not failed
        # TODO: better sorting of docs
        all_docs = []
        for task_id, elastic_docs in grouped.items():
            elastic_docs = sorted(elastic_docs,
                                  key=lambda x:
                                  (x['order'], x['state'], x['completed_at']))
            grouped_by_order = {
                k: list(v)
                for k, v in groupby(elastic_docs, key=lambda x: x['order'])
            }
            soec_docs = grouped_by_order.get(2)
            toec_docs = grouped_by_order.get(3)
            if soec_docs:
                final_doc = soec_docs[-1]
            else:
                final_doc = toec_docs[-1]
            structure = Structure.from_dict(final_doc['optimized_structure'])
            formula = structure.composition.reduced_formula
            elements = [s.symbol for s in structure.composition.elements]
            chemsys = '-'.join(elements)

            # Issue warning if relaxed structure differs
            warnings = final_doc.get('warnings') or []
            opt = Structure.from_dict(final_doc['optimized_structure'])
            init = Structure.from_dict(final_doc['input_structure'])
            # TODO: are these the right params?
            if not StructureMatcher().fit(init, opt):
                warnings.append("Inequivalent optimization structure")
            material_mag = CollinearMagneticStructureAnalyzer(
                opt).ordering.value
            material_mag = mag_types[material_mag]
            if final_doc['magnetic_type'] != material_mag:
                warnings.append("Elastic magnetic phase is {}".format(
                    final_doc['magnetic_type']))
            warnings = warnings or None

            # Filter for failure and warnings
            k_vrh = final_doc['k_vrh']
            if k_vrh < 0 or k_vrh > 600:
                state = 'failed'
            elif warnings is not None:
                state = 'warning'
            else:
                state = 'successful'
            final_doc.update({"warnings": warnings})
            elastic_summary = {
                'task_id': task_id,
                'all_elastic_fits': elastic_docs,
                'elasticity': final_doc,
                'spacegroup': init.get_space_group_info()[0],
                'magnetic_type': final_doc['magnetic_type'],
                'pretty_formula': formula,
                'chemsys': chemsys,
                'elements': elements,
                'last_updated': self.elasticity.lu_field,
                'state': state
            }
            if toec_docs:
                # TODO: this should be a bit more refined
                final_toec_doc = deepcopy(toec_docs[-1])
                et_exp = ElasticTensorExpansion.from_voigt(
                    final_toec_doc['elastic_tensor_expansion'])
                symbol_dict = et_exp[1].zeroed(1e-2).get_symbol_dict()
                final_toec_doc.update({"symbol_dict": symbol_dict})
                set_(elastic_summary, "elasticity.third_order", final_toec_doc)
            all_docs.append(jsanitize(elastic_summary))
            # elastic_summary.update(final_doc)
        return all_docs
Example #22
0
def get_elastic_analysis(opt_task, defo_tasks):
    """
    Performs the analysis of opt_tasks and defo_tasks necessary for
    an elastic analysis

    Args:
        opt_task: task doc corresponding to optimization
        defo_tasks: task_doc corresponding to deformations

    Returns:
        elastic document with fitted elastic tensor and analysis

    """
    elastic_doc = {"warnings": []}
    opt_struct = Structure.from_dict(opt_task['output']['structure'])
    input_struct = Structure.from_dict(opt_task['input']['structure'])
    # For now, discern order (i.e. TOEC) using parameters from optimization
    # TODO: figure this out more intelligently
    diff = get(opt_task, "input.incar.EDIFFG", 0)
    order = 3 if np.isclose(diff, -0.001) else 2
    explicit, derived = process_elastic_calcs(opt_task, defo_tasks)
    all_calcs = explicit + derived
    stresses = [c.get("cauchy_stress") for c in all_calcs]
    pk_stresses = [c.get("pk_stress") for c in all_calcs]
    strains = [c.get("strain") for c in all_calcs]
    elastic_doc['calculations'] = all_calcs
    vstrains = [s.zeroed(0.002).voigt for s in strains]
    if np.linalg.matrix_rank(vstrains) == 6:
        if order == 2:
            et_fit = legacy_fit(strains, stresses)
        elif order == 3:
            # Test for TOEC
            if len(strains) < 70:
                logger.info("insufficient valid strains for {} TOEC".format(
                    opt_task['formula_pretty']))
                return None
            eq_stress = -0.1 * Stress(opt_task['output']['stress'])
            # strains = [s.zeroed(0.0001) for s in strains]
            # et_expansion = pdb_function(ElasticTensorExpansion.from_diff_fit,
            #     strains, pk_stresses, eq_stress=eq_stress, tol=1e-5)
            et_exp_raw = ElasticTensorExpansion.from_diff_fit(
                strains, pk_stresses, eq_stress=eq_stress, tol=1e-6)
            et_exp = et_exp_raw.voigt_symmetrized.convert_to_ieee(opt_struct)
            et_exp = et_exp.round(1)
            et_fit = ElasticTensor(et_exp[0])
            # Update elastic doc with TOEC stuff
            tec = et_exp.thermal_expansion_coeff(opt_struct, 300)
            elastic_doc.update({
                "elastic_tensor_expansion":
                elastic_sanitize(et_exp),
                "elastic_tensor_expansion_original":
                elastic_sanitize(et_exp_raw),
                "thermal_expansion_tensor":
                tec,
                "average_linear_thermal_expansion":
                np.trace(tec) / 3
            })
        et = et_fit.voigt_symmetrized.convert_to_ieee(opt_struct)
        vasp_input = opt_task['input']
        if 'structure' in vasp_input:
            vasp_input.pop('structure')
        completed_at = max([d['completed_at'] for d in defo_tasks])
        elastic_doc.update({
            "optimization_task_id":
            opt_task['task_id'],
            "optimization_dir_name":
            opt_task['dir_name'],
            "cauchy_stresses":
            stresses,
            "strains":
            strains,
            "elastic_tensor":
            elastic_sanitize(et.zeroed(0.01).round(0)),
            # Convert compliance to 10^-12 Pa
            "compliance_tensor":
            elastic_sanitize(et.compliance_tensor * 1000),
            "elastic_tensor_original":
            elastic_sanitize(et_fit),
            "optimized_structure":
            opt_struct,
            "spacegroup":
            input_struct.get_space_group_info()[0],
            "input_structure":
            input_struct,
            "completed_at":
            completed_at,
            "optimization_input":
            vasp_input,
            "order":
            order,
            "pretty_formula":
            opt_struct.composition.reduced_formula
        })
        # Add magnetic type
        mag = CollinearMagneticStructureAnalyzer(opt_struct).ordering.value
        elastic_doc['magnetic_type'] = mag_types[mag]
        try:
            prop_dict = et.get_structure_property_dict(opt_struct)
            prop_dict.pop('structure')
        except ValueError:
            logger.debug("Negative K or G found, structure property "
                         "dict not computed")
            prop_dict = et.property_dict
        for k, v in prop_dict.items():
            if k in ['homogeneous_poisson', 'universal_anisotropy']:
                prop_dict[k] = np.round(v, 2)
            else:
                prop_dict[k] = np.round(v, 0)
        elastic_doc.update(prop_dict)
        # Update with state and warnings
        state, warnings = get_state_and_warnings(elastic_doc)
        elastic_doc.update({"state": state, "warnings": warnings})
        # TODO: add kpoints params?
        return elastic_doc
    else:
        logger.info("insufficient valid strains for {}".format(
            opt_task['formula_pretty']))
        return None
Example #23
0
def group_by_material_id(materials_dict,
                         docs,
                         structure_key='structure',
                         tol=1e-6,
                         loosen=True,
                         structure_matcher=None):
    """
    Groups a collection of documents by material id
    as found in a materials collection

    Args:
        materials_dict (dict): dictionary of structures keyed by task_id
        docs ([dict]): list of documents
        tol (float): tolerance for lattice grouping
        loosen (bool): whether or not to loosen criteria if no matches are
            found
        structure_key (string): mongo-style key of documents where structures
            are contained (e. g. input.structure or output.structure)
        structure_matcher (StructureMatcher): structure
            matcher for finding equivalent structures

    Returns:
        documents grouped by task_id from the materials
        collection
    """
    # Structify all input structures
    materials_dict = {
        mp_id: Structure.from_dict(struct)
        for mp_id, struct in materials_dict.items()
    }
    # Get magnetic phases
    mags = {}
    # TODO: refactor this with data from materials collection?
    for mp_id, structure in materials_dict.items():
        mag = CollinearMagneticStructureAnalyzer(structure).ordering.value
        mags[mp_id] = mag_types[mag]
    docs_by_mp_id = {}
    for doc in docs:
        sm = structure_matcher or StructureMatcher(
            comparator=ElementComparator())
        structure = Structure.from_dict(get(doc, structure_key))
        input_sg_symbol = SpacegroupAnalyzer(structure,
                                             0.1).get_space_group_symbol()
        # Iterate over all candidates until match is found
        matches = {
            c_id: candidate
            for c_id, candidate in materials_dict.items()
            if sm.fit(candidate, structure)
        }
        niter = 0
        if not matches:
            # First try with conventional structure then loosen match criteria
            convs = {
                c_id:
                SpacegroupAnalyzer(candidate,
                                   0.1).get_conventional_standard_structure()
                for c_id, candidate in materials_dict.items()
            }
            matches = {
                c_id: candidate
                for c_id, candidate in materials_dict.items()
                if sm.fit(convs[c_id], structure)
            }
            while len(matches) < 1 and niter < 4 and loosen:
                logger.debug("Loosening sm criteria")
                sm = StructureMatcher(sm.ltol * 2,
                                      sm.stol * 2,
                                      sm.angle_tol * 2,
                                      primitive_cell=False)
                matches = {
                    c_id: candidate
                    for c_id, candidate in materials_dict.items()
                    if sm.fit(convs[c_id], structure)
                }
                niter += 1
        if matches:
            # Get best match by spacegroup, then mag phase, then closest density
            mag = doc['magnetic_type']

            def sort_criteria(m_id):
                dens_diff = abs(matches[m_id].density - structure.density)
                sg = matches[m_id].get_space_group_info(0.1)[0]
                mag_id = mags[m_id]
                # prefer explicit matches, allow non-mag materials match with FM tensors
                if mag_id == mag:
                    mag_match = 0
                elif mag_id == 'Non-magnetic' and mag == 'FM':
                    mag_match = 1
                else:
                    mag_match = 2
                return (sg != input_sg_symbol, mag_match, dens_diff)

            sorted_ids = sorted(list(matches.keys()), key=sort_criteria)
            mp_id = sorted_ids[0]
            if mp_id in docs_by_mp_id:
                docs_by_mp_id[mp_id].append(doc)
            else:
                docs_by_mp_id[mp_id] = [doc]
        else:
            logger.debug("No material match found for formula {}".format(
                structure.composition.reduced_formula))
    return docs_by_mp_id
Example #24
0
    def test_matches(self):
        self.assertTrue(self.NiO.matches(self.NiO_AFM_111))
        self.assertTrue(self.NiO.matches(self.NiO_AFM_001))

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

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

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