Пример #1
0
    def _get_valences(self):
        """
        Computes ionic valences of elements for all sites in the structure.
        """
        try:
            bv = BVAnalyzer()
            self._structure = bv.get_oxi_state_decorated_structure(
                self._structure)
            valences = bv.get_valences(self._structure)
        except:
            try:
                bv = BVAnalyzer(symm_tol=0.0)
                self._structure = bv.get_oxi_state_decorated_structure(
                    self._structure)
                valences = bv.get_valences(self._structure)
            except:
                valences = [0] * self._structure.num_sites
                #raise

        #el = [site.specie.symbol for site in self._structure.sites]
        #el = [site.species_string for site in self._structure.sites]
        #el = [site.specie for site in self._structure.sites]
        #valence_dict = dict(zip(el, valences))
        #print valence_dict
        return valences
Пример #2
0
 def __init__(self,
              symm_tol=0.1,
              max_radius=4,
              max_permutations=100000,
              distance_scale_factor=1.015):
     self.analyzer = BVAnalyzer(symm_tol, max_radius, max_permutations,
                                distance_scale_factor)
Пример #3
0
 def __init__(
     self,
     symm_tol=0.1,
     max_radius=4,
     max_permutations=100000,
     distance_scale_factor=1.015,
 ):
     """
     Args:
         symm_tol (float): Symmetry tolerance used to determine which sites are
             symmetrically equivalent. Set to 0 to turn off symmetry.
         max_radius (float): Maximum radius in Angstrom used to find nearest
             neighbors.
         max_permutations (int): Maximum number of permutations of oxidation
             states to test.
         distance_scale_factor (float): A scale factor to be applied. This is
             useful for scaling distances, esp in the case of
             calculation-relaxed structures, which may tend to under (GGA) or
             over bind (LDA). The default of 1.015 works for GGA. For
             experimental structure, set this to 1.
     """
     self.symm_tol = symm_tol
     self.max_radius = max_radius
     self.max_permutations = max_permutations
     self.distance_scale_factor = distance_scale_factor
     self.analyzer = BVAnalyzer(symm_tol, max_radius, max_permutations, distance_scale_factor)
Пример #4
0
    def from_py_struct(structure: pymatgen.core.Structure):
        """Create a SmactStructure from a pymatgen Structure object.

        Args:
            structure: A pymatgen Structure.

        Returns:
            :class:`~.SmactStructure`

        """
        if not isinstance(structure, pymatgen.core.Structure):
            raise TypeError("Structure must be a pymatgen.core.Structure instance.")

        bva = BVAnalyzer()
        struct = bva.get_oxi_state_decorated_structure(structure)

        sites, species = SmactStructure.__parse_py_sites(struct)

        lattice_mat = struct.lattice.matrix

        lattice_param = 1.0

        return SmactStructure(
          species,
          lattice_mat,
          sites,
          lattice_param,
          sanitise_species=True, )
Пример #5
0
    def tersoff_potential(self, structure):
        """
        Generate the species, tersoff potential lines for an oxide structure

        Args:
            structure:
                pymatgen.core.structure.Structure
        """
        bv = BVAnalyzer()
        el = [site.species_string for site in structure.sites]
        valences = bv.get_valences(structure)
        el_val_dict = dict(zip(el, valences))

        gin = "species \n"
        qerfstring = "qerfc\n"

        for key in el_val_dict.keys():
            if key != "O" and el_val_dict[key] % 1 != 0:
                raise SystemError("Oxide has mixed valence on metal")
            specie_string = key + " core " + str(el_val_dict[key]) + "\n"
            gin += specie_string
            qerfstring += key + " " + key + " 0.6000 10.0000 \n"

        gin += "# noelectrostatics \n Morse \n"
        met_oxi_ters = Tersoff_pot().data
        for key in el_val_dict.keys():
            if key != "O":
                metal = key + "(" + str(int(el_val_dict[key])) + ")"
                ters_pot_str = met_oxi_ters[metal]
                gin += ters_pot_str

        gin += qerfstring
        return gin
