예제 #1
0
    def __init__(self, lambda_table=None, alpha=-5):
        if lambda_table is not None:
            self._lambda_table = lambda_table
        else:
            module_dir = os.path.dirname(__file__)
            json_file = os.path.join(module_dir, 'data', 'lambda.json')
            with open(json_file) as f:
                self._lambda_table = json.load(f)

        # build map of specie pairs to lambdas
        self.alpha = alpha
        self._l = {}
        self.species = set()
        for row in self._lambda_table:
            if 'D1+' not in row:
                s1 = Specie.from_string(row[0])
                s2 = Specie.from_string(row[1])
                self.species.add(s1)
                self.species.add(s2)
                self._l[frozenset([s1, s2])] = float(row[2])

        # create Z and px
        self.Z = 0
        self._px = defaultdict(float)
        for s1, s2 in itertools.product(self.species, repeat=2):
            value = math.exp(self.get_lambda(s1, s2))
            self._px[s1] += value / 2
            self._px[s2] += value / 2
            self.Z += value
예제 #2
0
 def test_to_from_string(self):
     fe3 = Specie("Fe", 3, {"spin": 5})
     self.assertEqual(str(fe3), "Fe3+spin=5")
     fe = Specie.from_string("Fe3+spin=5")
     self.assertEqual(fe.spin, 5)
     mo0 = Specie("Mo", 0, {"spin": 5})
     self.assertEqual(str(mo0), "Mo0+spin=5")
     mo = Specie.from_string("Mo0+spin=4")
     self.assertEqual(mo.spin, 4)
 def as_dict(self):
     """
     JSON serialization of a SubStructureSpecie
     """
     sp = Specie(self.specie.symbol, self.specie.oxi_state, self.specie._properties)
     return {"@module": self.__class__.__module__,
             "@class": self.__class__.__name__,
             "specie": sp.as_dict(),
             "weight": self.weight}
예제 #4
0
    def from_dict(cls, d, lattice=None):
        """
        Create PeriodicSite from dict representation.

        Args:
            d (dict): dict representation of PeriodicSite
            lattice: Optional lattice to override lattice specified in d.
                Useful for ensuring all sites in a structure share the same
                lattice.

        Returns:
            PeriodicSite
        """
        atoms_n_occu = {}
        for sp_occu in d["species"]:
            if "oxidation_state" in sp_occu and Element.is_valid_symbol(
                    sp_occu["element"]):
                sp = Specie.from_dict(sp_occu)
            elif "oxidation_state" in sp_occu:
                sp = DummySpecie.from_dict(sp_occu)
            else:
                sp = Element(sp_occu["element"])
            atoms_n_occu[sp] = sp_occu["occu"]
        props = d.get("properties", None)
        lattice = lattice if lattice else Lattice.from_dict(d["lattice"])
        return cls(atoms_n_occu, d["abc"], lattice, properties=props)
예제 #5
0
 def from_dict(d):
     """
     Create Site from dict representation
     """
     atoms_n_occu = {}
     for sp_occu in d["species"]:
         sp = Specie.from_dict(sp_occu) if "oxidation_state" in sp_occu \
             else Element(sp_occu["element"])
         atoms_n_occu[sp] = sp_occu["occu"]
     props = d.get("properties", None)
     return Site(atoms_n_occu, d["xyz"], properties=props)
예제 #6
0
    def __init__(self, lambda_table=None, alpha=-5):
        #store the input table for the to_dict method
        self._lambda_table = lambda_table

        if not lambda_table:
            module_dir = os.path.dirname(__file__)
            json_file = os.path.join(module_dir, 'data', 'lambda.json')
            with open(json_file) as f:
                lambda_table = json.load(f)

        #build map of specie pairs to lambdas
        l = {}
        for row in lambda_table:
            if not row[0] == 'D1+' and not row[1] == 'D1+':
                s1 = Specie.from_string(row[0])
                s2 = Specie.from_string(row[1])
                l[frozenset([s1, s2])] = float(row[2])

        self._lambda = l
        self._alpha = alpha

        #create the partition functions Z and px
        sp_set = set()
        for key in self._lambda.keys():
            sp_set.update(key)
        px = dict.fromkeys(sp_set, 0.)
        Z = 0
        for s1, s2 in itertools.product(sp_set, repeat=2):
            value = math.exp(self._lambda.get(frozenset([s1, s2]),
                                              self._alpha))
            #not sure why the factor of 2 is here but it matches up
            #with BURP. BURP may actually be missing a factor of 2,
            #but it doesn't have a huge effect
            px[s1] += value / 2
            px[s2] += value / 2
            Z += value

        self._Z = Z
        self._px = px
        self.species_list = list(sp_set)
예제 #7
0
def find_connected_atoms(struct, tolerance=0.45, ldict=JmolNN().el_radius):
    """
    Finds the list of bonded atoms.

    Author: "Gowoon Cheon"
    Email: "*****@*****.**"

    Args:
        struct (Structure): Input structure
        tolerance: length in angstroms used in finding bonded atoms. Two atoms
            are considered bonded if (radius of atom 1) + (radius of atom 2) +
            (tolerance) < (distance between atoms 1 and 2). Default
            value = 0.45, the value used by JMol and Cheon et al.
        ldict: dictionary of bond lengths used in finding bonded atoms. Values
            from JMol are used as default

    Returns:
        (np.ndarray): A numpy array of shape (number of bonded pairs, 2); each
        row of is of the form [atomi, atomj]. atomi and atomj are the indices of
        the atoms in the input structure. If any image of atomj is bonded to
        atomi with periodic boundary conditions, [atomi, atomj] is included in
        the list. If atomi is bonded to multiple images of atomj, it is only
        counted once.
    """
    n_atoms = len(struct.species)
    fc = np.array(struct.frac_coords)
    species = list(map(str, struct.species))

    # in case of charged species
    for i, item in enumerate(species):
        if item not in ldict.keys():
            species[i] = str(Specie.from_string(item).element)
    latmat = struct.lattice.matrix
    connected_list = []

    for i in range(n_atoms):
        for j in range(i + 1, n_atoms):
            max_bond_length = ldict[species[i]] + ldict[species[j]] + tolerance
            add_ij = False
            for move_cell in itertools.product(
                    [0, 1, -1], [0, 1, -1], [0, 1, -1]):
                if not add_ij:
                    frac_diff = fc[j] + move_cell - fc[i]
                    distance_ij = np.dot(latmat.T, frac_diff)
                    if np.linalg.norm(distance_ij) < max_bond_length:
                        add_ij = True
            if add_ij:
                connected_list.append([i, j])
    return np.array(connected_list)
예제 #8
0
 def from_dict(cls, d):
     """
     Create Site from dict representation
     """
     atoms_n_occu = {}
     for sp_occu in d["species"]:
         if "oxidation_state" in sp_occu and Element.is_valid_symbol(
                 sp_occu["element"]):
             sp = Specie.from_dict(sp_occu)
         elif "oxidation_state" in sp_occu:
             sp = DummySpecie.from_dict(sp_occu)
         else:
             sp = Element(sp_occu["element"])
         atoms_n_occu[sp] = sp_occu["occu"]
     props = d.get("properties", None)
     return cls(atoms_n_occu, d["xyz"], properties=props)
예제 #9
0
    def test_get_shannon_radius(self):
        self.assertEqual(Specie("Li", 1).get_shannon_radius("IV"), 0.59)
        mn2 = Specie("Mn", 2)
        self.assertEqual(mn2.get_shannon_radius("IV", "High Spin"), 0.66)
        self.assertEqual(mn2.get_shannon_radius("V", "High Spin"), 0.75)

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            # Trigger a warning.
            r = mn2.get_shannon_radius("V")
            # Verify some things
            self.assertEqual(len(w), 1)
            self.assertIs(w[-1].category, UserWarning)
            self.assertEqual(r, 0.75)

        self.assertEqual(mn2.get_shannon_radius("VI", "Low Spin"), 0.67)
        self.assertEqual(mn2.get_shannon_radius("VI", "High Spin"), 0.83)
        self.assertEqual(mn2.get_shannon_radius("VII", "High Spin"), 0.9)
        self.assertEqual(mn2.get_shannon_radius("VIII"), 0.96)
예제 #10
0
def find_connected_atoms(struct, tolerance=0.45, ldict=JmolNN().el_radius):
    """
    Finds bonded atoms and returns a adjacency matrix of bonded atoms.

    Author: "Gowoon Cheon"
    Email: "*****@*****.**"

    Args:
        struct (Structure): Input structure
        tolerance: length in angstroms used in finding bonded atoms. Two atoms
            are considered bonded if (radius of atom 1) + (radius of atom 2) +
            (tolerance) < (distance between atoms 1 and 2). Default
            value = 0.45, the value used by JMol and Cheon et al.
        ldict: dictionary of bond lengths used in finding bonded atoms. Values
            from JMol are used as default

    Returns:
        (np.ndarray): A numpy array of shape (number of atoms, number of atoms);
        If any image of atom j is bonded to atom i with periodic boundary
        conditions, the matrix element [atom i, atom j] is 1.
    """
    n_atoms = len(struct.species)
    fc = np.array(struct.frac_coords)
    fc_copy = np.repeat(fc[:, :, np.newaxis], 27, axis=2)
    neighbors = np.array(list(itertools.product([0, 1, -1], [0, 1, -1], [0, 1, -1]))).T
    neighbors = np.repeat(neighbors[np.newaxis, :, :], 1, axis=0)
    fc_diff = fc_copy - neighbors
    species = list(map(str, struct.species))
    # in case of charged species
    for i, item in enumerate(species):
        if not item in ldict.keys():
            species[i] = str(Specie.from_string(item).element)
    latmat = struct.lattice.matrix
    connected_matrix = np.zeros((n_atoms,n_atoms))

    for i in range(n_atoms):
        for j in range(i + 1, n_atoms):
            max_bond_length = ldict[species[i]] + ldict[species[j]] + tolerance
            frac_diff = fc_diff[j] - fc_copy[i]
            distance_ij = np.dot(latmat.T, frac_diff)
            # print(np.linalg.norm(distance_ij,axis=0))
            if sum(np.linalg.norm(distance_ij, axis=0) < max_bond_length) > 0:
                connected_matrix[i, j] = 1
                connected_matrix[j, i] = 1
    return connected_matrix
예제 #11
0
    def from_dict(d, lattice=None):
        """
        Create PeriodicSite from dict representation.

        Args:
            d:
                dict representation of PeriodicSite
            lattice:
                Optional lattice to override lattice specified in d. Useful for
                ensuring all sites in a structure share the same lattice.
        """
        atoms_n_occu = {}
        for sp_occu in d["species"]:
            sp = Specie.from_dict(sp_occu) if "oxidation_state" in sp_occu \
                else Element(sp_occu["element"])
            atoms_n_occu[sp] = sp_occu["occu"]
        props = d.get("properties", None)
        lattice = lattice if lattice else Lattice.from_dict(d["lattice"])
        return PeriodicSite(atoms_n_occu, d["abc"], lattice, properties=props)
예제 #12
0
파일: nmr.py 프로젝트: ExpHP/pymatgen
    def coupling_constant(self, specie):
        """
        Computes the couplling constant C_q as defined in:
            Wasylishen R E, Ashbrook S E, Wimperis S. NMR of quadrupolar nuclei
            in solid materials[M]. John Wiley & Sons, 2012. (Chapter 3.2)

        C_q for a specific atom type for this electric field tensor:
                C_q=e*Q*V_zz/h
            h: planck's constant
            Q: nuclear electric quadrupole moment in mb (millibarn
            e: elementary proton charge

        Args:
            specie: flexible input to specify the species at this site.
                    Can take a isotope or element string, Specie object,
                    or Site object

        Return:

            the coupling constant as a FloatWithUnit in MHz
        """
        planks_constant=FloatWithUnit(6.62607004E-34, "m^2 kg s^-1")
        Vzz=FloatWithUnit(self.V_zz, "V ang^-2")
        e=FloatWithUnit(-1.60217662E-19, "C")

        # Convert from string to Specie object
        if isinstance(specie, str):
            # isotope was provided in string format
            if len(specie.split("-")) > 1:
                isotope=str(specie)
                specie=Specie(specie.split("-")[0])
                Q=specie.get_nmr_quadrupole_moment(isotope)
            else:
                specie=Specie(specie)
                Q=specie.get_nmr_quadrupole_moment()
        elif isinstance(specie, Site):
            specie=specie.specie
            Q=specie.get_nmr_quadrupole_moment()
        elif isinstance(specie, Specie):
            Q=specie.get_nmr_quadrupole_moment()
        else:
            raise ValueError("Invalid speciie provided for quadrupolar coupling constant calcuations")

        return (e * Q * Vzz / planks_constant).to("MHz")
