Пример #1
0
    def _sanitize_input_structure(input_structure: Structure) -> Structure:
        """Sanitize our input structure by removing magnetic information
        and making primitive.

        Args:
          input_structure: Structure

        Returns: Structure

        """

        input_structure = input_structure.copy()

        # remove any annotated spin
        input_structure.remove_spin()

        # sanitize input structure: first make primitive ...
        input_structure = input_structure.get_primitive_structure(
            use_site_props=False)

        # ... and strip out existing magmoms, which can cause conflicts
        # with later transformations otherwise since sites would end up
        # with both magmom site properties and Species spins defined
        if "magmom" in input_structure.site_properties:
            input_structure.remove_site_property("magmom")

        return input_structure
Пример #2
0
    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
Пример #3
0
    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