Пример #6
0
    def __init__(self, defect):
        """
        Args:
            defect(Defect): pymatgen Defect object
        """
        self.defect = defect

        try:
            bv = BVAnalyzer()
            struct_valences = bv.get_valences(self.defect.bulk_structure)
            site_index = self.defect.bulk_structure.get_sites_in_sphere(
                self.defect.site.coords, 0.1, include_index=True)[0][2]
            def_site_valence = struct_valences[site_index]
        except Exception:  # sometimes valences cant be assigned
            def_site_valence = 0

        if isinstance(defect, Vacancy):
            self.charges = [-1 * def_site_valence]
        elif isinstance(defect, Substitution):
            #(minimize difference with host site specie)
            probable_chgs = [
                ox - def_site_valence
                for ox in self.defect.site.specie.oxidation_states
            ]
            self.charges = [min(probable_chgs, key=abs)]
        elif isinstance(defect, Interstitial):
            self.charges = [0]
        else:
            raise ValueError("Defect Type not recognized.")
Пример #7
0
    def predict(self, structure, ref_structure, test_isostructural=True):
        """
        Given a structure, returns back the predicted volume.

        Args:
            structure (Structure): structure w/unknown volume
            ref_structure (Structure): A reference structure with a similar
                structure but different species.
            test_isostructural (bool): Whether to test that the two
                structures are isostructural. This algo works best for
                isostructural compounds. Defaults to True.

        Returns:
            a float value of the predicted volume
        """
        if not is_ox(structure):
            a = BVAnalyzer()
            structure = a.get_oxi_state_decorated_structure(structure)
        if not is_ox(ref_structure):
            a = BVAnalyzer()
            ref_structure = a.get_oxi_state_decorated_structure(ref_structure)

        if test_isostructural:
            m = StructureMatcher()
            mapping = m.get_best_electronegativity_anonymous_mapping(
                structure, ref_structure)
            if mapping is None:
                raise ValueError("Input structures do not match!")

        comp = structure.composition
        ref_comp = ref_structure.composition

        numerator = 0
        denominator = 0

        # Here, the 1/3 factor on the composition accounts for atomic
        # packing. We want the number per unit length.

        # TODO: AJ doesn't understand the (1/3). It would make sense to him
        # if you were doing atomic volume and not atomic radius
        for k, v in comp.items():
            numerator += k.ionic_radius * v**(1 / 3)
        for k, v in ref_comp.items():
            denominator += k.ionic_radius * v**(1 / 3)

        # The scaling factor is based on lengths. We apply a power of 3.
        return ref_structure.volume * (numerator / denominator)**3
Пример #8
0
def add_bv_structure(doc):
    struc = Structure.from_dict(doc["structure"])
    try:
        bva = BVAnalyzer()
        bv_struct = bva.get_oxi_state_decorated_structure(struc)
        doc["bv_structure"] = bv_struct.as_dict()
    except Exception as e:
        print("BVAnalyzer error: {}".format(e))
Пример #9
0
def get_basic_analysis_and_error_checks(d, max_force_threshold=0.5,
                                        volume_change_threshold=0.2):

    initial_vol = d["input"]["crystal"]["lattice"]["volume"]
    final_vol = d["output"]["crystal"]["lattice"]["volume"]
    delta_vol = final_vol - initial_vol
    percent_delta_vol = delta_vol / initial_vol
    coord_num = get_coordination_numbers(d)
    calc = d["calculations"][-1]
    gap = calc["output"]["bandgap"]
    cbm = calc["output"]["cbm"]
    vbm = calc["output"]["vbm"]
    is_direct = calc["output"]["is_gap_direct"]

    warning_msgs = []
    error_msgs = []

    if abs(percent_delta_vol) > volume_change_threshold:
        warning_msgs.append("Volume change > {}%"
                            .format(volume_change_threshold * 100))

    bv_struct = Structure.from_dict(d["output"]["crystal"])
    try:
        bva = BVAnalyzer()
        bv_struct = bva.get_oxi_state_decorated_structure(bv_struct)
    except ValueError as e:
        logger.error("Valence cannot be determined due to {e}."
                     .format(e=e))
    except Exception as ex:
        logger.error("BVAnalyzer error {e}.".format(e=str(ex)))

    max_force = None
    if d["state"] == "successful" and \
            d["calculations"][0]["input"]["parameters"].get("NSW", 0) > 0:
        # handle the max force and max force error
        max_force = max([np.linalg.norm(a)
                        for a in d["calculations"][-1]["output"]
                        ["ionic_steps"][-1]["forces"]])

        if max_force > max_force_threshold:
            error_msgs.append("Final max force exceeds {} eV"
                              .format(max_force_threshold))
            d["state"] = "error"

        s = Structure.from_dict(d["output"]["crystal"])
        if not s.is_valid():
            error_msgs.append("Bad structure (atoms are too close!)")
            d["state"] = "error"

    return {"delta_volume": delta_vol,
            "max_force": max_force,
            "percent_delta_volume": percent_delta_vol,
            "warnings": warning_msgs,
            "errors": error_msgs,
            "coordination_numbers": coord_num,
            "bandgap": gap, "cbm": cbm, "vbm": vbm,
            "is_gap_direct": is_direct,
            "bv_structure": bv_struct.as_dict()}