예제 #13
0
    def apply_transformation(self, structure, return_ranked_list=False):
        # Make a mutable structure first
        mods = Structure.from_sites(structure)
        for sp, spin in self.mag_species_spin.items():
            sp = get_el_sp(sp)
            oxi_state = getattr(sp, "oxi_state", 0)
            if spin:
                up = Specie(sp.symbol, oxi_state, {"spin": abs(spin)})
                down = Specie(sp.symbol, oxi_state, {"spin": -abs(spin)})
                mods.replace_species({
                    sp:
                    Composition({
                        up: self.order_parameter,
                        down: 1 - self.order_parameter
                    })
                })
            else:
                mods.replace_species(
                    {sp: Specie(sp.symbol, oxi_state, {"spin": spin})})

        if mods.is_ordered:
            return [mods] if return_ranked_list > 1 else mods

        enum_args = self.kwargs

        enum_args["min_cell_size"] = max(
            int(
                MagOrderingTransformation.determine_min_cell(
                    structure, self.mag_species_spin, self.order_parameter)),
            enum_args.get("min_cell_size", 1))

        max_cell = enum_args.get('max_cell_size')
        if max_cell:
            if enum_args["min_cell_size"] > max_cell:
                raise ValueError('Specified max cell size is smaller'
                                 ' than the minimum enumerable cell size')
        else:
            enum_args["max_cell_size"] = enum_args["min_cell_size"]

        t = EnumerateStructureTransformation(**enum_args)

        alls = t.apply_transformation(mods,
                                      return_ranked_list=return_ranked_list)

        try:
            num_to_return = int(return_ranked_list)
        except ValueError:
            num_to_return = 1

        if num_to_return == 1 or not return_ranked_list:
            return alls[0]["structure"] if num_to_return else alls

        m = StructureMatcher(comparator=SpinComparator())
        key = lambda x: SpacegroupAnalyzer(x, 0.1).get_space_group_number()
        out = []
        for _, g in groupby(sorted([d["structure"] for d in alls], key=key),
                            key):
            g = list(g)
            grouped = m.group_structures(g)
            out.extend([{
                "structure": g[0],
                "energy": self.energy_model.get_energy(g[0])
            } for g in grouped])

        self._all_structures = sorted(out, key=lambda d: d["energy"])

        return self._all_structures[0:num_to_return]
예제 #14
0
    def oxi_state_guesses(self, oxi_states_override=None, target_charge=0,
                          all_oxi_states=False, max_sites=None):
        """
        Checks if the composition is charge-balanced and returns back all
        charge-balanced oxidation state combinations. Composition must have
        integer values. Note that more num_atoms in the composition gives
        more degrees of freedom. e.g., if possible oxidation states of
        element X are [2,4] and Y are [-3], then XY is not charge balanced
        but X2Y2 is. Results are returned from most to least probable based
        on ICSD statistics. Use max_sites to improve performance if needed.

        Args:
            oxi_states_override (dict): dict of str->list to override an
                element's common oxidation states, e.g. {"V": [2,3,4,5]}
            target_charge (int): the desired total charge on the structure.
                Default is 0 signifying charge balance.
            all_oxi_states (bool): if True, an element defaults to
                all oxidation states in pymatgen Element.icsd_oxidation_states.
                Otherwise, default is Element.common_oxidation_states. Note
                that the full oxidation state list is *very* inclusive and
                can produce nonsensical results.
            max_sites (int): if possible, will reduce Compositions to at most
                this many many sites to speed up oxidation state guesses. Set
                to -1 to just reduce fully.

        Returns:
            A list of dicts - each dict reports an element symbol and average
                oxidation state across all sites in that composition. If the
                composition is not charge balanced, an empty list is returned.
        """

        comp = self.copy()

        # reduce Composition if necessary
        if max_sites == -1:
            comp = self.reduced_composition

        elif max_sites and comp.num_atoms > max_sites:
            reduced_comp, reduced_factor = self.\
                get_reduced_composition_and_factor()
            if reduced_factor > 1:
                reduced_comp *= max(1, int(max_sites / reduced_comp.num_atoms))
                comp = reduced_comp  # as close to max_sites as possible
            if comp.num_atoms > max_sites:
                raise ValueError("Composition {} cannot accommodate max_sites "
                                 "setting!".format(comp))

        # Load prior probabilities of oxidation states, used to rank solutions
        if not Composition.oxi_prob:
            module_dir = os.path.join(os.path.
                                      dirname(os.path.abspath(__file__)))
            all_data = loadfn(os.path.join(module_dir, "..",
                                           "analysis", "icsd_bv.yaml"))
            Composition.oxi_prob = {Specie.from_string(sp): data
                                    for sp, data in
                                    all_data["occurrence"].items()}

        oxi_states_override = oxi_states_override or {}

        # assert: Composition only has integer amounts
        if not all(amt == int(amt) for amt in comp.values()):
            raise ValueError("Charge balance analysis requires integer "
                             "values in Composition!")

        # for each element, determine all possible sum of oxidations
        # (taking into account nsites for that particular element)
        el_amt = comp.get_el_amt_dict()
        els = el_amt.keys()
        el_sums = []  # matrix: dim1= el_idx, dim2=possible sums
        el_sum_scores = defaultdict(set)  # dict of el_idx, sum -> score
        for idx, el in enumerate(els):
            el_sum_scores[idx] = {}
            el_sums.append([])
            if oxi_states_override.get(el):
                oxids = oxi_states_override[el]
            elif all_oxi_states:
                oxids = Element(el).oxidation_states
            else:
                oxids = Element(el).icsd_oxidation_states or \
                        Element(el).oxidation_states

            # get all possible combinations of oxidation states
            # and sum each combination
            for oxid_combo in combinations_with_replacement(oxids,
                                                            int(el_amt[el])):
                if sum(oxid_combo) not in el_sums[idx]:
                    el_sums[idx].append(sum(oxid_combo))
                    score = sum([Composition.oxi_prob.get(Specie(el, o), 0) for
                                 o in oxid_combo])  # how probable is this combo?
                    el_sum_scores[idx][sum(oxid_combo)] = max(
                        el_sum_scores[idx].get(sum(oxid_combo), 0), score)


        all_sols = []  # will contain all solutions
        all_scores = []  # will contain a score for each solution
        for x in product(*el_sums):
            # each x is a trial of one possible oxidation sum for each element
            if sum(x) == target_charge:  # charge balance condition
                el_sum_sol = dict(zip(els, x))  # element->oxid_sum
                # normalize oxid_sum by amount to get avg oxid state
                sol = {el: v / el_amt[el] for el, v in el_sum_sol.items()}
                all_sols.append(sol)  # add the solution to the list of solutions

                # determine the score for this solution
                score = 0
                for idx, v in enumerate(x):
                    score += el_sum_scores[idx][v]
                all_scores.append(score)

        # sort the solutions by highest to lowest score
        all_sols = [x for (y, x) in sorted(zip(all_scores, all_sols),
                                           key=lambda pair: pair[0],
                                           reverse=True)]
        return all_sols
예제 #15
0
 def test_get_oxi_state_structure(self):
     s = Structure.from_file(os.path.join(test_dir, "LiMn2O4.json"))
     news = self.analyzer.get_oxi_state_decorated_structure(s)
     self.assertIn(Specie("Mn", 3), news.composition.elements)
     self.assertIn(Specie("Mn", 4), news.composition.elements)
예제 #16
0
        [1 - x_4f, 1 - x_4f, 0],  # O(4f)
        [0.5 - x_4f, 0.5 + x_4f, 0.5],  # O(4f)
        [0.5 + x_4f, 0.5 - x_4f, 0.5],  # O(4f)
    ])
    # fmt: on
    structure = Structure(lattice, species, frac_coords)
    return structure


if __name__ == "__main__":
    # enumerate rutile-like SnO1-x derivative structures
    rutile = get_rutile_structure()

    max_index = 3

    mapping_color_species = [DummySpecie("X"), Specie("O"), Specie("Sn")]
    num_types = len(mapping_color_species)
    composition_constraints = None
    # fmt: off
    base_site_constraints = [
        [2],  # 2a
        [2],  # 2a
        [0, 1],  # 4f
        [0, 1],  # 4f
        [0, 1],  # 4f
        [0, 1],  # 4f
    ]
    # fmt: on

    dirname = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                           "SnO1-x")
예제 #17
0
    def _get_structure(self, data, primitive, substitution_dictionary=None):
        """
        Generate structure from part of the cif.
        """
        # Symbols often representing
        #common representations for elements/water in cif files
        special_symbols = {
            "D": "D",
            "Hw": "H",
            "Ow": "O",
            "Wat": "O",
            "wat": "O"
        }
        elements = [el.symbol for el in Element]

        lattice = self.get_lattice(data)
        self.symmetry_operations = self.get_symops(data)
        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()

        def parse_symbol(sym):

            if substitution_dictionary:
                return substitution_dictionary.get(sym)
            else:
                m = re.findall(r"w?[A-Z][a-z]*", sym)
                if m and m != "?":
                    return m[0]
                return ""

        for i in range(len(data["_atom_site_label"])):
            symbol = parse_symbol(data["_atom_site_label"][i])

            if symbol:
                if symbol not in elements and symbol not in special_symbols:
                    symbol = symbol[:2]
            else:
                continue
            # make sure symbol was properly parsed from _atom_site_label
            # otherwise get it from _atom_site_type_symbol
            try:
                if symbol in special_symbols:
                    get_el_sp(special_symbols.get(symbol))
                else:
                    Element(symbol)
            except (KeyError, ValueError):
                # sometimes the site doesn't have the type_symbol.
                # we then hope the type_symbol can be parsed from the label
                if "_atom_site_type_symbol" in data.data.keys():
                    symbol = data["_atom_site_type_symbol"][i]

            if oxi_states is not None:
                if symbol in special_symbols:
                    el = get_el_sp(
                        special_symbols.get(symbol) + str(oxi_states[symbol]))
                else:
                    el = Specie(symbol, oxi_states.get(symbol, 0))
            else:

                el = get_el_sp(special_symbols.get(symbol) if \
                            symbol in special_symbols else symbol)

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1
            if occu > 0:
                coord = (x, y, z)
                if coord not in coord_to_species:
                    coord_to_species[coord] = {el: occu}
                else:
                    coord_to_species[coord][el] = occu

        coord_to_species = {
            k: Composition(v)
            for k, v in coord_to_species.items()
        }
        allspecies = []
        allcoords = []

        if coord_to_species.items():
            for species, group in groupby(sorted(list(
                    coord_to_species.items()),
                                                 key=lambda x: x[1]),
                                          key=lambda x: x[1]):

                tmp_coords = [site[0] for site in group]

                coords = self._unique_coords(tmp_coords)

                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])

            #rescale occupancies if necessary
            for species in allspecies:
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    for key, value in six.iteritems(species):
                        species[key] = value / totaloccu

        if allspecies and len(allspecies) == len(allcoords):

            struct = Structure(lattice, allspecies, allcoords)
            struct = struct.get_sorted_structure()

            if primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            return struct
예제 #18
0
    def _get_structure(self, data, primitive):
        """
        Generate structure from part of the cif.
        """
        def get_num_implicit_hydrogens(sym):
            num_h = {"Wat": 2, "wat": 2, "O-H": 1}
            return num_h.get(sym[:3], 0)

        lattice = self.get_lattice(data)

        # if magCIF, get magnetic symmetry moments and magmoms
        # else standard CIF, and use empty magmom dict
        if self.feature_flags["magcif_incommensurate"]:
            raise NotImplementedError(
                "Incommensurate structures not currently supported.")
        elif self.feature_flags["magcif"]:
            self.symmetry_operations = self.get_magsymops(data)
            magmoms = self.parse_magmoms(data, lattice=lattice)
        else:
            self.symmetry_operations = self.get_symops(data)
            magmoms = {}

        oxi_states = self.parse_oxi_states(data)
        coord_to_species = OrderedDict()
        coord_to_magmoms = OrderedDict()

        def get_matching_coord(coord):
            keys = list(coord_to_species.keys())
            coords = np.array(keys)
            for op in self.symmetry_operations:
                c = op.operate(coord)
                inds = find_in_coord_list_pbc(coords,
                                              c,
                                              atol=self._site_tolerance)
                # cant use if inds, because python is dumb and np.array([0]) evaluates
                # to False
                if len(inds):
                    return keys[inds[0]]
            return False

        label_el_dict = {}

        for i in range(len(data["_atom_site_label"])):
            try:
                # If site type symbol exists, use it. Otherwise, we use the
                # label.
                symbol = self._parse_symbol(data["_atom_site_type_symbol"][i])

                label = data["_atom_site_label"][i]

                num_h = get_num_implicit_hydrogens(
                    data["_atom_site_type_symbol"][i])

            except KeyError:
                symbol = self._parse_symbol(data["_atom_site_label"][i])

                label = data["_atom_site_label"][i]

                num_h = get_num_implicit_hydrogens(data["_atom_site_label"][i])
            if not symbol:
                continue

            if oxi_states is not None:
                o_s = oxi_states.get(symbol, 0)
                # use _atom_site_type_symbol if possible for oxidation state
                if "_atom_site_type_symbol" in data.data.keys():
                    oxi_symbol = data["_atom_site_type_symbol"][i]
                    o_s = oxi_states.get(oxi_symbol, o_s)
                try:
                    el = Specie(symbol, o_s)
                except:
                    el = DummySpecie(symbol, o_s)
            else:
                el = get_el_sp(symbol)

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            magmom = magmoms.get(data["_atom_site_label"][i],
                                 np.array([0, 0, 0]))

            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                comp_d = {el: occu}

                if num_h > 0:
                    comp_d["H"] = num_h
                comp = Composition(comp_d)
                if not match:
                    coord_to_species[coord] = comp
                    coord_to_magmoms[coord] = magmom
                else:
                    coord_to_species[match] += comp
                    # disordered magnetic not currently supported
                    coord_to_magmoms[match] = None

            label_el_dict[coord] = label

        sum_occu = [
            sum(c.values()) for c in coord_to_species.values() if
            not set(c.elements) == {Element("O"), Element("H")}
        ]
        if any([o > 1 for o in sum_occu]):
            msg = "Some occupancies (%s) sum to > 1! If they are within " \
                    "the tolerance, they will be rescaled." % str(sum_occu)
            warnings.warn(msg)
            self.errors.append(msg)

        allspecies = []
        allcoords = []
        allmagmoms = []
        allhydrogens = []
        alllabels = []

        # check to see if magCIF file is disordered
        if self.feature_flags["magcif"]:
            for k, v in coord_to_magmoms.items():
                if v is None:
                    # Proposed solution to this is to instead store magnetic
                    # moments as Specie 'spin' property, instead of site
                    # property, but this introduces ambiguities for end user
                    # (such as unintended use of `spin` and Specie will have
                    # fictious oxidation state).
                    raise NotImplementedError(
                        'Disordered magnetic structures not currently supported.'
                    )
        if coord_to_species.items():
            for comp, group in groupby(sorted(list(coord_to_species.items()),
                                              key=lambda x: x[1]),
                                       key=lambda x: x[1]):
                tmp_coords = [site[0] for site in group]

                #print(tmp_coords)
                labels = []
                for i in tmp_coords:
                    labels.append(label_el_dict[i])

                #print(labels)
                tmp_magmom = [
                    coord_to_magmoms[tmp_coord] for tmp_coord in tmp_coords
                ]
                if self.feature_flags["magcif"]:
                    coords, magmoms, coords_num = self._unique_coords(
                        tmp_coords, magmoms_in=tmp_magmom, lattice=lattice)
                else:
                    coords, magmoms, coords_num = self._unique_coords(
                        tmp_coords)

                if set(comp.elements) == {Element("O"), Element("H")}:
                    # O with implicit hydrogens
                    im_h = comp["H"]
                    species = Composition({"O": comp["O"]})
                else:
                    im_h = 0
                    species = comp

                allhydrogens.extend(len(coords) * [im_h])
                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])
                allmagmoms.extend(magmoms)

                for i in range(len(coords_num)):

                    alllabels.extend(coords_num[i] * [labels[i]])

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords) \
                and len(allspecies) == len(allmagmoms):
            site_properties = dict()
            if any(allhydrogens):
                assert len(allhydrogens) == len(allcoords)
                site_properties["implicit_hydrogens"] = allhydrogens

            if self.feature_flags["magcif"]:
                site_properties["magmom"] = allmagmoms

            if len(site_properties) == 0:
                site_properties = None

            struct = Structure(lattice,
                               allspecies,
                               allcoords,
                               site_properties=site_properties)
            #struct = struct.get_sorted_structure()

            if primitive and self.feature_flags['magcif']:
                struct = struct.get_primitive_structure(use_site_props=True)
            elif primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            struct.add_site_property("_atom_site_label", alllabels)
            return struct
예제 #19
0
    def _get_oxid_state_guesses(self, all_oxi_states, max_sites, oxi_states_override, target_charge):
        """
        Utility operation for guessing oxidation states.

        See `oxi_state_guesses` for full details. This operation does the calculation
        of the most likely oxidation states

        Args:
            oxi_states_override (dict): dict of str->list to override an
                element's common oxidation states, e.g. {"V": [2,3,4,5]}
            target_charge (int): the desired total charge on the structure.
                Default is 0 signifying charge balance.
            all_oxi_states (bool): if True, an element defaults to
                all oxidation states in pymatgen Element.icsd_oxidation_states.
                Otherwise, default is Element.common_oxidation_states. Note
                that the full oxidation state list is *very* inclusive and
                can produce nonsensical results.
            max_sites (int): if possible, will reduce Compositions to at most
                this many many sites to speed up oxidation state guesses. Set
                to -1 to just reduce fully.
        Returns:
            A list of dicts - each dict reports an element symbol and average
                oxidation state across all sites in that composition. If the
                composition is not charge balanced, an empty list is returned.
            A list of dicts - each dict maps the element symbol to a list of
                oxidation states for each site of that element. For example, Fe3O4 could
                return a list of [2,2,2,3,3,3] for the oxidation states of If the composition
                is

            """
        comp = self.copy()
        # reduce Composition if necessary
        if max_sites == -1:
            comp = self.reduced_composition

        elif max_sites and comp.num_atoms > max_sites:
            reduced_comp, reduced_factor = self. \
                get_reduced_composition_and_factor()
            if reduced_factor > 1:
                reduced_comp *= max(1, int(max_sites / reduced_comp.num_atoms))
                comp = reduced_comp  # as close to max_sites as possible
            if comp.num_atoms > max_sites:
                raise ValueError("Composition {} cannot accommodate max_sites "
                                 "setting!".format(comp))

        # Load prior probabilities of oxidation states, used to rank solutions
        if not Composition.oxi_prob:
            module_dir = os.path.join(os.path.
                                      dirname(os.path.abspath(__file__)))
            all_data = loadfn(os.path.join(module_dir, "..",
                                           "analysis", "icsd_bv.yaml"))
            Composition.oxi_prob = {Specie.from_string(sp): data
                                    for sp, data in
                                    all_data["occurrence"].items()}
        oxi_states_override = oxi_states_override or {}
        # assert: Composition only has integer amounts
        if not all(amt == int(amt) for amt in comp.values()):
            raise ValueError("Charge balance analysis requires integer "
                             "values in Composition!")

        # for each element, determine all possible sum of oxidations
        # (taking into account nsites for that particular element)
        el_amt = comp.get_el_amt_dict()
        els = el_amt.keys()
        el_sums = []  # matrix: dim1= el_idx, dim2=possible sums
        el_sum_scores = defaultdict(set)  # dict of el_idx, sum -> score
        el_best_oxid_combo = {}  # dict of el_idx, sum -> oxid combo with best score
        for idx, el in enumerate(els):
            el_sum_scores[idx] = {}
            el_best_oxid_combo[idx] = {}
            el_sums.append([])
            if oxi_states_override.get(el):
                oxids = oxi_states_override[el]
            elif all_oxi_states:
                oxids = Element(el).oxidation_states
            else:
                oxids = Element(el).icsd_oxidation_states or \
                        Element(el).oxidation_states

            # get all possible combinations of oxidation states
            # and sum each combination
            for oxid_combo in combinations_with_replacement(oxids,
                                                            int(el_amt[el])):

                # List this sum as a possible option
                oxid_sum = sum(oxid_combo)
                if oxid_sum not in el_sums[idx]:
                    el_sums[idx].append(oxid_sum)

                # Determine how probable is this combo?
                score = sum([Composition.oxi_prob.get(Specie(el, o), 0) for
                             o in oxid_combo])

                # If it is the most probable combo for a certain sum,
                #   store the combination
                if oxid_sum not in el_sum_scores[idx] or score > el_sum_scores[idx].get(oxid_sum, 0):
                    el_sum_scores[idx][oxid_sum] = score
                    el_best_oxid_combo[idx][oxid_sum] = oxid_combo

        # Determine which combination of oxidation states for each element
        #    is the most probable
        all_sols = []  # will contain all solutions
        all_oxid_combo = []  # will contain the best combination of oxidation states for each site
        all_scores = []  # will contain a score for each solution
        for x in product(*el_sums):
            # each x is a trial of one possible oxidation sum for each element
            if sum(x) == target_charge:  # charge balance condition
                el_sum_sol = dict(zip(els, x))  # element->oxid_sum
                # normalize oxid_sum by amount to get avg oxid state
                sol = {el: v / el_amt[el] for el, v in el_sum_sol.items()}
                all_sols.append(sol)  # add the solution to the list of solutions

                # determine the score for this solution
                score = 0
                for idx, v in enumerate(x):
                    score += el_sum_scores[idx][v]
                all_scores.append(score)

                # collect the combination of oxidation states for each site
                all_oxid_combo.append(dict((e,el_best_oxid_combo[idx][v]) for idx, (e,v) in enumerate(zip(els,x))))

        # sort the solutions by highest to lowest score
        if len(all_scores) > 0:
            all_sols, all_oxid_combo = zip(*[(y, x) for (z, y, x) in sorted(zip(all_scores, all_sols, all_oxid_combo),
                                                                            key=lambda pair: pair[0],
                                                                            reverse=True)])
        return all_sols, all_oxid_combo
 def test_no_oxidation_state(self):
     mo0 = Specie("Mo", None, {"spin": 5})
     self.assertEqual(str(mo0), "Mo,spin=5")
예제 #21
0
 def test_sort(self):
     els = map(get_el_sp, ["N3-", "Si4+", "Si3+"])
     self.assertEqual(sorted(els), [Specie("Si", 3), Specie("Si", 4),
                                    Specie("N", -3)])
예제 #22
0
    def _get_structure(self, data, primitive):
        """
        Generate structure from part of the cif.
        """
        lengths = [
            str2float(data["_cell_length_" + i]) for i in ["a", "b", "c"]
        ]
        angles = [
            str2float(data["_cell_angle_" + i])
            for i in ["alpha", "beta", "gamma"]
        ]
        lattice = Lattice.from_lengths_and_angles(lengths, angles)
        try:
            sympos = data["_symmetry_equiv_pos_as_xyz"]
        except KeyError:
            try:
                sympos = data["_symmetry_equiv_pos_as_xyz_"]
            except KeyError:
                warnings.warn("No _symmetry_equiv_pos_as_xyz type key found. "
                              "Defaulting to P1.")
                sympos = ['x, y, z']
        self.symmetry_operations = parse_symmetry_operations(sympos)

        def parse_symbol(sym):
            m = re.search("([A-Z][a-z]*)", sym)
            if m:
                return m.group(1)
            return ""

        try:
            oxi_states = {
                data["_atom_type_symbol"][i]:
                str2float(data["_atom_type_oxidation_number"][i])
                for i in xrange(len(data["_atom_type_symbol"]))
            }
        except (ValueError, KeyError):
            oxi_states = None

        coord_to_species = OrderedDict()

        for i in xrange(len(data["_atom_site_type_symbol"])):
            symbol = parse_symbol(data["_atom_site_type_symbol"][i])
            if oxi_states is not None:
                el = Specie(symbol,
                            oxi_states[data["_atom_site_type_symbol"][i]])
            else:
                el = Element(symbol)
            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1
            if occu > 0:
                coord = (x, y, z)
                if coord not in coord_to_species:
                    coord_to_species[coord] = {el: occu}
                else:
                    coord_to_species[coord][el] = occu

        allspecies = []
        allcoords = []

        for coord, species in coord_to_species.items():
            coords = self._unique_coords(coord)
            allcoords.extend(coords)
            allspecies.extend(len(coords) * [species])

        #rescale occupancies if necessary
        for species in allspecies:
            totaloccu = sum(species.values())
            if 1 < totaloccu <= self._occupancy_tolerance:
                for key, value in species.iteritems():
                    species[key] = value / totaloccu

        struct = Structure(lattice, allspecies, allcoords)
        if primitive:
            struct = struct.get_primitive_structure().get_reduced_structure()
        return struct.get_sorted_structure()
 def test_cached(self):
     specie5 = Specie("Fe", 2)