Пример #10
0
    def calc(self, item):
        s = Structure.from_dict(item["structure"])

        d = {
            "pymatgen_version": pymatgen_version,
            "successful": False,
            "bond_valence": {
                "structure": item["structure"],
                "method": None
            },
        }

        try:
            bva = BVAnalyzer()
            valences = bva.get_valences(s)
            possible_species = {
                str(Specie(s[idx].specie, oxidation_state=valence))
                for idx, valence in enumerate(valences)
            }

            d["successful"] = True
            s.add_oxidation_state_by_site(valences)

            d["bond_valence"] = {
                "possible_species": list(possible_species),
                "possible_valences": valences,
                "method": "BVAnalyzer",
                "structure": s.as_dict(),
            }

        except Exception as e:
            self.logger.error("BVAnalyzer failed with: {}".format(e))

            try:
                first_oxi_state_guess = s.composition.oxi_state_guesses(
                    max_sites=-50)[0]
                valences = [
                    first_oxi_state_guess[site.species_string] for site in s
                ]
                possible_species = {
                    str(Specie(el, oxidation_state=valence))
                    for el, valence in first_oxi_state_guess.items()
                }

                d["successful"] = True
                s.add_oxidation_state_by_site(valences)

                d["bond_valence"] = {
                    "possible_species": list(possible_species),
                    "possible_valences": valences,
                    "method": "oxi_state_guesses",
                    "structure": s.as_dict(),
                }
            except Exception as e:
                self.logger.error(
                    "Oxidation state guess failed with: {}".format(e))

        return d
Пример #11
0
 def setUp(self):
     mgo_latt = [[4.212, 0, 0], [0, 4.212, 0], [0, 0, 4.212]]
     mgo_specie = ["Mg", 'O'] * 4
     mgo_frac_cord = [[0, 0, 0], [0.5, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0],
                      [0.5, 0, 0.5], [0, 0, 0.5], [0, 0.5, 0.5],
                      [0.5, 0.5, 0.5]]
     self.mgo_uc = Structure(mgo_latt, mgo_specie, mgo_frac_cord, True,
                             True)
     bv = BVAnalyzer()
     val = bv.get_valences(self.mgo_uc)
     el = [site.species_string for site in self.mgo_uc.sites]
     self.val_dict = dict(zip(el, val))
Пример #12
0
    def setUp(self):
        filepath = os.path.join(test_dir, 'POSCAR')
        p = Poscar.from_file(filepath)
        self.structure = p.structure
        bv = BVAnalyzer()
        valences = bv.get_valences(self.structure)
        el = [site.species_string for site in self.structure.sites]
        valence_dict = dict(zip(el, valences))
        self.rad_dict = {}
        for k, v in valence_dict.items():
            self.rad_dict[k] = float(Specie(k, v).ionic_radius)

        assert len(self.rad_dict) == len(self.structure.composition)
Пример #13
0
 def setUp(self):
     filepath1 = os.path.join(PymatgenTest.TEST_FILES_DIR, "Li2O.cif")
     p = CifParser(filepath1).get_structures(False)[0]
     bv = BVAnalyzer()
     valences = bv.get_valences(p)
     el = [site.species_string for site in p.sites]
     val_dict = dict(zip(el, valences))
     self._radii = {}
     for k, v in val_dict.items():
         k1 = re.sub(r"[1-9,+,\-]", "", k)
         self._radii[k1] = float(Species(k1, v).ionic_radius)
     p.remove(0)
     self._vac_struct = p
Пример #14
0
 def setUp(self):
     filepath1 = os.path.join(test_dir, 'Li2O.cif')
     p = CifParser(filepath1).get_structures(False)[0]
     bv = BVAnalyzer()
     valences = bv.get_valences(p)
     el = [site.species_string for site in p.sites]
     val_dict = dict(zip(el, valences))
     self._radii = {}
     for k, v in val_dict.items():
         k1 = re.sub(r'[1-9,+,\-]', '', k)
         self._radii[k1] = float(Specie(k1, v).ionic_radius)
     p.remove(0)
     self._vac_struct = p
Пример #15
0
    def from_mp(
        species: List[Union[Tuple[str, int, int], Tuple[smact.Species, int]]],
        api_key: str,
    ):
        """Create a SmactStructure using the first Materials Project entry for a composition.

        Args:
            species: See :meth:`~.__init__`.
            api_key: A www.materialsproject.org API key.

        Returns:
            :class:`~.SmactStructure`

        """
        sanit_species = SmactStructure._sanitise_species(species)

        with MPRester(api_key) as m:
            eles = SmactStructure._get_ele_stoics(sanit_species)
            formula = "".join(f"{ele}{stoic}" for ele, stoic in eles.items())
            structs = m.query(
                criteria={"reduced_cell_formula": formula},
                properties=["structure"],
            )

            if len(structs) == 0:
                raise ValueError(
                    "Could not find composition in Materials Project Database, "
                    "please supply a structure.")

            struct = structs[0][
                'structure']  # Default to first found structure

        if 0 not in (spec[1]
                     for spec in sanit_species):  # If everything's charged
            bva = BVAnalyzer()
            struct = bva.get_oxi_state_decorated_structure(struct)

        lattice_mat = struct.lattice.matrix

        lattice_param = 1.0  # TODO Use actual lattice parameter

        sites, _ = SmactStructure.__parse_py_sites(struct)

        return SmactStructure(
            sanit_species,
            lattice_mat,
            sites,
            lattice_param,
            sanitise_species=False,
        )
Пример #16
0
 def setUp(self):
     filepath = os.path.join(test_dir, 'POSCAR')
     p = Poscar.from_file(filepath)
     self.structure = p.structure
     bv = BVAnalyzer()
     self.structure = bv.get_oxi_state_decorated_structure(self.structure)
     valences = bv.get_valences(self.structure)
     radii = []
     for i in range(len(valences)):
         el = self.structure.sites[i].specie.symbol
         radius = Specie(el, valences[i]).ionic_radius
         radii.append(radius)
     el = [site.species_string for site in self.structure.sites]
     self.rad_dict = dict(zip(el, radii))
     for el in self.rad_dict.keys():
         print((el, self.rad_dict[el].real))
Пример #17
0
    def setUp(self):
        with open("mp-7000.json", "r") as f:
            dict_lse = json.load(f)
        lse = LightStructureEnvironments.from_dict(dict_lse)
        struct = lse.structure
        bva = BVAnalyzer()
        valences = bva.get_valences(structure=struct)
        lgf = LocalGeometryFinder()
        lgf.setup_structure(structure=struct)
        se = lgf.compute_structure_environments(maximum_distance_factor=1.41, only_cations=False, valences=valences)
        strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters()
        self.lse = LightStructureEnvironments.from_structure_environments(strategy=strategy, structure_environments=se)

        with open("mp-5634.json", "r") as f:
            dict_lse2 = json.load(f)
        self.lse2 = LightStructureEnvironments.from_dict(dict_lse2)