예제 #24
0
파일: cif.py 프로젝트: yanguang21/pymatgen
    def _get_structure(self, data, primitive, substitution_dictionary=None):
        """
        Generate structure from part of the cif.
        """
        # Symbols often representing
        # common representations for elements/water in cif files
        special_symbols = {
            "D": "D",
            "Hw": "H",
            "Ow": "O",
            "Wat": "O",
            "wat": "O"
        }
        elements = [el.symbol for el in Element]

        lattice = self.get_lattice(data)
        self.symmetry_operations = self.get_symops(data)
        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()

        def parse_symbol(sym):

            if substitution_dictionary:
                return substitution_dictionary.get(sym)
            else:
                m = re.findall(r"w?[A-Z][a-z]*", sym)
                if m and m != "?":
                    return m[0]
                return ""

        def get_matching_coord(coord):
            for op in self.symmetry_operations:
                c = op.operate(coord)
                for k in coord_to_species.keys():
                    if np.allclose(pbc_diff(c, k), (0, 0, 0),
                                   atol=self._site_tolerance):
                        return tuple(k)
            return False

        for i in range(len(data["_atom_site_label"])):
            symbol = parse_symbol(data["_atom_site_label"][i])

            if symbol:
                if symbol not in elements and symbol not in special_symbols:
                    symbol = symbol[:2]
            else:
                continue
            # make sure symbol was properly parsed from _atom_site_label
            # otherwise get it from _atom_site_type_symbol
            try:
                if symbol in special_symbols:
                    get_el_sp(special_symbols.get(symbol))
                else:
                    Element(symbol)
            except (KeyError, ValueError):
                # sometimes the site doesn't have the type_symbol.
                # we then hope the type_symbol can be parsed from the label
                if "_atom_site_type_symbol" in data.data.keys():
                    symbol = data["_atom_site_type_symbol"][i]

            if oxi_states is not None:
                if symbol in special_symbols:
                    el = get_el_sp(
                        special_symbols.get(symbol) + str(oxi_states[symbol]))
                else:
                    el = Specie(symbol, oxi_states.get(symbol, 0))
            else:

                el = get_el_sp(special_symbols.get(symbol, symbol))

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                if not match:
                    coord_to_species[coord] = Composition({el: occu})
                else:
                    coord_to_species[match] += {el: occu}

        if any([sum(c.values()) > 1 for c in coord_to_species.values()]):
            warnings.warn("Some occupancies sum to > 1! If they are within "
                          "the tolerance, they will be rescaled.")

        allspecies = []
        allcoords = []

        if coord_to_species.items():
            for species, group in groupby(sorted(list(
                    coord_to_species.items()),
                                                 key=lambda x: x[1]),
                                          key=lambda x: x[1]):

                tmp_coords = [site[0] for site in group]

                coords = self._unique_coords(tmp_coords)

                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords):
            struct = Structure(lattice, allspecies, allcoords)
            struct = struct.get_sorted_structure()

            if primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            return struct
    def test_light_structure_environments(self):
        with ScratchDir("."):
            f = open("{}/{}".format(se_files_dir, 'se_mp-7000.json'), 'r')
            dd = json.load(f)
            f.close()

            se = StructureEnvironments.from_dict(dd)

            strategy = SimplestChemenvStrategy()
            lse = LightStructureEnvironments.from_structure_environments(
                structure_environments=se,
                strategy=strategy,
                valences='undefined')
            isite = 6
            nb_set = lse.neighbors_sets[isite][0]
            neighb_coords = [
                np.array([0.2443798, 1.80409653, -1.13218359]),
                np.array([1.44020353, 1.11368738, 1.13218359]),
                np.array([2.75513098, 2.54465207, -0.70467298]),
                np.array([0.82616785, 3.65833945, 0.70467298])
            ]
            neighb_indices = [0, 3, 5, 1]
            neighb_images = [[0, 0, -1], [0, 0, 0], [0, 0, -1], [0, 0, 0]]

            np.testing.assert_array_almost_equal(neighb_coords,
                                                 nb_set.neighb_coords)
            np.testing.assert_array_almost_equal(
                neighb_coords, [s.coords for s in nb_set.neighb_sites])
            nb_sai = nb_set.neighb_sites_and_indices
            np.testing.assert_array_almost_equal(
                neighb_coords, [sai['site'].coords for sai in nb_sai])
            np.testing.assert_array_almost_equal(
                neighb_indices, [sai['index'] for sai in nb_sai])
            nb_iai = nb_set.neighb_indices_and_images
            np.testing.assert_array_almost_equal(
                neighb_indices, [iai['index'] for iai in nb_iai])
            np.testing.assert_array_equal(
                neighb_images, [iai['image_cell'] for iai in nb_iai])

            self.assertEqual(nb_set.__len__(), 4)
            self.assertEqual(nb_set.__hash__(), 4)

            self.assertFalse(nb_set.__ne__(nb_set))

            self.assertEqual(
                nb_set.__str__(), 'Neighbors Set for site #6 :\n'
                ' - Coordination number : 4\n'
                ' - Neighbors sites indices : 0, 1, 2, 3\n')

            stats = lse.get_statistics()

            neighbors = lse.strategy.get_site_neighbors(
                site=lse.structure[isite])
            self.assertArrayAlmostEqual(
                neighbors[0].coords,
                np.array([0.2443798, 1.80409653, -1.13218359]))
            self.assertArrayAlmostEqual(
                neighbors[1].coords,
                np.array([1.44020353, 1.11368738, 1.13218359]))
            self.assertArrayAlmostEqual(
                neighbors[2].coords,
                np.array([2.75513098, 2.54465207, -0.70467298]))
            self.assertArrayAlmostEqual(
                neighbors[3].coords,
                np.array([0.82616785, 3.65833945, 0.70467298]))

            equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform(
                neighbors[0])
            self.assertEqual(equiv_site_index_and_transform[0], 0)
            self.assertArrayAlmostEqual(equiv_site_index_and_transform[1],
                                        [0.0, 0.0, 0.0])
            self.assertArrayAlmostEqual(equiv_site_index_and_transform[2],
                                        [0.0, 0.0, -1.0])

            equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform(
                neighbors[1])
            self.assertEqual(equiv_site_index_and_transform[0], 3)
            self.assertArrayAlmostEqual(equiv_site_index_and_transform[1],
                                        [0.0, 0.0, 0.0])
            self.assertArrayAlmostEqual(equiv_site_index_and_transform[2],
                                        [0.0, 0.0, 0.0])

            self.assertEqual(stats['atom_coordination_environments_present'],
                             {'Si': {
                                 'T:4': 3.0
                             }})
            self.assertEqual(stats['coordination_environments_atom_present'],
                             {'T:4': {
                                 'Si': 3.0
                             }})
            self.assertEqual(
                stats['fraction_atom_coordination_environments_present'],
                {'Si': {
                    'T:4': 1.0
                }})

            site_info_ce = lse.get_site_info_for_specie_ce(specie=Specie(
                'Si', 4),
                                                           ce_symbol='T:4')
            np.testing.assert_array_almost_equal(site_info_ce['fractions'],
                                                 [1.0, 1.0, 1.0])
            np.testing.assert_array_almost_equal(site_info_ce['csms'], [
                0.009887784240541068, 0.009887786546730826,
                0.009887787384385317
            ])
            self.assertEqual(site_info_ce['isites'], [6, 7, 8])

            site_info_allces = lse.get_site_info_for_specie_allces(
                specie=Specie('Si', 4))

            self.assertEqual(site_info_allces['T:4'], site_info_ce)

            self.assertFalse(lse.contains_only_one_anion('I-'))
            self.assertFalse(lse.contains_only_one_anion_atom('I'))
            self.assertTrue(
                lse.site_contains_environment(isite=isite, ce_symbol='T:4'))
            self.assertFalse(
                lse.site_contains_environment(isite=isite, ce_symbol='S:4'))
            self.assertFalse(
                lse.structure_contains_atom_environment(atom_symbol='Si',
                                                        ce_symbol='S:4'))
            self.assertTrue(
                lse.structure_contains_atom_environment(atom_symbol='Si',
                                                        ce_symbol='T:4'))
            self.assertFalse(
                lse.structure_contains_atom_environment(atom_symbol='O',
                                                        ce_symbol='T:4'))
            self.assertTrue(lse.uniquely_determines_coordination_environments)
            self.assertFalse(lse.__ne__(lse))

            envs = lse.strategy.get_site_coordination_environments(
                lse.structure[6])
            self.assertEqual(len(envs), 1)
            self.assertEqual(envs[0][0], 'T:4')

            multi_strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters(
            )

            lse_multi = LightStructureEnvironments.from_structure_environments(
                strategy=multi_strategy,
                structure_environments=se,
                valences='undefined')
            self.assertAlmostEqual(
                lse_multi.coordination_environments[isite][0]['csm'],
                0.009887784240541068)
            self.assertAlmostEqual(
                lse_multi.coordination_environments[isite][0]['ce_fraction'],
                1.0)
            self.assertEqual(
                lse_multi.coordination_environments[isite][0]['ce_symbol'],
                'T:4')
예제 #26
0
 def test_get_el_sp(self):
     self.assertEqual(get_el_sp("Fe2+"), Specie("Fe", 2))
     self.assertEqual(get_el_sp("3"), Element("Li"))
     self.assertEqual(get_el_sp("U"), Element("U"))
     self.assertEqual(get_el_sp("X2+"), DummySpecie("X", 2))
     self.assertEqual(get_el_sp("Mn3+"), Specie("Mn", 3))
예제 #27
0
 def test_pickle(self):
     el1 = Specie("Fe", 3)
     o = pickle.dumps(el1)
     self.assertEqual(el1, pickle.loads(o))
예제 #28
0
파일: cif.py 프로젝트: cespejo79/pymatgen
    def _get_structure(self, data, primitive, substitution_dictionary=None):
        """
        Generate structure from part of the cif.
        """
        # Symbols often representing
        # common representations for elements/water in cif files
        special_symbols = {
            "D": "D",
            "Hw": "H",
            "Ow": "O",
            "Wat": "O",
            "wat": "O"
        }
        elements = [el.symbol for el in Element]

        lattice = self.get_lattice(data)
        self.symmetry_operations = self.get_symops(data)
        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()

        def parse_symbol(sym):

            if substitution_dictionary:
                return substitution_dictionary.get(sym)
            elif sym in ['OH', 'OH2']:
                warnings.warn("Symbol '{}' not recognized".format(sym))
                return ""
            else:
                m = re.findall(r"w?[A-Z][a-z]*", sym)
                if m and m != "?":
                    return m[0]
                return ""

        def get_matching_coord(coord):
            for op in self.symmetry_operations:
                c = op.operate(coord)
                for k in coord_to_species.keys():
                    if np.allclose(pbc_diff(c, k), (0, 0, 0),
                                   atol=self._site_tolerance):
                        return tuple(k)
            return False

        ############################################################
        """
        This part of the code deals with handling formats of data as found in CIF files extracted from the
        Springer Materials/Pauling File databases, and that are different from standard ICSD formats.
        """

        # Check to see if "_atom_site_type_symbol" exists, as some test CIFs do not contain this key.
        if "_atom_site_type_symbol" in data.data.keys():

            # Keep a track of which data row needs to be removed.
            # Example of a row: Nb,Zr '0.8Nb + 0.2Zr' .2a .m-3m 0 0 0 1 14 'rhombic dodecahedron, Nb<sub>14</sub>'
            # Without this code, the above row in a structure would be parsed as an ordered site with only Nb (since
            # CifParser would try to parse the first two characters of the label "Nb,Zr") and occupancy=1.
            # However, this site is meant to be a disordered site with 0.8 of Nb and 0.2 of Zr.
            idxs_to_remove = []

            for idx, el_row in enumerate(data["_atom_site_label"]):

                # CIF files from the Springer Materials/Pauling File have switched the label and symbol. Thus, in the
                # above shown example row, '0.8Nb + 0.2Zr' is the symbol. Below, we split the strings on ' + ' to
                # check if the length (or number of elements) in the label and symbol are equal.
                if len(data["_atom_site_type_symbol"][idx].split(' + ')) > \
                        len(data["_atom_site_label"][idx].split(' + ')):

                    # Dictionary to hold extracted elements and occupancies
                    els_occu = {}

                    # parse symbol to get element names and occupancy and store in "els_occu"
                    symbol_str = data["_atom_site_type_symbol"][idx]
                    symbol_str_lst = symbol_str.split(' + ')
                    for elocc_idx in range(len(symbol_str_lst)):
                        # Remove any bracketed items in the string
                        symbol_str_lst[elocc_idx] = re.sub(
                            '\([0-9]*\)', '',
                            symbol_str_lst[elocc_idx].strip())

                        # Extract element name and its occupancy from the string, and store it as a
                        # key-value pair in "els_occ".
                        els_occu[str(re.findall('\D+', symbol_str_lst[elocc_idx].strip())[1]).replace('<sup>', '')] = \
                            float('0' + re.findall('\.?\d+', symbol_str_lst[elocc_idx].strip())[1])

                    x = str2float(data["_atom_site_fract_x"][idx])
                    y = str2float(data["_atom_site_fract_y"][idx])
                    z = str2float(data["_atom_site_fract_z"][idx])

                    coord = (x, y, z)
                    # Add each partially occupied element on the site coordinate
                    for et in els_occu:
                        match = get_matching_coord(coord)
                        if not match:
                            coord_to_species[coord] = Composition(
                                {parse_symbol(et): els_occu[parse_symbol(et)]})
                        else:
                            coord_to_species[match] += {
                                parse_symbol(et): els_occu[parse_symbol(et)]
                            }
                    idxs_to_remove.append(idx)

            # Remove the original row by iterating over all keys in the CIF data looking for lists, which indicates
            # multiple data items, one for each row, and remove items from the list that corresponds to the removed row,
            # so that it's not processed by the rest of this function (which would result in an error).
            for cif_key in data.data:
                if type(data.data[cif_key]) == list:
                    for id in sorted(idxs_to_remove, reverse=True):
                        del data.data[cif_key][id]

        ############################################################

        for i in range(len(data["_atom_site_label"])):
            symbol = parse_symbol(data["_atom_site_label"][i])

            if symbol:
                if symbol not in elements and symbol not in special_symbols:
                    symbol = symbol[:2]
            else:
                continue
            # make sure symbol was properly parsed from _atom_site_label
            # otherwise get it from _atom_site_type_symbol
            try:
                if symbol in special_symbols:
                    get_el_sp(special_symbols.get(symbol))
                else:
                    Element(symbol)
            except (KeyError, ValueError):
                # sometimes the site doesn't have the type_symbol.
                # we then hope the type_symbol can be parsed from the label
                if "_atom_site_type_symbol" in data.data.keys():
                    symbol = data["_atom_site_type_symbol"][i]

            if oxi_states is not None:
                if symbol in special_symbols:
                    el = get_el_sp(
                        special_symbols.get(symbol) + str(oxi_states[symbol]))
                else:
                    el = Specie(symbol, oxi_states.get(symbol, 0))
            else:

                el = get_el_sp(special_symbols.get(symbol, symbol))

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                if not match:
                    coord_to_species[coord] = Composition({el: occu})
                else:
                    coord_to_species[match] += {el: occu}

        if any([sum(c.values()) > 1 for c in coord_to_species.values()]):
            warnings.warn("Some occupancies sum to > 1! If they are within "
                          "the tolerance, they will be rescaled.")

        allspecies = []
        allcoords = []

        if coord_to_species.items():
            for species, group in groupby(sorted(list(
                    coord_to_species.items()),
                                                 key=lambda x: x[1]),
                                          key=lambda x: x[1]):

                tmp_coords = [site[0] for site in group]

                coords = self._unique_coords(tmp_coords)

                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords):
            struct = Structure(lattice, allspecies, allcoords)
            struct = struct.get_sorted_structure()

            if primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            return struct
 def test_ionic_radius(self):
     self.assertEqual(self.specie2.ionic_radius, 78.5 / 100)
     self.assertEqual(self.specie3.ionic_radius, 92 / 100)
     self.assertAlmostEqual(Specie("Mn", 4).ionic_radius, 0.67)
예제 #30
0
 def setUp(self):
     self.df = pd.DataFrame({"composition": [Composition("Fe2O3"),
                                             Composition({Specie("Fe", 2): 1, Specie("O", -2): 1})]})
예제 #31
0
    def test_get_crystal_field_spin(self):
        self.assertEqual(Specie("Fe", 2).get_crystal_field_spin(), 4)
        self.assertEqual(Specie("Fe", 3).get_crystal_field_spin(), 5)
        self.assertEqual(Specie("Fe", 4).get_crystal_field_spin(), 4)
        self.assertEqual(Specie("Co", 3).get_crystal_field_spin(
            spin_config="low"), 0)
        self.assertEqual(Specie("Co", 4).get_crystal_field_spin(
            spin_config="low"), 1)
        self.assertEqual(Specie("Ni", 3).get_crystal_field_spin(
            spin_config="low"), 1)
        self.assertEqual(Specie("Ni", 4).get_crystal_field_spin(
            spin_config="low"), 0)

        self.assertRaises(AttributeError,
                          Specie("Li", 1).get_crystal_field_spin)
        self.assertRaises(AttributeError,
                          Specie("Ge", 4).get_crystal_field_spin)
        self.assertRaises(AttributeError,
                          Specie("H", 1).get_crystal_field_spin)
        self.assertRaises(AttributeError,
                          Specie("Fe", 10).get_crystal_field_spin)
        self.assertRaises(ValueError, Specie("Fe", 2).get_crystal_field_spin,
                          "hex")
예제 #32
0
    def oxi_state_guesses(self,
                          oxi_states_override=None,
                          target_charge=0,
                          all_oxi_states=False,
                          max_sites=None):
        """
        Checks if the composition is charge-balanced and returns back all
        charge-balanced oxidation state combinations. Composition must have
        integer values. Note that more num_atoms in the composition gives
        more degrees of freedom. e.g., if possible oxidation states of
        element X are [2,4] and Y are [-3], then XY is not charge balanced
        but X2Y2 is. Results are returned from most to least probable based
        on ICSD statistics. Use max_sites to improve performance if needed.

        Args:
            oxi_states_override (dict): dict of str->list to override an
                element's common oxidation states, e.g. {"V": [2,3,4,5]}
            target_charge (int): the desired total charge on the structure.
                Default is 0 signifying charge balance.
            all_oxi_states (bool): if True, an element defaults to
                all oxidation states in pymatgen Element.icsd_oxidation_states.
                Otherwise, default is Element.common_oxidation_states. Note
                that the full oxidation state list is *very* inclusive and
                can produce nonsensical results.
            max_sites (int): if possible, will reduce Compositions to at most
                this many many sites to speed up oxidation state guesses. Set
                to -1 to just reduce fully.

        Returns:
            A list of dicts - each dict reports an element symbol and average
                oxidation state across all sites in that composition. If the
                composition is not charge balanced, an empty list is returned.
        """

        comp = self.copy()

        # reduce Composition if necessary
        if max_sites == -1:
            comp = self.reduced_composition

        elif max_sites and comp.num_atoms > max_sites:
            reduced_comp, reduced_factor = self.\
                get_reduced_composition_and_factor()
            if reduced_factor > 1:
                reduced_comp *= max(1, int(max_sites / reduced_comp.num_atoms))
                comp = reduced_comp  # as close to max_sites as possible
            if comp.num_atoms > max_sites:
                raise ValueError("Composition {} cannot accommodate max_sites "
                                 "setting!".format(comp))

        # Load prior probabilities of oxidation states, used to rank solutions
        if not Composition.oxi_prob:
            module_dir = os.path.join(
                os.path.dirname(os.path.abspath(__file__)))
            all_data = loadfn(
                os.path.join(module_dir, "..", "analysis", "icsd_bv.yaml"))
            Composition.oxi_prob = {
                Specie.from_string(sp): data
                for sp, data in all_data["occurrence"].items()
            }

        oxi_states_override = oxi_states_override or {}

        # assert: Composition only has integer amounts
        if not all(amt == int(amt) for amt in comp.values()):
            raise ValueError("Charge balance analysis requires integer "
                             "values in Composition!")

        # for each element, determine all possible sum of oxidations
        # (taking into account nsites for that particular element)
        el_amt = comp.get_el_amt_dict()
        els = el_amt.keys()
        el_sums = []  # matrix: dim1= el_idx, dim2=possible sums
        el_sum_scores = defaultdict(set)  # dict of el_idx, sum -> score
        for idx, el in enumerate(els):
            el_sum_scores[idx] = {}
            el_sums.append([])
            if oxi_states_override.get(el):
                oxids = oxi_states_override[el]
            elif all_oxi_states:
                oxids = Element(el).oxidation_states
            else:
                oxids = Element(el).icsd_oxidation_states or \
                        Element(el).oxidation_states

            # get all possible combinations of oxidation states
            # and sum each combination
            for oxid_combo in combinations_with_replacement(
                    oxids, int(el_amt[el])):
                if sum(oxid_combo) not in el_sums[idx]:
                    el_sums[idx].append(sum(oxid_combo))
                    score = sum([
                        Composition.oxi_prob.get(Specie(el, o), 0)
                        for o in oxid_combo
                    ])  # how probable is this combo?
                    el_sum_scores[idx][sum(oxid_combo)] = max(
                        el_sum_scores[idx].get(sum(oxid_combo), 0), score)

        all_sols = []  # will contain all solutions
        all_scores = []  # will contain a score for each solution
        for x in product(*el_sums):
            # each x is a trial of one possible oxidation sum for each element
            if sum(x) == target_charge:  # charge balance condition
                el_sum_sol = dict(zip(els, x))  # element->oxid_sum
                # normalize oxid_sum by amount to get avg oxid state
                sol = {el: v / el_amt[el] for el, v in el_sum_sol.items()}
                all_sols.append(
                    sol)  # add the solution to the list of solutions

                # determine the score for this solution
                score = 0
                for idx, v in enumerate(x):
                    score += el_sum_scores[idx][v]
                all_scores.append(score)

        # sort the solutions by highest to lowest score
        all_sols = [
            x for (y, x) in sorted(zip(all_scores, all_sols),
                                   key=lambda pair: pair[0],
                                   reverse=True)
        ]
        return all_sols
예제 #33
0
        "H", "B", "C", "Si", "N", "P", "As", "Sb", "O", "S", "Se", "Te", "F",
        "Cl", "Br", "I"
    ]
]

module_dir = os.path.dirname(os.path.abspath(__file__))

# Read in BV parameters.
BV_PARAMS = {}
for k, v in loadfn(os.path.join(module_dir, "bvparam_1991.yaml")).items():
    BV_PARAMS[Element(k)] = v

# Read in yaml containing data-mined ICSD BV data.
all_data = loadfn(os.path.join(module_dir, "icsd_bv.yaml"))
ICSD_BV_DATA = {
    Specie.from_string(sp): data
    for sp, data in all_data["bvsum"].items()
}
PRIOR_PROB = {
    Specie.from_string(sp): data
    for sp, data in all_data["occurrence"].items()
}