Пример #18
0
    def _get_valences(self):
        """
        Computes ionic valences of elements for all sites in the structure.
        """
        bv = BVAnalyzer()
        try:
            valences = bv.get_valences(self._structure)
        except:
            err_str = "BVAnalyzer failed. The defect effective charge, and"
            err_str += " volume and surface area may not work"
            print err_str
            raise LookupError()

        el = [site.species_string for site in self.structure.sites]
        valence_dict = dict(zip(el, valences))
        return valence_dict
    def setUp(self):
        """
        Setup MgO rocksalt structure for testing Vacancy
        """
        mgo_latt = [[4.212, 0, 0], [0, 4.212, 0], [0, 0, 4.212]]
        mgo_specie = ["Mg"] * 4 + ["O"] * 4
        mgo_frac_cord = [[0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5],
                         [0.5, 0, 0], [0, 0.5, 0], [0, 0, 0.5], [0.5, 0.5, 0.5]]
        self._mgo_uc = Structure(mgo_latt, mgo_specie, mgo_frac_cord, True,
                                 True)

        bv = BVAnalyzer()
        self._mgo_uc = bv.get_oxi_state_decorated_structure(self._mgo_uc)
        self._mgo_val_rad_eval = ValenceIonicRadiusEvaluator(self._mgo_uc)
        self._mgo_val = self._mgo_val_rad_eval.valences
        self._mgo_rad = self._mgo_val_rad_eval.radii
        self._mgo_vac = Vacancy(self._mgo_uc, self._mgo_val, self._mgo_rad)
Пример #20
0
def get_oxidation_states(stru):
    try:
        valences = BVAnalyzer().get_valences(stru)
        elems = [i for i in stru.species]
        oxids = set(zip(elems,valences))
        reduced_elems = set(elems)
        out={i:[] for i in reduced_elems}
        for i in oxids:
            if i[1] not in out[i[0]]:
                out[i[0]].append(i[1])
        out1 = {i:np.sign(out[i][0])*np.max(np.abs(out[i])) for i in out}
        return out1
    except:
        oxi_states = {}
        oxi_range = {}
        reduc_comp = stru.composition.reduced_composition
        oxid_states = get_oxid_states_composition(reduc_comp)
        return oxid_states
Пример #21
0
    def get_atomic_radii(self):
        if not self.pymatgen_radii:
            return None

        try:
            bv = BVAnalyzer()
            valences = bv.get_valences(self._structure)
            elements = [site.species_string for site in self._structure.sites]

            valence_dict = dict(zip(elements, valences))
            radii = {}
            for k, v in valence_dict.items():
                radii[k] = float(Specie(k, v).ionic_radius)

        except (ValueError, TypeError) as e:
            radii = None

        return radii
Пример #22
0
    def __init__(self, structure, include_bv_charge=False):
        """
        Initializes a Vacancy Generator
        Args:
            structure(Structure): pymatgen structure object
        """
        self.structure = structure
        self.include_bv_charge = include_bv_charge

        # Find equivalent site list
        sga = SpacegroupAnalyzer(self.structure)
        self.symm_structure = sga.get_symmetrized_structure()
        self.equiv_site_seq = list(self.symm_structure.equivalent_sites)

        self.struct_valences = None
        if self.include_bv_charge:
            bv = BVAnalyzer()
            self.struct_valences = bv.get_valences(self.structure)
Пример #23
0
    def get_valences(self):
        """
        Uses Pymatgen to obtain likely valence states of every element in structure

        Returns
            vals: dictionary of average valence state for every element in composition
        """

        struct = self.structure
        bv = BVAnalyzer()
        try:
            valences = bv.get_valences(struct)
            struct = bv.get_oxi_state_decorated_structure(struct)
        except:
            return None

        if isinstance(valences[0], list):
            valences = [item for sublist in valences for item in sublist]

        stoich = defaultdict(int)
        for site in struct.as_dict()['sites']:
            elem = site['species'][0]['element']
            stoich[elem] += 1

        vals = {}

        for spec in stoich.keys():
            vals[spec] = 0.0

        for atom in range(len(struct)):
            try:
                vals[struct.as_dict()['sites'][atom]['species'][0]
                     ['element']] += valences[atom]
            except Exception as e:
                print("Trouble with {}".format(struct.formula))
                print('Do you have partial occupancies?')
                return None

        for spec in vals:
            vals[spec] = vals[spec] / stoich[spec]
            vals[spec] = int(round(vals[spec]))

        return vals
Пример #24
0
    def get_coordsites_min_max_charge(self, n):
        """
        Minimum and maximum charge of sites surrounding the vacancy site.

        Args:
            n: Index of vacancy list
        """
        bv = BVAnalyzer()
        struct_valences = bv.get_valences(self._structure)
        coordinated_site_valences = []

        def _get_index(site):
            for i in range(len(self._structure.sites)):
                if site.is_periodic_image(self._structure.sites[i]):
                    return i
            raise ValueError("Site not found")

        for site in self._defect_coord_sites[n]:
            ind = _get_index(site)
            coordinated_site_valences.append(struct_valences[ind])
        coordinated_site_valences.sort()
        return coordinated_site_valences[0], coordinated_site_valences[-1]