def calculate_bv_sum(site, nn_list, scale_factor=1.0):
    """
    Calculates the BV sum of a site.

    Args:
        site (PeriodicSite): The central site to calculate the bond valence
        nn_list ([Neighbor]): A list of namedtuple Neighbors having "distance"
예제 #34
0
 def setUp(self):
     self.specie1 = Specie.from_string("Fe2+")
     self.specie2 = Specie("Fe", 3)
     self.specie3 = Specie("Fe", 2)
     self.specie4 = Specie("Fe", 2, {"spin": 5})
예제 #35
0
    def test_utils(self):
        """Test utilities for the generation of Abinit inputs."""
        # Test as_structure and from/to abivars
        si = Structure.as_structure(abidata.cif_file("si.cif"))
        assert si.formula == "Si2"
        assert si.latex_formula == "Si$_{2}$"
        assert si.abi_spacegroup is None and not si.has_abi_spacegroup
        assert "ntypat" in si.to(fmt="abivars")

        spgroup = si.spgset_abi_spacegroup(has_timerev=True)
        assert spgroup is not None
        assert si.has_abi_spacegroup
        assert si.abi_spacegroup.spgid == 227
        kfrac_coords = si.get_kcoords_from_names(["G", "X", "L", "Gamma"])
        self.assert_equal(kfrac_coords,
            ([[0. , 0. , 0. ], [0.5, 0. , 0.5], [0.5, 0.5, 0.5], [0. , 0. , 0. ]]))

        si_wfk = Structure.as_structure(abidata.ref_file("si_scf_WFK.nc"))
        assert si_wfk.formula == "Si2"
        si_wfk.print_neighbors(radius=2.5)

        assert si_wfk.has_abi_spacegroup
        # Cannot change spacegroup
        with self.assertRaises(ValueError):
            si_wfk.spgset_abi_spacegroup(has_timerev=True)

        # K and U are equivalent. [5/8, 1/4, 5/8] should return U
        assert si_wfk.findname_in_hsym_stars([3/8, 3/8, 3/4]) == "K"
        assert si_wfk.findname_in_hsym_stars([5/8, 1/4, 5/8]) == "U"

        # TODO: Fix order of atoms in supercells.
        # Test __mul__, __rmul__ (should return Abipy structures)
        assert si_wfk == 1 * si_wfk
        supcell = si_wfk * [2, 2, 2]
        assert len(supcell) == 8 * len(si_wfk) and hasattr(supcell, "abi_string")

        si_abi = Structure.from_file(abidata.ref_file("refs/si_ebands/run.abi"))
        assert si_abi.formula == "Si2"
        self.assert_equal(si_abi.frac_coords, [[0, 0, 0], [0.25, 0.25, 0.25]])

        si_abo = Structure.from_file(abidata.ref_file("refs/si_ebands/run.abo"))
        assert si_abo == si_abi
        assert "ntypat" in si_abi.to(fmt="abivars")

        znse = Structure.from_file(abidata.ref_file("refs/znse_phonons/ZnSe_hex_qpt_DDB"))
        assert len(znse) == 4
        assert znse.formula == "Zn2 Se2"
        self.assert_almost_equal(znse.frac_coords.flat, [
            0.33333333333333,  0.66666666666667, 0.99962203020000,
            0.66666666666667,  0.33333333333333, 0.49962203020000,
            0.33333333333333,  0.66666666666667, 0.62537796980000,
            0.66666666666667,  0.33333333333333, 0.12537796980000])

        from abipy.core.structure import diff_structures
        diff_structures([si_abi, znse], headers=["si_abi", "znse"], fmt="abivars", mode="table")
        diff_structures([si_abi, znse], headers=["si_abi", "znse"], fmt="abivars", mode="diff")

        # From pickle file.
        import pickle
        tmp_path = self.get_tmpname(suffix=".pickle")
        with open(tmp_path, "wb") as fh:
            pickle.dump(znse, fh)
        same_znse = Structure.from_file(tmp_path)
        assert same_znse == znse
        same_znse = Structure.as_structure(tmp_path)
        assert same_znse == znse

        for fmt in ["abivars", "cif", "POSCAR", "json", "xsf", "qe", "siesta", "wannier90"]:
            assert len(znse.convert(fmt=fmt)) > 0

        for fmt in ["abinit", "w90", "siesta"]:
            assert len(znse.get_kpath_input_string(fmt=fmt)) > 0

        oxi_znse = znse.get_oxi_state_decorated()
        assert len(oxi_znse.abi_string)
        from pymatgen.core.periodic_table import Specie
        assert Specie("Zn", 2) in oxi_znse.composition.elements
        assert Specie("Se", -2) in oxi_znse.composition.elements

        system = si.spget_lattice_type()
        assert system == "cubic"

        e = si.spget_equivalent_atoms(printout=True)
        assert len(e.irred_pos) == 1
        self.assert_equal(e.eqmap[0], [0, 1])
        for irr_pos in e.irred_pos:
            assert len(e.eqmap[irr_pos]) > 0
        assert "equivalent_atoms" in e.spgdata

        if self.has_matplotlib():
            assert si.plot_bz(show=False)
            assert si.plot_bz(pmg_path=False, show=False)
            assert si.plot(show=False)
            if sys.version[0:3] > '2.7':
                # pmg broke py compatibility
                assert si.plot_xrd(show=False)

        if self.has_mayavi():
            #assert si.plot_vtk(show=False)  # Disabled due to (core dumped) on travis
            assert si.plot_mayaview(show=False)

        if self.has_panel():
            assert hasattr(si.get_panel(), "show")

        assert si is Structure.as_structure(si)
        assert si == Structure.as_structure(si.to_abivars())
        assert si == Structure.from_abivars(si.to_abivars())
        assert len(si.abi_string)
        assert si.reciprocal_lattice == si.lattice.reciprocal_lattice

        kptbounds = si.calc_kptbounds()
        ksamp = si.calc_ksampling(nksmall=10)

        shiftk = [[ 0.5,  0.5,  0.5], [ 0.5,  0. ,  0. ], [ 0. ,  0.5,  0. ], [ 0. ,  0. ,  0.5]]
        self.assert_equal(si.calc_ngkpt(nksmall=2), [2, 2, 2])
        self.assert_equal(si.calc_shiftk(), shiftk)
        self.assert_equal(ksamp.ngkpt, [10, 10, 10])
        self.assert_equal(ksamp.shiftk, shiftk)

        lif = Structure.from_abistring("""
acell      7.7030079150    7.7030079150    7.7030079150 Angstrom
rprim      0.0000000000    0.5000000000    0.5000000000
           0.5000000000    0.0000000000    0.5000000000
           0.5000000000    0.5000000000    0.0000000000
natom      2
ntypat     2
typat      1 2
znucl      3 9
xred       0.0000000000    0.0000000000    0.0000000000
           0.5000000000    0.5000000000    0.5000000000