Пример #25
0
def get_basic_analysis_and_error_checks(d):
    initial_vol = d["input"]["crystal"]["lattice"]["volume"]
    final_vol = d["output"]["crystal"]["lattice"]["volume"]
    delta_vol = final_vol - initial_vol
    percent_delta_vol = delta_vol / initial_vol
    coord_num = get_coordination_numbers(d)
    calc = d["calculations"][-1]
    gap = calc["output"]["bandgap"]
    cbm = calc["output"]["cbm"]
    vbm = calc["output"]["vbm"]
    is_direct = calc["output"]["is_gap_direct"]

    if abs(percent_delta_vol) > 0.20:
        warning_msgs = ["Volume change > 20%"]
    else:
        warning_msgs = []

    bv_struct = Structure.from_dict(d["output"]["crystal"])
    try:
        bva = BVAnalyzer()
        bv_struct = bva.get_oxi_state_decorated_structure(bv_struct)
    except ValueError as e:
        logger.error("Valence cannot be determined due to {e}.".format(e=e))
    except Exception as ex:
        logger.error("BVAnalyzer error {e}.".format(e=str(ex)))

    return {
        "delta_volume": delta_vol,
        "percent_delta_volume": percent_delta_vol,
        "warnings": warning_msgs,
        "coordination_numbers": coord_num,
        "bandgap": gap,
        "cbm": cbm,
        "vbm": vbm,
        "is_gap_direct": is_direct,
        "bv_structure": bv_struct.to_dict
    }
Пример #26
0
    def process_item(self, item):
        s = Structure.from_dict(item['structure'])
        try:
            bva = BVAnalyzer()
            valences = bva.get_valences(s)
            possible_species = {
                str(Specie(s[idx].specie, oxidation_state=valence))
                for idx, valence in enumerate(valences)
            }

            method = "BVAnalyzer"
        except ValueError:
            try:
                first_oxi_state_guess = s.composition.oxi_state_guesses()[0]
                valences = [
                    first_oxi_state_guess[site.species_string] for site in s
                ]
                possible_species = {
                    str(Specie(el, oxidation_state=valence))
                    for el, valence in first_oxi_state_guess.items()
                }
                method = "oxi_state_guesses"
            except:
                return {
                    "task_id": item['task_id'],
                    "pymatgen_version": pymatgen_version,
                    "successful": False
                }

        return {
            "task_id": item['task_id'],
            "possible_species": list(possible_species),
            "possible_valences": valences,
            "method": method,
            "pymatgen_version": pymatgen_version,
            "successful": True
        }
def Calc_Ewald(pmg_struct, formal_val=[]):
    """
    input: pmg_struct: pymatgen structure
           formal_val: list - list of valence for each atom

    TBD: use input formal valence list to decorate the structure
    """
    # GET valence list
    if len(formal_val)==0:
        bv_analyzer=BVAnalyzer(max_radius=4) #max_radius default is 4
        formal_val=bv_analyzer.get_valences(pmg_struct)

    # default for cutoff is set to None
    real_cutoff = None
    rec_cutoff = None

    # Oxidation states are decorated automatically.  
    decorated_struct = bv_analyzer.get_oxi_state_decorated_structure(pmg_struct)
    NUM_sites=pmg_struct.num_sites
    
    # Per atom
    ewald_per_atom=1/NUM_sites*EwaldSummation(decorated_struct,
        real_space_cut=real_cutoff, recip_space_cut=rec_cutoff).total_energy
    return ewald_per_atom