""")
        assert lif.formula == "Li1 F1"
        same = Structure.rocksalt(7.7030079150, ["Li", "F"], units="ang")
        self.assert_almost_equal(lif.lattice.a,  same.lattice.a)

        si = Structure.from_mpid("mp-149")
        assert si.formula == "Si2"

        # Test abiget_spginfo
        d = si.abiget_spginfo(tolsym=None, pre="abi_")
        assert d["abi_spg_symbol"] == "Fd-3m"
        assert d["abi_spg_number"] == 227
        assert d["abi_bravais"] == "Bravais cF (face-center cubic)"

        llzo = Structure.from_file(abidata.cif_file("LLZO_oxi.cif"))
        assert llzo.is_ordered
        d = llzo.abiget_spginfo(tolsym=0.001)
        assert d["spg_number"] == 142

        mgb2_cod = Structure.from_cod_id(1526507, primitive=True)
        assert mgb2_cod.formula == "Mg1 B2"
        assert mgb2_cod.spget_lattice_type() == "hexagonal"

        mgb2 = abidata.structure_from_ucell("MgB2")
        if self.has_ase():
            mgb2.abi_primitive()

        assert [site.species_string for site in mgb2.get_sorted_structure_z()] == ["B", "B", "Mg"]

        s2inds = mgb2.get_symbol2indices()
        self.assert_equal(s2inds["Mg"], [0])
        self.assert_equal(s2inds["B"], [1, 2])

        s2coords = mgb2.get_symbol2coords()
        self.assert_equal(s2coords["Mg"], [[0, 0, 0]])
        self.assert_equal(s2coords["B"],  [[1/3, 2/3, 0.5], [2/3, 1/3, 0.5]])

        new_mgb2 = mgb2.scale_lattice(mgb2.volume * 1.1)
        self.assert_almost_equal(new_mgb2.volume, mgb2.volume * 1.1)
        assert new_mgb2.lattice.is_hexagonal

        # TODO: This part should be tested more carefully
        mgb2.abi_sanitize()
        mgb2.abi_sanitize(primitive_standard=True)
        mgb2.get_conventional_standard_structure()
        assert len(mgb2.abi_string)
        assert len(mgb2.spget_summary(site_symmetry=True, verbose=10))

        self.serialize_with_pickle(mgb2)

        pseudos = abidata.pseudos("12mg.pspnc", "5b.pspnc")
        nv = mgb2.num_valence_electrons(pseudos)
        assert nv == 8 and isinstance(nv , int)
        assert mgb2.valence_electrons_per_atom(pseudos) == [2, 3, 3]
        self.assert_equal(mgb2.calc_shiftk() , [[0.0, 0.0, 0.5]])

        bmol = Structure.boxed_molecule(pseudos, cart_coords=[[0, 0, 0], [5, 5, 5]], acell=[10, 10, 10])
        self.assert_almost_equal(bmol.volume, (10 * bohr_to_ang) ** 3)

        # FIXME This is buggy
        #acell = np.array([10, 20, 30])
        #batom = Structure.boxed_atom(abidata.pseudo("12mg.pspnc"), cart_coords=[1, 2, 3], acell=acell)
        #assert isinstance(batom, Structure)
        #assert len(batom.cart_coords) == 1
        #self.assert_equal(batom.cart_coords[0], [1, 2, 3])

        # Function to compute cubic a0 from primitive v0 (depends on struct_type)
        vol2a = {"fcc": lambda vol: (4 * vol) ** (1/3.),
                 "bcc": lambda vol: (2 * vol) ** (1/3.),
                 "zincblende": lambda vol: (4 * vol) ** (1/3.),
                 "rocksalt": lambda vol: (4 * vol) ** (1/3.),
                 "ABO3": lambda vol: vol ** (1/3.),
                 "hH": lambda vol: (4 * vol) ** (1/3.),
                 }

        a = 10
        bcc_prim = Structure.bcc(a, ["Si"], primitive=True)
        assert len(bcc_prim) == 1
        self.assert_almost_equal(a, vol2a["bcc"](bcc_prim.volume))
        bcc_conv = Structure.bcc(a, ["Si"], primitive=False)
        assert len(bcc_conv) == 2
        self.assert_almost_equal(a**3, bcc_conv.volume)
        fcc_prim = Structure.fcc(a, ["Si"], primitive=True)
        assert len(fcc_prim) == 1
        self.assert_almost_equal(a, vol2a["fcc"](fcc_prim.volume))
        fcc_conv = Structure.fcc(a, ["Si"], primitive=False)
        assert len(fcc_conv) == 4
        self.assert_almost_equal(a**3, fcc_conv.volume)
        zns = Structure.zincblende(a / bohr_to_ang, ["Zn", "S"], units="bohr")
        self.assert_almost_equal(a, vol2a["zincblende"](zns.volume))
        rock = Structure.rocksalt(a, ["Na", "Cl"])
        assert len(rock) == 2
        self.assert_almost_equal(a, vol2a["rocksalt"](rock.volume))
        perov = Structure.ABO3(a, ["Ca", "Ti", "O", "O", "O"])
        assert len(perov) == 5
        self.assert_almost_equal(a**3, perov.volume)

        # Test notebook generation.
        if self.has_nbformat():
            assert mgb2.write_notebook(nbpath=self.get_tmpname(text=True))
예제 #36
0
 def test_cmp(self):
     self.assertLess(self.specie1, self.specie2, "Fe2+ should be < Fe3+")
     self.assertLess(Specie("C", 1), Specie("Se", 1))
예제 #37
0
def get_dopants_from_shannon_radii(bonded_structure,
                                   num_dopants=5,
                                   match_oxi_sign=False):
    """
    Get dopant suggestions based on Shannon radii differences.

    Args:
        bonded_structure (StructureGraph): A pymatgen structure graph
            decorated with oxidation states. For example, generated using the
            CrystalNN.get_bonded_structure() method.
        num_dopants (int): The nummber of suggestions to return for
            n- and p-type dopants.
        match_oxi_sign (bool): Whether to force the dopant and original species
            to have the same sign of oxidation state. E.g. If the original site
            is in a negative charge state, then only negative dopants will be
            returned.

    Returns:
        (dict): Dopant suggestions, given as a dictionary with keys "n_type" and
        "p_type". The suggestions for each doping type are given as a list of
        dictionaries, each with they keys:

        - "radii_diff": The difference between the Shannon radii of the species.
        - "dopant_spcies": The dopant species.
        - "original_species": The substituted species.
    """
    # get a list of all Specie for all elements in all their common oxid states
    all_species = [
        Specie(el, oxi) for el in Element for oxi in el.common_oxidation_states
    ]

    # get a series of tuples with (coordination number, specie)
    cn_and_species = set((bonded_structure.get_coordination_of_site(i),
                          bonded_structure.structure[i].specie)
                         for i in range(bonded_structure.structure.num_sites))

    cn_to_radii_map = {}
    possible_dopants = []

    for cn, species in cn_and_species:
        cn_roman = _int_to_roman(cn)

        try:
            species_radius = species.get_shannon_radius(cn_roman)
        except KeyError:
            warnings.warn("Shannon radius not found for {} with coordination "
                          "number {}.\nSkipping...".format(species, cn))
            continue

        if cn not in cn_to_radii_map:
            cn_to_radii_map[cn] = _shannon_radii_from_cn(
                all_species, cn_roman, radius_to_compare=species_radius)

        shannon_radii = cn_to_radii_map[cn]

        possible_dopants += [{
            'radii_diff': p['radii_diff'],
            'dopant_species': p['species'],
            'original_species': species
        } for p in shannon_radii]

    possible_dopants.sort(key=lambda x: abs(x['radii_diff']))

    return _get_dopants(possible_dopants, num_dopants, match_oxi_sign)
 def from_dict(cls, d):
     return SubStructureSpecie.from_specie(Specie.from_dict(d['specie']), d.get('weight', 0.0))
예제 #39
0
spgr = struc.get_spacegroup_info()
print(struc.get_spacegroup_info())

# print(struc)
cations = ['Co', 'Rh', 'Ir']
#cations = ['Li', 'Na', 'K', 'Rb', 'Cs']
# cations2 = ['C','Si','Ge','Sn', 'Pb']
anions = ['F', 'Cl', 'Br', 'I']
counter = 0
for cation in cations:
    # for cation2 in cations2:
    for anion in anions:
        try:
            # list = Subs.pred_from_structures(target_species=[Specie(cation, +1), Specie(cation2, +2), Specie(anion, -1)], structures_list=[{'structure': struc, 'id': 'TEST'}])
            list = Subs.pred_from_structures(
                target_species=[Specie(cation, +3),
                                Specie(anion, -1)],
                structures_list=[{
                    'structure': struc,
                    'id': 'TEST'
                }])
        except:
            pass
        for c in list:
            counter += 1
            # c.final_structure.to(filename=cation + cation2 + anion +'3' + '_' + spgr[0] + ".cif")
            c.final_structure.to(filename=cation + anion + '3' + '_' +
                                 spgr[0] + ".cif")

# list_of_all_combos = Subs.pred_from_list([Specie('Be', +2), Specie('O', -2)])
# print(list_of_all_combos)
예제 #40
0
    def _get_structure(self, data, primitive):
        """
        Generate structure from part of the cif.
        """
        def parse_symbol(sym):
            # Common representations for elements/water in cif files
            # TODO: fix inconsistent handling of water
            special = {
                "D": "D",
                "Hw": "H",
                "Ow": "O",
                "Wat": "O",
                "wat": "O",
                "OH": "",
                "OH2": ""
            }
            m = re.findall(r"w?[A-Z][a-z]*", sym)
            if m and m != "?":
                if sym in special:
                    v = special[sym]
                else:
                    v = special.get(m[0], m[0])
                if len(m) > 1 or (m[0] in special):
                    warnings.warn("{} parsed as {}".format(sym, v))
                return v

        lattice = self.get_lattice(data)

        # if magCIF, get magnetic symmetry moments and magmoms
        # else standard CIF, and use empty magmom dict
        if self.feature_flags["magcif_incommensurate"]:
            raise NotImplementedError(
                "Incommensurate structures not currently supported.")
        elif self.feature_flags["magcif"]:
            self.symmetry_operations = self.get_magsymops(data)
            magmoms = self.parse_magmoms(data, lattice=lattice)
        else:
            self.symmetry_operations = self.get_symops(data)
            magmoms = {}

        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()
        coord_to_magmoms = OrderedDict()

        def get_matching_coord(coord):
            keys = list(coord_to_species.keys())
            coords = np.array(keys)
            for op in self.symmetry_operations:
                c = op.operate(coord)
                inds = find_in_coord_list_pbc(coords,
                                              c,
                                              atol=self._site_tolerance)
                # cant use if inds, because python is dumb and np.array([0]) evaluates
                # to False
                if len(inds):
                    return keys[inds[0]]
            return False

        for i in range(len(data["_atom_site_label"])):
            try:
                # If site type symbol exists, use it. Otherwise, we use the
                # label.
                symbol = parse_symbol(data["_atom_site_type_symbol"][i])
            except KeyError:
                symbol = parse_symbol(data["_atom_site_label"][i])
            if not symbol:
                continue

            if oxi_states is not None:
                o_s = oxi_states.get(symbol, 0)
                # use _atom_site_type_symbol if possible for oxidation state
                if "_atom_site_type_symbol" in data.data.keys():
                    oxi_symbol = data["_atom_site_type_symbol"][i]
                    o_s = oxi_states.get(oxi_symbol, o_s)
                try:
                    el = Specie(symbol, o_s)
                except:
                    el = DummySpecie(symbol, o_s)
            else:
                el = get_el_sp(symbol)

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            magmom = magmoms.get(data["_atom_site_label"][i], Magmom(0))

            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                if not match:
                    coord_to_species[coord] = Composition({el: occu})
                    coord_to_magmoms[coord] = magmom
                else:
                    coord_to_species[match] += {el: occu}
                    coord_to_magmoms[
                        match] = None  # disordered magnetic not currently supported

        sum_occu = [sum(c.values()) for c in coord_to_species.values()]
        if any([o > 1 for o in sum_occu]):
            warnings.warn(
                "Some occupancies (%s) sum to > 1! If they are within "
                "the tolerance, they will be rescaled." % str(sum_occu))

        allspecies = []
        allcoords = []
        allmagmoms = []

        # check to see if magCIF file is disordered
        if self.feature_flags["magcif"]:
            for k, v in coord_to_magmoms.items():
                if v is None:
                    # Proposed solution to this is to instead store magnetic moments
                    # as Specie 'spin' property, instead of site property, but this
                    # introduces ambiguities for end user (such as unintended use of
                    # `spin` and Specie will have fictious oxidation state).
                    raise NotImplementedError(
                        'Disordered magnetic structures not currently supported.'
                    )

        if coord_to_species.items():
            for species, group in groupby(sorted(list(
                    coord_to_species.items()),
                                                 key=lambda x: x[1]),
                                          key=lambda x: x[1]):
                tmp_coords = [site[0] for site in group]
                tmp_magmom = [
                    coord_to_magmoms[tmp_coord] for tmp_coord in tmp_coords
                ]

                if self.feature_flags["magcif"]:
                    coords, magmoms = self._unique_coords(
                        tmp_coords, tmp_magmom)
                else:
                    coords, magmoms = self._unique_coords(tmp_coords)

                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])
                allmagmoms.extend(magmoms)

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords) and len(
                allspecies) == len(allmagmoms):

            if self.feature_flags["magcif"]:
                struct = Structure(lattice,
                                   allspecies,
                                   allcoords,
                                   site_properties={"magmom": allmagmoms})
            else:
                struct = Structure(lattice, allspecies, allcoords)

            struct = struct.get_sorted_structure()

            if primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            return struct
예제 #41
0
 def setUp(self):
     self.specie1 = Specie.from_string("Fe2+")
     self.specie2 = Specie("Fe", 3)
     self.specie3 = Specie("Fe", 2)
     self.specie4 = Specie("Fe", 2, {"spin": 5})
예제 #42
0
    def _get_oxid_state_guesses(self, all_oxi_states, max_sites,
                                oxi_states_override, target_charge):
        """
        Utility operation for guessing oxidation states.

        See `oxi_state_guesses` for full details. This operation does the calculation
        of the most likely oxidation states

        Args:
            oxi_states_override (dict): dict of str->list to override an
                element's common oxidation states, e.g. {"V": [2,3,4,5]}
            target_charge (int): the desired total charge on the structure.
                Default is 0 signifying charge balance.
            all_oxi_states (bool): if True, an element defaults to
                all oxidation states in pymatgen Element.icsd_oxidation_states.
                Otherwise, default is Element.common_oxidation_states. Note
                that the full oxidation state list is *very* inclusive and
                can produce nonsensical results.
            max_sites (int): if possible, will reduce Compositions to at most
                this many many sites to speed up oxidation state guesses. Set
                to -1 to just reduce fully.
        Returns:
            A list of dicts - each dict reports an element symbol and average
                oxidation state across all sites in that composition. If the
                composition is not charge balanced, an empty list is returned.
            A list of dicts - each dict maps the element symbol to a list of
                oxidation states for each site of that element. For example, Fe3O4 could
                return a list of [2,2,2,3,3,3] for the oxidation states of If the composition
                is

            """
        comp = self.copy()
        # reduce Composition if necessary
        if max_sites == -1:
            comp = self.reduced_composition

        elif max_sites and comp.num_atoms > max_sites:
            reduced_comp, reduced_factor = self. \
                get_reduced_composition_and_factor()
            if reduced_factor > 1:
                reduced_comp *= max(1, int(max_sites / reduced_comp.num_atoms))
                comp = reduced_comp  # as close to max_sites as possible
            if comp.num_atoms > max_sites:
                raise ValueError("Composition {} cannot accommodate max_sites "
                                 "setting!".format(comp))

        # Load prior probabilities of oxidation states, used to rank solutions
        if not Composition.oxi_prob:
            module_dir = os.path.join(
                os.path.dirname(os.path.abspath(__file__)))
            all_data = loadfn(
                os.path.join(module_dir, "..", "analysis", "icsd_bv.yaml"))
            Composition.oxi_prob = {
                Specie.from_string(sp): data
                for sp, data in all_data["occurrence"].items()
            }
        oxi_states_override = oxi_states_override or {}
        # assert: Composition only has integer amounts
        if not all(amt == int(amt) for amt in comp.values()):
            raise ValueError("Charge balance analysis requires integer "
                             "values in Composition!")

        # for each element, determine all possible sum of oxidations
        # (taking into account nsites for that particular element)
        el_amt = comp.get_el_amt_dict()
        els = el_amt.keys()
        el_sums = []  # matrix: dim1= el_idx, dim2=possible sums
        el_sum_scores = defaultdict(set)  # dict of el_idx, sum -> score
        el_best_oxid_combo = {
        }  # dict of el_idx, sum -> oxid combo with best score
        for idx, el in enumerate(els):
            el_sum_scores[idx] = {}
            el_best_oxid_combo[idx] = {}
            el_sums.append([])
            if oxi_states_override.get(el):
                oxids = oxi_states_override[el]
            elif all_oxi_states:
                oxids = Element(el).oxidation_states
            else:
                oxids = Element(el).icsd_oxidation_states or \
                        Element(el).oxidation_states

            # get all possible combinations of oxidation states
            # and sum each combination
            for oxid_combo in combinations_with_replacement(
                    oxids, int(el_amt[el])):

                # List this sum as a possible option
                oxid_sum = sum(oxid_combo)
                if oxid_sum not in el_sums[idx]:
                    el_sums[idx].append(oxid_sum)

                # Determine how probable is this combo?
                score = sum([
                    Composition.oxi_prob.get(Specie(el, o), 0)
                    for o in oxid_combo
                ])

                # If it is the most probable combo for a certain sum,
                #   store the combination
                if oxid_sum not in el_sum_scores[
                        idx] or score > el_sum_scores[idx].get(oxid_sum, 0):
                    el_sum_scores[idx][oxid_sum] = score
                    el_best_oxid_combo[idx][oxid_sum] = oxid_combo

        # Determine which combination of oxidation states for each element
        #    is the most probable
        all_sols = []  # will contain all solutions
        all_oxid_combo = [
        ]  # will contain the best combination of oxidation states for each site
        all_scores = []  # will contain a score for each solution
        for x in product(*el_sums):
            # each x is a trial of one possible oxidation sum for each element
            if sum(x) == target_charge:  # charge balance condition
                el_sum_sol = dict(zip(els, x))  # element->oxid_sum
                # normalize oxid_sum by amount to get avg oxid state
                sol = {el: v / el_amt[el] for el, v in el_sum_sol.items()}
                all_sols.append(
                    sol)  # add the solution to the list of solutions

                # determine the score for this solution
                score = 0
                for idx, v in enumerate(x):
                    score += el_sum_scores[idx][v]
                all_scores.append(score)

                # collect the combination of oxidation states for each site
                all_oxid_combo.append(
                    dict((e, el_best_oxid_combo[idx][v])
                         for idx, (e, v) in enumerate(zip(els, x))))

        # sort the solutions by highest to lowest score
        if len(all_scores) > 0:
            all_sols, all_oxid_combo = zip(
                *[(y, x)
                  for (z, y,
                       x) in sorted(zip(all_scores, all_sols, all_oxid_combo),
                                    key=lambda pair: pair[0],
                                    reverse=True)])
        return all_sols, all_oxid_combo
예제 #43
0
파일: cif.py 프로젝트: ProkillerJ/pymatgen
    def _get_structure(self, data, primitive):
        """
        Generate structure from part of the cif.
        """
        def parse_symbol(sym):
            # Common representations for elements/water in cif files
            # TODO: fix inconsistent handling of water
            special = {"D": "D", "Hw": "H", "Ow": "O", "Wat": "O",
                       "wat": "O", "OH": "", "OH2": ""}
            m = re.findall(r"w?[A-Z][a-z]*", sym)
            if m and m != "?":
                if sym in special:
                    v = special[sym]
                else:
                    v = special.get(m[0], m[0])
                if len(m) > 1 or (m[0] in special):
                    warnings.warn("{} parsed as {}".format(sym, v))
                return v

        lattice = self.get_lattice(data)
        self.symmetry_operations = self.get_symops(data)
        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()

        def get_matching_coord(coord):
            keys = list(coord_to_species.keys())
            coords = np.array(keys)
            for op in self.symmetry_operations:
                c = op.operate(coord)
                inds = find_in_coord_list_pbc(coords, c, atol=self._site_tolerance)
                # cant use if inds, because python is dumb and np.array([0]) evaluates
                # to False
                if len(inds):
                    return keys[inds[0]]
            return False

        ############################################################
        """
        This part of the code deals with handling formats of data as found in
        CIF files extracted from the Springer Materials/Pauling File
        databases, and that are different from standard ICSD formats.
        """

        # Check to see if "_atom_site_type_symbol" exists, as some test CIFs do
        # not contain this key.
        if "_atom_site_type_symbol" in data.data.keys():

            # Keep a track of which data row needs to be removed.
            # Example of a row: Nb,Zr '0.8Nb + 0.2Zr' .2a .m-3m 0 0 0 1 14
            # 'rhombic dodecahedron, Nb<sub>14</sub>'
            # Without this code, the above row in a structure would be parsed
            # as an ordered site with only Nb (since
            # CifParser would try to parse the first two characters of the
            # label "Nb,Zr") and occupancy=1.
            # However, this site is meant to be a disordered site with 0.8 of
            # Nb and 0.2 of Zr.
            idxs_to_remove = []

            for idx, el_row in enumerate(data["_atom_site_label"]):

                # CIF files from the Springer Materials/Pauling File have
                # switched the label and symbol. Thus, in the
                # above shown example row, '0.8Nb + 0.2Zr' is the symbol.
                # Below, we split the strings on ' + ' to
                # check if the length (or number of elements) in the label and
                # symbol are equal.
                if len(data["_atom_site_type_symbol"][idx].split(' + ')) > \
                        len(data["_atom_site_label"][idx].split(' + ')):

                    # Dictionary to hold extracted elements and occupancies
                    els_occu = {}

                    # parse symbol to get element names and occupancy and store
                    # in "els_occu"
                    symbol_str = data["_atom_site_type_symbol"][idx]
                    symbol_str_lst = symbol_str.split(' + ')
                    for elocc_idx in range(len(symbol_str_lst)):
                        # Remove any bracketed items in the string
                        symbol_str_lst[elocc_idx] = re.sub(r'\([0-9]*\)', '',
                            symbol_str_lst[elocc_idx].strip())

                        # Extract element name and its occupancy from the
                        # string, and store it as a
                        # key-value pair in "els_occ".
                        els_occu[str(re.findall(r'\D+', symbol_str_lst[
                            elocc_idx].strip())[1]).replace('<sup>', '')] = \
                            float('0' + re.findall(r'\.?\d+', symbol_str_lst[
                                elocc_idx].strip())[1])

                    x = str2float(data["_atom_site_fract_x"][idx])
                    y = str2float(data["_atom_site_fract_y"][idx])
                    z = str2float(data["_atom_site_fract_z"][idx])

                    coord = (x, y, z)
                    # Add each partially occupied element on the site coordinate
                    for et in els_occu:
                        match = get_matching_coord(coord)
                        if not match:
                            coord_to_species[coord] = Composition(
                                {parse_symbol(et): els_occu[parse_symbol(et)]})
                        else:
                            coord_to_species[match] += {
                                parse_symbol(et): els_occu[parse_symbol(et)]}
                    idxs_to_remove.append(idx)

            # Remove the original row by iterating over all keys in the CIF
            # data looking for lists, which indicates
            # multiple data items, one for each row, and remove items from the
            # list that corresponds to the removed row,
            # so that it's not processed by the rest of this function (which
            # would result in an error).
            for cif_key in data.data:
                if type(data.data[cif_key]) == list:
                    for id in sorted(idxs_to_remove, reverse=True):
                        del data.data[cif_key][id]

        ############################################################
        for i in range(len(data["_atom_site_label"])):
            try:
                # If site type symbol exists, use it. Otherwise, we use the
                # label.
                symbol = parse_symbol(data["_atom_site_type_symbol"][i])
            except KeyError:
                symbol = parse_symbol(data["_atom_site_label"][i])
            if not symbol:
                continue

            if oxi_states is not None:
                o_s = oxi_states.get(symbol, 0)
                # use _atom_site_type_symbol if possible for oxidation state
                if "_atom_site_type_symbol" in data.data.keys():
                    oxi_symbol = data["_atom_site_type_symbol"][i]
                    o_s = oxi_states.get(oxi_symbol, o_s)
                try:
                    el = Specie(symbol, o_s)
                except:
                    el = DummySpecie(symbol, o_s)
            else:
                el = get_el_sp(symbol)

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])

            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                if not match:
                    coord_to_species[coord] = Composition({el: occu})
                else:
                    coord_to_species[match] += {el: occu}

        sum_occu = [sum(c.values()) for c in coord_to_species.values()]
        if any([o > 1 for o in sum_occu]):
            warnings.warn("Some occupancies (%s) sum to > 1! If they are within "
                          "the tolerance, they will be rescaled." % str(sum_occu))

        allspecies = []
        allcoords = []

        if coord_to_species.items():
            for species, group in groupby(
                    sorted(list(coord_to_species.items()), key=lambda x: x[1]),
                    key=lambda x: x[1]):
                tmp_coords = [site[0] for site in group]

                coords = self._unique_coords(tmp_coords)

                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords):
            struct = Structure(lattice, allspecies, allcoords)
            struct = struct.get_sorted_structure()

            if primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()
            return struct
예제 #44
0
def Analyze_VASP_MD(args):
    """
    Analyze diffusivity from a series vasprun.xml (or vasprun.xml.gz) files at one
            temperature
    :param args: please check main function for details of args
    :return:
    """
    vasprun_dirs = []
    for i in range(args.runs_start, args.runs_end + 1):
        if os.path.exists(
                os.path.join(args.folder_feature + str(i), 'vasprun.xml.gz')):
            vasprun_dirs.append(
                os.path.join(args.folder_feature + str(i), 'vasprun.xml.gz'))
        elif os.path.exists(
                os.path.join(args.folder_feature + str(i), 'vasprun.xml')):
            vasprun_dirs.append(
                os.path.join(args.folder_feature + str(i), 'vasprun.xml'))
        else:
            raise Exception(
                "No vasprun.xml or vasprun.xml.gz in folder {}".format(
                    args.folder_feature + str(i)))

    # In analyzing Arrhenius relationship, it is required to provide charged specie. To keep consistent, I also
    # require charged specie, even it is not necessary
    specie = Specie.from_string(args.specie)
    da = DiffusivityAnalyzer.from_files(vasprun_dirs, str(specie.element), step_skip=args.step_skip,
                                        ncores=args.ncores,
                                        time_intervals_number=args.time_intervals_number,
                                        spec_dict={'lower_bound': args.lower_bound_in_a_square \
                                                                  * args.site_distance \
                                                                  * args.site_distance,
                                                   'upper_bound': args.upper_bound,
                                                   'minimum_msd_diff': args.minimum_msd_diff_in_a_square \
                                                                       * args.site_distance \
                                                                       * args.site_distance,
                                                   }
                                        )
    ea = ErrorAnalysisFromDiffusivityAnalyzer(da,
                                              site_distance=args.site_distance)
    if da.diffusivity > 0:  # The linear fitting succeed
        summary_info = ea.get_summary_dict(oxidized_specie=args.specie)
    # if the msd profile of the MD doesn't fulfill the fitting requirements,
    # da.diffusivity is set  to be negative
    else:
        summary_info = {
            "diffusion result":
            "MSD calculated from MD doesn't fulfill the fitting requirement",
            "max msd": max(da.msd),
            'msd': da.msd,
            'dt': da.dt,
            'msd_component': da.msd_component
        }
        print("Output msd-dt into {}K_msd-dt.csv".format(int(da.temperature)))
        args.msd_file = "{}K_msd-dt.csv".format(int(da.temperature))

    # output
    print("=" * 40)
    print("Used vasprun.xml files")
    print("Start run: {}, end run: {}".format(vasprun_dirs[0],
                                              vasprun_dirs[-1]))
    print("=" * 40)

    # results table
    header_result = ("Parameter", "Value")
    result_table = PrettyTable(header_result)
    result_table.align["Parameter"] = "l"
    for k, v in summary_info.items():
        if k not in ['msd', 'dt', 'msd_component']:
            result_table.add_row([k, str(v)])
    result_table.add_row(['composition', str(da.structure.composition)])
    print("Results table: ")
    print("Diffusivity unit: cm^2/s, Conductivity unit: mS/cm")
    print(result_table.get_string(sortby='Parameter'))
    # print(citing_info)
    # whether output msd
    if args.msd_file:
        print("Output msd-dt into file: {}".format(args.msd_file))
        with open(args.msd_file, 'w') as fp:
            w_csv = csv.writer(fp, delimiter=',')
            data = [
                summary_info['dt'], summary_info['msd'],
                summary_info['msd_component'][0],
                summary_info['msd_component'][1],
                summary_info['msd_component'][2]
            ]
            w_csv.writerows([[
                "dt (fs)", "msd (A^2)", "msd_component_0", "msd_component_1",
                "msd_component_2"
            ]])
            w_csv.writerows(zip(*data))
예제 #45
0
 def test_cached(self):
     specie5 = Specie("Fe", 2)
     self.assertEqual(id(specie5), id(self.specie3))
예제 #46
0
                           "N", "P", "As", "Sb",
                           "O", "S", "Se", "Te",
                           "F", "Cl", "Br", "I"])

module_dir = os.path.dirname(os.path.abspath(__file__))

#Read in BV parameters.
BV_PARAMS = {}
with open(os.path.join(module_dir, "bvparam_1991.json"), "r") as f:
    for k, v in json.load(f).items():
        BV_PARAMS[Element(k)] = v

#Read in json containing data-mined ICSD BV data.
with open(os.path.join(module_dir, "icsd_bv.json"), "r") as f:
    all_data = json.load(f)
    ICSD_BV_DATA = {Specie.from_string(sp): data
                    for sp, data in all_data["bvsum"].items()}
    PRIOR_PROB = {Specie.from_string(sp): data
                  for sp, data in all_data["occurrence"].items()}


def calculate_bv_sum(site, nn_list, scale_factor=1.0):
    """
    Calculates the BV sum of a site.

    Args:
        site:
            The site
        nn_list:
            List of nearest neighbors in the format [(nn_site, dist), ...].
        anion_el:
예제 #47
0
 def test_deepcopy(self):
     el1 = Specie("Fe", 4)
     el2 = Specie("Na", 1)
     ellist = [el1, el2]
     self.assertEqual(ellist, deepcopy(ellist),
                      "Deepcopy operation doesn't produce exact copy.")
 def from_dict(cls, d):
     return SubStructureSite.from_coords_and_specie(d['coords'],
                                                    Specie.from_dict(d['specie']), d.get('weight', 0.0))