Пример #28
0
    def get_analysis_and_structure(self,
                                   structure,
                                   calculate_valences=True,
                                   guesstimate_spin=False,
                                   op_threshold=0.1):
        """
        Obtain an analysis of a given structure and if it may be Jahn-Teller
        active or not. This is a heuristic, and may give false positives and
        false negatives (false positives are preferred).

        :param structure: input structure
        :param calculate_valences (bool): whether to attempt to calculate valences or not, structure
            should have oxidation states to perform analysis
        :param guesstimate_spin (bool): whether to guesstimate spin state from magnetic moments
            or not, use with caution
        :param op_threshold (float): threshold for order parameter above which to consider site
            to match an octahedral or tetrahedral motif, since Jahn-Teller structures can often be
            quite distorted, this threshold is smaller than one might expect
        :return (dict): analysis of structure, with key 'strength' which may be 'none', 'strong',
            'weak', or 'unknown'
        """

        structure = structure.get_primitive_structure()

        if calculate_valences:
            bva = BVAnalyzer()
            structure = bva.get_oxi_state_decorated_structure(structure)

        # no point testing multiple equivalent sites, doesn't make any difference to analysis
        # but makes returned
        symmetrized_structure = SpacegroupAnalyzer(
            structure).get_symmetrized_structure()

        # to detect structural motifs of a given site
        op = LocalStructOrderParams(['oct', 'tet'])

        # dict of site index to the Jahn-Teller analysis of that site
        jt_sites = []
        non_jt_sites = []

        for indices in symmetrized_structure.equivalent_indices:

            idx = indices[0]
            site = symmetrized_structure[idx]

            # only interested in sites with oxidation states
            if isinstance(site.specie,
                          Specie) and site.specie.element.is_transition_metal:

                # get motif around site
                order_params = op.get_order_parameters(symmetrized_structure,
                                                       idx)

                if order_params[0] > order_params[1] and order_params[
                        0] > op_threshold:
                    motif = 'oct'
                    motif_order_parameter = order_params[0]
                elif order_params[1] > op_threshold:
                    motif = 'tet'
                    motif_order_parameter = order_params[1]
                else:
                    motif = 'unknown'
                    motif_order_parameter = None

                if motif == "oct" or motif == "tet":

                    # guess spin of metal ion
                    if guesstimate_spin and 'magmom' in site.properties:

                        # estimate if high spin or low spin
                        magmom = site.properties['magmom']
                        spin_state = self._estimate_spin_state(
                            site.specie, motif, magmom)
                    else:
                        spin_state = "unknown"

                    magnitude = self.get_magnitude_of_effect_from_species(
                        site.specie, spin_state, motif)

                    if magnitude != "none":

                        ligands = get_neighbors_of_site_with_index(
                            structure, idx, approach="min_dist", delta=0.15)
                        ligand_bond_lengths = [
                            ligand.distance(structure[idx])
                            for ligand in ligands
                        ]
                        ligands_species = list(
                            set([str(ligand.specie) for ligand in ligands]))
                        ligand_bond_length_spread = max(ligand_bond_lengths) - \
                                                    min(ligand_bond_lengths)

                        def trim(f):
                            # avoid storing to unreasonable precision, hurts readability
                            return float("{:.4f}".format(f))

                        # to be Jahn-Teller active, all ligands have to be the same
                        if len(ligands_species) == 1:
                            jt_sites.append({
                                'strength':
                                magnitude,
                                'motif':
                                motif,
                                'motif_order_parameter':
                                trim(motif_order_parameter),
                                'spin_state':
                                spin_state,
                                'species':
                                str(site.specie),
                                'ligand':
                                ligands_species[0],
                                'ligand_bond_lengths': [
                                    trim(length)
                                    for length in ligand_bond_lengths
                                ],
                                'ligand_bond_length_spread':
                                trim(ligand_bond_length_spread),
                                'site_indices':
                                indices
                            })

                    # store reasons for not being J-T active
                    else:
                        non_jt_sites.append({
                            'site_indices':
                            indices,
                            'strength':
                            "none",
                            'reason':
                            "Not Jahn-Teller active for this "
                            "electronic configuration."
                        })
                else:
                    non_jt_sites.append({
                        'site_indices': indices,
                        'strength': "none",
                        'reason': "motif is {}".format(motif)
                    })

        # perform aggregation of all sites
        if jt_sites:
            analysis = {'active': True}
            # if any site could exhibit 'strong' Jahn-Teller effect
            # then mark whole structure as strong
            strong_magnitudes = [
                site['strength'] == "strong" for site in jt_sites
            ]
            if any(strong_magnitudes):
                analysis['strength'] = "strong"
            else:
                analysis['strength'] = "weak"
            analysis['sites'] = jt_sites
            return analysis, structure
        else:
            return {'active': False, 'sites': non_jt_sites}, structure
Пример #29
0
    def buckingham_potential(self, structure, val_dict=None):
        """
        Generate species, buckingham, and spring options for an oxide structure
        using the parameters in default libraries.

        Ref:
            1. G.V. Lewis and C.R.A. Catlow, J. Phys. C: Solid State Phys.,
               18, 1149-1161 (1985)
            2. T.S.Bush, J.D.Gale, C.R.A.Catlow and P.D. Battle,
               J. Mater Chem., 4, 831-837 (1994)

        Args:
            structure:
                pymatgen.core.structure.Structure
            val_dict (Needed if structure is not charge neutral)
                El:valence dictionary, where El is element.
        """
        if not val_dict:
            bv = BVAnalyzer()
            el = [site.species_string for site in structure.sites]
            valences = bv.get_valences(structure)
            val_dict = dict(zip(el, valences))

        #Try bush library first
        bpb = BuckinghamPotBush()
        bpl = BuckinghamPotLewis()
        gin = ""
        for key in val_dict.keys():
            use_bush = True
            el = re.sub('[1-9,+,\-]', '', key)
            if el not in bpb.species_dict.keys():
                use_bush = False
            elif val_dict[key] != bpb.species_dict[el]['oxi']:
                use_bush = False
            if use_bush:
                gin += "species \n"
                gin += bpb.species_dict[el]['inp_str']
                gin += "buckingham \n"
                gin += bpb.pot_dict[el]
                gin += "spring \n"
                gin += bpb.spring_dict[el]
                continue

            #Try lewis library next if element is not in bush
            #use_lewis = True
            if el != "O":  # For metals the key is "Metal_OxiState+"
                k = el + '_' + str(int(val_dict[key])) + '+'
                if k not in bpl.species_dict.keys():
                    #use_lewis = False
                    raise GulpError("Element {} not in library".format(k))
                gin += "species\n"
                gin += bpl.species_dict[k]
                gin += "buckingham\n"
                gin += bpl.pot_dict[k]
            else:
                gin += "species\n"
                k = "O_core"
                gin += bpl.species_dict[k]
                k = "O_shel"
                gin += bpl.species_dict[k]
                gin += "buckingham\n"
                gin += bpl.pot_dict[key]
                gin += 'spring\n'
                gin += bpl.spring_dict[key]
        return gin
Пример #30
0
sites_Na = [[55, 59, 64, 68], [51, 54, 60, 63], [56, 58, 65, 67], [52, 53, 62, 64], [57, 57, 67, 67]]
directories = ["c5", "c4", "c3", "c2", "c1", "relax", "t1", "t2", "t3", "t4", "t5"]
o_fig = o_file = 'NaObonds'
all_dist_NaO = []# (strain,layers,atom,bond)

for element in directories:
"""
This populates the distances array with all the Na-O bonds detected by the CrystalNN routine.
The innermost index varies in size as each atom in sites_Na can have more or less neigbours.
The second innermost index size is always 4 due to array symmetry reasons. It registers the number of Na atoms at a layer
The third innermost index is 5. It is the number of layers in the slab.
The first index registers the strain steps taken.
"""
    input_struct = Structure.from_file( element+"/CONTCAR" )
    ox_struct = BVAnalyzer().get_oxi_state_decorated_structure( input_struct )
    dist_NaO = []
    for layer, atom_list in enumerate( sites_Na ):
        layer_atoms = []
        for atom in atom_list:
            atom_neigbors = CrystalNN( cation_anion=True ).get_nn( ox_struct, atom-1 )
            distances = []
            for neighbor in range( len( atom_neigbors ) ):
                distances.append( atom_neigbors[neighbor].distance( input_struct[atom-1] ) )
            layer_atoms.append( distances )
        dist_NaO.append( layer_atoms )
    all_dist_NaO.append( dist_NaO )

avrg_bond = np.zeros( [len(all_dist_NaO), len(all_dist_NaO[0]), len(all_dist_NaO[0][0])] )
avrg_layer = np.zeros( [len(all_dist_NaO), len(all_dist_NaO[0])] )
stdev_bond = np.zeros( [len(all_dist_NaO), len(all_dist_NaO[0]), len(all_dist_NaO[0][0])] )