Пример #1
0
    def test_attributes(self):
        is_true = {("Xe", "Kr") : "is_noble_gas",
                   ("Fe", "Ni") : 'is_transition_metal',
                   ('Li', 'Cs') : 'is_alkali',
                   ('Ca', 'Mg') : 'is_alkaline',
                   ('F', 'Br', 'I') : 'is_halogen',
                   ('La',) : 'is_lanthanoid',
                   ('U', 'Pu') : 'is_actinoid',
                   ('Si', 'Ge') : 'is_metalloid'
                   }

        for k, v in is_true.items():
            for sym in k:
                self.assertTrue(getattr(Element(sym), v), sym + ' is false')

        keys = ["name", "Z", "mendeleev_no", "atomic_mass", "electronic_structure", "X", "atomic_radius", "min_oxidation_state",
                        "max_oxidation_state", "electrical_resistivity", "velocity_of_sound",
                        "reflectivity", "refractive_index", "poissons_ratio", "molar_volume", "thermal_conductivity", "melting_point", "boiling_point",
                        "liquid_range", "critical_temperature", "superconduction_temperature",
                        "bulk_modulus", "youngs_modulus", "brinell_hardness", "rigidity_modulus", "mineral_hardness",
                        "vickers_hardness", "density_of_solid", "coefficient_of_linear_thermal_expansion", "oxidation_states", "common_oxidation_states", 'average_ionic_radius', 'ionic_radii']

        #Test all elements up to Uranium
        for i in range(1, 93):
            for k in keys:
                self.assertIsNotNone(getattr(Element.from_Z(i), k))
            el = Element.from_Z(i)
            if len(el.oxidation_states) > 0:
                self.assertEqual(max(el.oxidation_states), el.max_oxidation_state)
                self.assertEqual(min(el.oxidation_states), el.min_oxidation_state)
Пример #2
0
    def test_attributes(self):
        is_true = {("Xe", "Kr"): "is_noble_gas",
                   ("Fe", "Ni"): "is_transition_metal",
                   ("Li", "Cs"): "is_alkali",
                   ("Ca", "Mg"): "is_alkaline",
                   ("F", "Br", "I"): "is_halogen",
                   ("La",): "is_lanthanoid",
                   ("U", "Pu"): "is_actinoid",
                   ("Si", "Ge"): "is_metalloid",
                   ("O", "Te"): "is_chalcogen"}

        for k, v in is_true.items():
            for sym in k:
                self.assertTrue(getattr(Element(sym), v), sym + " is false")

        keys = ["mendeleev_no", "atomic_mass",
                "electronic_structure", "atomic_radius",
                "min_oxidation_state", "max_oxidation_state",
                "electrical_resistivity", "velocity_of_sound", "reflectivity",
                "refractive_index", "poissons_ratio", "molar_volume",
                "thermal_conductivity", "melting_point", "boiling_point",
                "liquid_range", "critical_temperature",
                "superconduction_temperature",
                "bulk_modulus", "youngs_modulus", "brinell_hardness",
                "rigidity_modulus", "mineral_hardness",
                "vickers_hardness", "density_of_solid",
                "atomic_orbitals", "coefficient_of_linear_thermal_expansion",
                "oxidation_states",
                "common_oxidation_states", "average_ionic_radius",
                "average_cationic_radius", "average_anionic_radius",
                "ionic_radii", "long_name", "metallic_radius", "iupac_ordering"]

        # Test all elements up to Uranium
        for i in range(1, 104):
            el = Element.from_Z(i)
            d = el.data
            for k in keys:
                k_str = k.capitalize().replace("_", " ")
                if k_str in d and (not str(d[k_str]).startswith("no data")):
                    self.assertIsNotNone(getattr(el, k))
                elif k == "long_name":
                    self.assertEqual(getattr(el, "long_name"), d["Name"])
                elif k == "iupac_ordering":
                    self.assertTrue("IUPAC ordering" in d)
                    self.assertIsNotNone(getattr(el, k))
            el = Element.from_Z(i)
            if len(el.oxidation_states) > 0:
                self.assertEqual(max(el.oxidation_states),
                                 el.max_oxidation_state)
                self.assertEqual(min(el.oxidation_states),
                                 el.min_oxidation_state)

            if el.symbol not in ["He", "Ne", "Ar"]:
                self.assertTrue(el.X > 0, "No electroneg for %s" % el)

        self.assertRaises(ValueError, Element.from_Z, 1000)
Пример #3
0
 def test_equals(self):
     random_z = random.randint(1, 92)
     fixed_el = Element.from_Z(random_z)
     other_z = random.randint(1, 92)
     while other_z == random_z:
         other_z = random.randint(1, 92)
     comp1 = Composition({fixed_el:1, Element.from_Z(other_z) :0})
     other_z = random.randint(1, 92)
     while other_z == random_z:
         other_z = random.randint(1, 92)
     comp2 = Composition({fixed_el:1, Element.from_Z(other_z) :0})
     self.assertEqual(comp1, comp2, "Composition equality test failed. %s should be equal to %s" % (comp1.formula, comp2.formula))
     self.assertEqual(comp1.__hash__(), comp2.__hash__(), "Hashcode equality test failed!")
Пример #4
0
    def test_element(self):
        symbols = list()
        for i in range(1, 102):
            el = Element.from_Z(i)
            self.assertGreater(el.atomic_mass, 0,
                               "Atomic mass cannot be negative!")
            self.assertNotIn(el.symbol, symbols,
                             "Duplicate symbol for " + el.symbol)
            symbols.append(""" + el.symbol + """)
            self.assertIsNotNone(el.group,
                                 "Group cannot be none for Z=" + str(i))
            self.assertIsNotNone(el.row, "Row cannot be none for Z=" + str(i))

            #Test all properties
            all_attr = ["Z", "symbol", "X", "name", "atomic_mass",
                        "atomic_radius", "max_oxidation_state",
                        "min_oxidation_state", "mendeleev_no",
                        "electrical_resistivity", "velocity_of_sound",
                        "reflectivity", "refractive_index", "poissons_ratio",
                        "molar_volume", "electronic_structure",
                        "thermal_conductivity", "boiling_point",
                        "melting_point", "critical_temperature",
                        "superconduction_temperature", "liquid_range",
                        "bulk_modulus", "youngs_modulus", "brinell_hardness",
                        "rigidity_modulus", "mineral_hardness",
                        "vickers_hardness", "density_of_solid",
                        "coefficient_of_linear_thermal_expansion"]

            for a in all_attr:
                self.assertIsNotNone(el, a)
Пример #5
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)
Пример #6
0
        def _parse_chomp_and_rank(m, f, m_dict, m_points):
            """
            A helper method for formula parsing that helps in interpreting and
            ranking indeterminate formulas
            Author: Anubhav Jain

            Args:
                m: A regex match, with the first group being the element and
                    the second group being the amount
                f: The formula part containing the match
                m_dict: A symbol:amt dictionary from the previously parsed
                    formula
                m_points: Number of points gained from the previously parsed
                    formula

            Returns:
                A tuple of (f, m_dict, points) where m_dict now contains data
                from the match and the match has been removed (chomped) from
                the formula f. The "goodness" of the match determines the
                number of points returned for chomping. Returns
                (None, None, None) if no element could be found...
            """

            points = 0
            # Points awarded if the first element of the element is correctly
            # specified as a capital
            points_first_capital = 100
            # Points awarded if the second letter of the element is correctly
            # specified as lowercase
            points_second_lowercase = 100

            #get element and amount from regex match
            el = m.group(1)
            if len(el) > 2 or len(el) < 1:
                raise CompositionError("Invalid element symbol entered!")
            amt = float(m.group(2)) if m.group(2).strip() != "" else 1

            #convert the element string to proper [uppercase,lowercase] format
            #and award points if it is already in that format
            char1 = el[0]
            char2 = el[1] if len(el) > 1 else ""

            if char1 == char1.upper():
                points += points_first_capital
            if char2 and char2 == char2.lower():
                points += points_second_lowercase

            el = char1.upper() + char2.lower()

            #if it's a valid element, chomp and add to the points
            if Element.is_valid_symbol(el):
                if el in m_dict:
                    m_dict[el] += amt * factor
                else:
                    m_dict[el] = amt * factor
                return f.replace(m.group(), "", 1), m_dict, m_points + points

            #else return None
            return None, None, None
Пример #7
0
    def test_pickle(self):
        el1 = Element.Fe
        o = pickle.dumps(el1)
        self.assertEqual(el1, pickle.loads(o))

        #Test all elements up to Uranium
        for i in range(1, 93):
            self.serialize_with_pickle(Element.from_Z(i), test_eq=True)
Пример #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 from_string(string, symbols=None):
    structures = []
    timesteps = []
    steps = string.split("ITEM: TIMESTEP")
    steps.pop(0)
    for step in steps:
        lines = tuple(step.split("\n"))
        mdstep = int(lines[1])
        natoms = int(lines[3])
        xbox   = tuple((lines[5] + " 0").split())
        ybox   = tuple((lines[6] + " 0").split())
        zbox   = tuple((lines[7] + " 0").split())
        xlo = float(xbox[0])
        xhi = float(xbox[1])
        xy  = float(xbox[2])
        ylo = float(ybox[0])
        yhi = float(ybox[1])
        xz  = float(ybox[2])
        zlo = float(zbox[0])
        zhi = float(zbox[1])
        yz  = float(zbox[2])
        xlo -= np.min([0, xy, xz, xy+xz])
        xhi -= np.max([0, xy, xz, xy+xz])
        ylo -= np.min([0, yz])
        yhi -= np.max([0, yz])
      
        lattice = [xhi-xlo, 0, 0, xy, yhi-ylo, 0, xz, yz, zhi-zlo]
        atoms = [[float(j) for j in line.split()] for line in lines[9:-1]]
        atoms = sorted(atoms, key=lambda a_entry: a_entry[0], reverse=True) 

        coords = []
        atomic_sym = []
        while (len(atoms)):
           one = atoms.pop()
           typ = one[1]
           coo = one[2:5]
           sym = symbols[typ] if symbols else Element.from_Z(typ).symbol
           atomic_sym.append(sym)
           coords.append(coo)
        struct = Structure(lattice, atomic_sym, coords,
                   to_unit_cell=False, validate_proximity=False,
                   coords_are_cartesian=False)
        structures.append(struct)
        timesteps.append(mdstep)
        
    return lmpdump(structures, timesteps)
Пример #10
0
    def test_element(self):
        symbols = list()
        for i in range(1, 102):
            el = Element.from_Z(i)
            self.assertGreater(el.atomic_mass, 0, "Atomic mass cannot be negative!")
            self.assertNotIn(el.symbol, symbols, "Duplicate symbol for " + el.symbol)
            symbols.append('"' + el.symbol + '"')
            self.assertIsNotNone(el.group, "Group cannot be none for Z=" + str(i))
            self.assertIsNotNone(el.row, "Row cannot be none for Z=" + str(i))

            #Test all properties
            all_attr = ['Z', 'symbol', 'X', 'name', 'atomic_mass', 'atomic_radius', 'max_oxidation_state', 'min_oxidation_state', 'mendeleev_no',
            'electrical_resistivity', 'velocity_of_sound', 'reflectivity', 'refractive_index', 'poissons_ratio', 'molar_volume' , 'electronic_structure',
            'thermal_conductivity', 'boiling_point', 'melting_point', 'critical_temperature', 'superconduction_temperature', 'liquid_range', 'bulk_modulus',
            'youngs_modulus', 'brinell_hardness', 'rigidity_modulus', 'mineral_hardness', 'vickers_hardness', 'density_of_solid', 'coefficient_of_linear_thermal_expansion']

            for a in all_attr:
                self.assertIsNotNone(el, a)
Пример #11
0
def get_transformed_entries(entries, elements):
    comp_matrix = get_comp_matrix(entries, elements)
    newmat = []
    energies = []
    for i in xrange(len(elements)):
        col = comp_matrix[:, i]
        maxval = max(col)
        maxind = list(col).index(maxval)
        newmat.append(comp_matrix[maxind])
        energies.append(entries[i].energy_per_atom)
    invm = np.linalg.inv(np.array(newmat).transpose())
    newentries = []
    for i in xrange(len(entries)):
        entry = entries[i]
        lincomp = np.dot(invm, comp_matrix[i])
        lincomp = np.around(lincomp, 5)
        comp = Composition({Element.from_Z(j + 1):lincomp[j] for j in xrange(len(elements))})
        scaled_energy = entry.energy_per_atom - sum(lincomp * energies)
        newentries.append(TransformedPDEntry(comp, scaled_energy, entry))
    return newentries
Пример #12
0
    def write_data_file(self, organism, job_dir_path, composition_space):
        """
        Writes the file (called in.data) containing the structure that LAMMPS
        reads.

        Args:
            organism: the Organism whose structure to write

            job_dir_path: the path the job directory (as a string) where the
                file will be written

            composition_space: the CompositionSpace of the search
        """

        # get xhi, yhi and zhi from the lattice vectors
        lattice_coords = organism.cell.lattice.matrix
        xhi = lattice_coords[0][0]
        yhi = lattice_coords[1][1]
        zhi = lattice_coords[2][2]
        box_size = [[0.0, xhi], [0.0, yhi], [0.0, zhi]]

        # get xy, xz and yz from the lattice vectors
        xy = lattice_coords[1][0]
        xz = lattice_coords[2][0]
        yz = lattice_coords[2][1]

        # get a list of the elements from the lammps input script to
        # preserve their order of appearance
        # TODO: not all formats give the element symbols at the end of the line
        #       containing the pair_coeff keyword. Find a better way.
        num_elements = len(composition_space.get_all_elements())
        if num_elements == 1:
            all_elements = composition_space.get_all_elements()
        else:
            with open(self.input_script, 'r') as f:
                lines = f.readlines()
                for line in lines:
                    if 'pair_coeff' in line:
                        element_symbols = line.split()[-1 * num_elements:]
                all_elements = []
                for symbol in element_symbols:
                    all_elements.append(Element(symbol))

        # get the dictionary of atomic masses - set the atom types to the order
        # of their appearance in the lammps input script
        atomic_masses_dict = {}
        for i in range(len(all_elements)):
            atomic_masses_dict[all_elements[i].symbol] = [
                i + 1, float(all_elements[i].atomic_mass)
            ]

        # get the atoms data
        atoms_data = LammpsData.get_atoms_data(organism.cell,
                                               atomic_masses_dict,
                                               set_charge=True)

        # make a LammpsData object
        lammps_data = LammpsData(box_size, atomic_masses_dict.values(),
                                 atoms_data)

        # write the data to a file
        # This method doesn't write the tilts, so we have to add those.
        lammps_data.write_file(job_dir_path + '/in.data')

        # read the in.data file as a list of strings
        with open(job_dir_path + '/in.data', 'r') as f:
            lines = f.readlines()

        # find the location to insert the tilts
        insertion_index = 0
        for line in lines:
            if 'zhi' in line:
                insertion_index = lines.index(line) + 1

        # build the string containing the tilts and add it
        tilts_string = str(xy) + ' ' + str(xz) + ' ' + str(yz) + ' xy xz yz\n'
        lines.insert(insertion_index, tilts_string)

        # overwrite the in.data file with the new contents, including the tilts
        with open(job_dir_path + '/in.data', 'w') as f:
            for line in lines:
                f.write('%s' % line)
Пример #13
0
    def from_string(data, default_names=None):
        """
        Reads a Poscar from a string.

        The code will try its best to determine the elements in the POSCAR in
        the following order:
        1. If default_names are supplied and valid, it will use those. Usually,
        default names comes from an external source, such as a POTCAR in the
        same directory.
        2. If there are no valid default names but the input file is Vasp5-like
        and contains element symbols in the 6th line, the code will use that.
        3. Failing (2), the code will check if a symbol is provided at the end
        of each coordinate.

        If all else fails, the code will just assign the first n elements in
        increasing atomic number, where n is the number of species, to the
        Poscar. For example, H, He, Li, ....  This will ensure at least a
        unique element is assigned to each site and any analysis that does not
        require specific elemental properties should work fine.

        Args:
            data:
                string containing Poscar data.
            default_names:
                default symbols for the POSCAR file, usually coming from a
                POTCAR in the same directory.

        Returns:
            Poscar object.
        """

        chunks = re.split("^\s*$", data.strip(), flags=re.MULTILINE)

        #Parse positions
        lines = tuple(clean_lines(chunks[0].split("\n"), False))
        comment = lines[0]
        scale = float(lines[1])
        lattice = np.array([map(float, line.split())
                            for line in lines[2:5]])
        if scale < 0:
            # In vasp, a negative scale factor is treated as a volume. We need
            # to translate this to a proper lattice vector scaling.
            vol = abs(det(lattice))
            lattice *= (-scale / vol) ** (1 / 3)
        else:
            lattice *= scale

        vasp5_symbols = False
        try:
            natoms = map(int, lines[5].split())
            ipos = 6
        except ValueError:
            vasp5_symbols = True
            symbols = lines[5].split()
            natoms = map(int, lines[6].split())
            atomic_symbols = list()
            for i in xrange(len(natoms)):
                atomic_symbols.extend([symbols[i]] * natoms[i])
            ipos = 7

        postype = lines[ipos].split()[0]

        sdynamics = False
        # Selective dynamics
        if postype[0] in "sS":
            sdynamics = True
            ipos += 1
            postype = lines[ipos].split()[0]

        cart = postype[0] in "cCkK"
        nsites = sum(natoms)

        # If default_names is specified (usually coming from a POTCAR), use
        # them. This is in line with Vasp"s parsing order that the POTCAR
        # specified is the default used.
        if default_names:
            try:
                atomic_symbols = []
                for i in xrange(len(natoms)):
                    atomic_symbols.extend([default_names[i]] * natoms[i])
                vasp5_symbols = True
            except IndexError:
                pass

        if not vasp5_symbols:
            ind = 3 if not sdynamics else 6
            try:
                #check if names are appended at the end of the coordinates.
                atomic_symbols = [l.split()[ind]
                                  for l in lines[ipos + 1:ipos + 1 + nsites]]
                #Ensure symbols are valid elements
                if not all([Element.is_valid_symbol(sym)
                            for sym in atomic_symbols]):
                    raise ValueError("Non-valid symbols detected.")
                vasp5_symbols = True
            except (ValueError, IndexError):
                #Defaulting to false names.
                atomic_symbols = []
                for i in xrange(len(natoms)):
                    sym = Element.from_Z(i + 1).symbol
                    atomic_symbols.extend([sym] * natoms[i])
                warnings.warn("Elements in POSCAR cannot be determined. "
                              "Defaulting to false names {}."
                              .format(" ".join(atomic_symbols)))

        # read the atomic coordinates
        coords = []
        selective_dynamics = list() if sdynamics else None
        for i in xrange(nsites):
            toks = lines[ipos + 1 + i].split()
            coords.append(map(float, toks[:3]))
            if sdynamics:
                selective_dynamics.append([tok.upper()[0] == "T"
                                           for tok in toks[3:6]])

        struct = Structure(lattice, atomic_symbols, coords, False, False, cart)

        #parse velocities if any
        velocities = []
        if len(chunks) > 1:
            for line in chunks[1].strip().split("\n"):
                velocities.append([float(tok) for tok in line.split()])

        predictor_corrector = []
        if len(chunks) > 2:
            lines = chunks[2].strip().split("\n")
            predictor_corrector.append([int(lines[0])])
            for line in lines[1:]:
                predictor_corrector.append([float(tok)
                                            for tok in line.split()])

        return Poscar(struct, comment, selective_dynamics, vasp5_symbols,
                      velocities=velocities,
                      predictor_corrector=predictor_corrector)
def get_excluded_list():
    
    filename = "excluded_compounds.p"
    if os.path.exists(filename):
        with open(filename) as f:
            return pickle.load(f)
    
    from ga_optimization_ternary.fitness_evaluators import FitnessEvaluator, eval_fitness_simple
    all_AB = FitnessEvaluator(eval_fitness_simple, 10)._reverse_dict.keys()
    print 'generating exclusion ranks...'
    exclusions = []
    
    for a in all_AB:
        for b in all_AB:
            for x in range(7):
                
                #nelectrons must be even
                ne_a = Element.from_Z(a).Z
                ne_b = Element.from_Z(b).Z
                ne_x = None
                if x == 0:
                    ne_x = Element("O").Z * 3
                elif x == 1:
                    ne_x = Element("O").Z * 2 + Element("N").Z
                elif x == 2:
                    ne_x = Element("O").Z + Element("N").Z * 2
                elif x == 3:
                    ne_x = Element("N").Z
                elif x == 4:
                    ne_x = Element("O").Z * 2 + Element("F").Z
                elif x == 5:
                    ne_x = Element("O").Z + Element("F").Z + Element("N").Z
                elif x == 6:
                    ne_x = Element("O").Z * 2 + Element("S").Z
                
                #modify the score based on charge-balance
                even_found = False
                el_a = Element.from_Z(a)
                el_b = Element.from_Z(b)
                val_x = 0
                if x == 0:
                    val_x = -2 * 3
                elif x == 1:
                    val_x = (-2 * 2) + (-3 * 1)
                elif x == 2:
                    val_x = (-2 * 1) + (-3 * 2)
                elif x == 3:
                    val_x = -3 * 3
                elif x == 4:
                    val_x = (-2 * 2) + (-1 * 1)
                elif x == 5:
                    val_x = (-2 * 1) + (-1 * 1) + (-3 * 1)
                elif x == 6:
                    val_x = (-2 * 2) + (-2 * 1)
                
                for a_oxi in el_a.oxidation_states:
                    for b_oxi in el_b.oxidation_states:
                        if (ne_a + ne_b + ne_x) % 2 == 0 and (a_oxi + b_oxi + val_x) == 0:
                            even_found = True
                
                if not even_found:
                    exclusions.append((a, b, x))
                
    with open(filename, "wb") as f:
        pickle.dump(exclusions, f)
    return exclusions
    def test_get_data(self):
        props = {
            "energy",
            "energy_per_atom",
            "formation_energy_per_atom",
            "nsites",
            "unit_cell_formula",
            "pretty_formula",
            "is_hubbard",
            "elements",
            "nelements",
            "e_above_hull",
            "hubbards",
            "is_compatible",
            "task_ids",
            "density",
            "icsd_ids",
            "total_magnetization",
        }
        mpid = "mp-1143"
        vals = requests.get(f"http://www.materialsproject.org/materials/{mpid}/json/")
        expected_vals = vals.json()

        for prop in props:
            if prop not in [
                "hubbards",
                "unit_cell_formula",
                "elements",
                "icsd_ids",
                "task_ids",
            ]:
                val = self.rester.get_data(mpid, prop=prop)[0][prop]
                if prop in ["energy", "energy_per_atom"]:
                    prop = "final_" + prop
                self.assertAlmostEqual(expected_vals[prop], val, 2, "Failed with property %s" % prop)
            elif prop in ["elements", "icsd_ids", "task_ids"]:
                upstream_vals = set(self.rester.get_data(mpid, prop=prop)[0][prop])
                self.assertLessEqual(set(expected_vals[prop]), upstream_vals)
            else:
                self.assertEqual(
                    expected_vals[prop],
                    self.rester.get_data(mpid, prop=prop)[0][prop],
                )

        props = ["structure", "initial_structure", "final_structure", "entry"]
        for prop in props:
            obj = self.rester.get_data(mpid, prop=prop)[0][prop]
            if prop.endswith("structure"):
                self.assertIsInstance(obj, Structure)
            elif prop == "entry":
                obj = self.rester.get_data(mpid, prop=prop)[0][prop]
                self.assertIsInstance(obj, ComputedEntry)

        # Test chemsys search
        data = self.rester.get_data("Fe-Li-O", prop="unit_cell_formula")
        self.assertTrue(len(data) > 1)
        elements = {Element("Li"), Element("Fe"), Element("O")}
        for d in data:
            self.assertTrue(set(Composition(d["unit_cell_formula"]).elements).issubset(elements))

        self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3", "badmethod")
Пример #16
0
    def test_ape(self):
        f = AtomicPackingEfficiency()
        ef = ElementFraction()
        ef.set_n_jobs(1)

        # Test the APE calculation routines
        self.assertAlmostEqual(1.11632, f.get_ideal_radius_ratio(15))
        self.assertAlmostEqual(0.154701, f.get_ideal_radius_ratio(2))
        self.assertAlmostEqual(1.65915, f.get_ideal_radius_ratio(27))
        self.assertAlmostEqual(15, f.find_ideal_cluster_size(1.116)[0])
        self.assertAlmostEqual(3, f.find_ideal_cluster_size(0.1)[0])
        self.assertAlmostEqual(24, f.find_ideal_cluster_size(2)[0])

        # Test the nearest neighbor lookup tool
        nn_lookup = f.create_cluster_lookup_tool(
            [Element('Cu'), Element('Zr')])

        #  Check that the table gets the correct structures
        stable_clusters = [
            Composition('CuZr10'),
            Composition('Cu6Zr6'),
            Composition('Cu8Zr5'),
            Composition('Cu13Zr1'),
            Composition('Cu3Zr12'),
            Composition('Cu8Zr8'),
            Composition('Cu12Zr5'),
            Composition('Cu17Zr')
        ]
        ds, _ = nn_lookup.kneighbors(ef.featurize_many(stable_clusters),
                                     n_neighbors=1)
        self.assertArrayAlmostEqual([[0]] * 8, ds)
        self.assertEqual(8, nn_lookup._fit_X.shape[0])

        # Swap the order of the clusters, make sure it gets the same list
        nn_lookup_swapped = f.create_cluster_lookup_tool(
            [Element('Zr'), Element('Cu')])
        self.assertArrayAlmostEqual(sorted(nn_lookup._fit_X.tolist()),
                                    sorted(nn_lookup_swapped._fit_X.tolist()))

        # Make sure we had a cache hit
        self.assertEqual(1, f._create_cluster_lookup_tool.cache_info().misses)
        self.assertEqual(1, f._create_cluster_lookup_tool.cache_info().hits)

        # Change the tolerance, see if it changes the results properly
        f.threshold = 0.002
        nn_lookup = f.create_cluster_lookup_tool(
            [Element('Cu'), Element('Zr')])
        self.assertEqual(2, nn_lookup._fit_X.shape[0])
        ds, _ = nn_lookup.kneighbors(ef.featurize_many(
            [Composition('CuZr10'),
             Composition('Cu3Zr12')]),
                                     n_neighbors=1)
        self.assertArrayAlmostEqual([[0]] * 2, ds)

        # Make sure we had a cache miss
        self.assertEqual(2, f._create_cluster_lookup_tool.cache_info().misses)
        self.assertEqual(1, f._create_cluster_lookup_tool.cache_info().hits)

        # Compute the distances from Cu50Zr50
        mean_dists = f.compute_nearest_cluster_distance(Composition('CuZr'))
        self.assertArrayAlmostEqual([0.424264, 0.667602, 0.800561],
                                    mean_dists,
                                    decimal=6)

        # Compute the optimal APE for Cu50Zr50
        self.assertArrayAlmostEqual([0.000233857, 0.003508794],
                                    f.compute_simultaneous_packing_efficiency(
                                        Composition('Cu50Zr50')))

        # Test the dataframe calculator
        df = pd.DataFrame({'comp': [Composition('CuZr')]})
        df = f.featurize_dataframe(df, 'comp')

        self.assertEqual(6, len(df.columns))
        self.assertIn('dist from 5 clusters |APE| < 0.002', df.columns)

        self.assertAlmostEqual(0.003508794,
                               df['mean abs simul. packing efficiency'][0])

        # Make sure it works with composition that do not match any efficient clusters
        feat = f.compute_nearest_cluster_distance(Composition('Al'))
        self.assertArrayAlmostEqual([1] * 3, feat)
Пример #17
0
    def from_entries(cls, entry1, entry2, working_ion_entry):
        """
        Args:
            entry1: Entry corresponding to one of the entries in the voltage step.
            entry2: Entry corresponding to the other entry in the voltage step.
            working_ion_entry: A single ComputedEntry or PDEntry representing
                the element that carries charge across the battery, e.g. Li.
        """
        # initialize some internal variables
        working_element = working_ion_entry.composition.elements[0]

        entry_charge = entry1
        entry_discharge = entry2
        if entry_charge.composition.get_atomic_fraction(working_element) > entry2.composition.get_atomic_fraction(
            working_element
        ):
            (entry_charge, entry_discharge) = (entry_discharge, entry_charge)

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        ion_sym = working_element.symbol

        frame_charge_comp = Composition({el: comp_charge[el] for el in comp_charge if el.symbol != ion_sym})
        frame_discharge_comp = Composition({el: comp_discharge[el] for el in comp_discharge if el.symbol != ion_sym})

        # Data validation

        # check that the ion is just a single element
        if not working_ion_entry.composition.is_element:
            raise ValueError("VoltagePair: The working ion specified must be " "an element")

        # check that at least one of the entries contains the working element
        if (
            not comp_charge.get_atomic_fraction(working_element) > 0
            and not comp_discharge.get_atomic_fraction(working_element) > 0
        ):
            raise ValueError("VoltagePair: The working ion must be present in " "one of the entries")

        # check that the entries do not contain the same amount of the workin
        # element
        if comp_charge.get_atomic_fraction(working_element) == comp_discharge.get_atomic_fraction(working_element):
            raise ValueError("VoltagePair: The working ion atomic percentage " "cannot be the same in both the entries")

        # check that the frameworks of the entries are equivalent
        if not frame_charge_comp.reduced_formula == frame_discharge_comp.reduced_formula:
            raise ValueError("VoltagePair: the specified entries must have the" " same compositional framework")

        # Initialize normalization factors, charged and discharged entries

        valence_list = Element(ion_sym).oxidation_states
        working_ion_valence = abs(max(valence_list))

        (
            framework,
            norm_charge,
        ) = frame_charge_comp.get_reduced_composition_and_factor()
        norm_discharge = frame_discharge_comp.get_reduced_composition_and_factor()[1]

        # Initialize normalized properties
        if hasattr(entry_charge, "structure"):
            _vol_charge = entry_charge.structure.volume / norm_charge
        else:
            _vol_charge = entry_charge.data.get("volume")

        if hasattr(entry_discharge, "structure"):
            _vol_discharge = entry_discharge.structure.volume / norm_discharge
        else:
            _vol_discharge = entry_discharge.data.get("volume")

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        _mass_charge = comp_charge.weight / norm_charge
        _mass_discharge = comp_discharge.weight / norm_discharge

        _num_ions_transferred = (comp_discharge[working_element] / norm_discharge) - (
            comp_charge[working_element] / norm_charge
        )

        _voltage = (
            ((entry_charge.energy / norm_charge) - (entry_discharge.energy / norm_discharge)) / _num_ions_transferred
            + working_ion_entry.energy_per_atom
        ) / working_ion_valence
        _mAh = _num_ions_transferred * Charge(1, "e").to("C") * Time(1, "s").to("h") * N_A * 1000 * working_ion_valence

        _frac_charge = comp_charge.get_atomic_fraction(working_element)
        _frac_discharge = comp_discharge.get_atomic_fraction(working_element)

        vpair = cls(
            voltage=_voltage,
            mAh=_mAh,
            mass_charge=_mass_charge,
            mass_discharge=_mass_discharge,
            vol_charge=_vol_charge,
            vol_discharge=_vol_discharge,
            frac_charge=_frac_charge,
            frac_discharge=_frac_discharge,
            working_ion_entry=working_ion_entry,
            entry_charge=entry_charge,
            entry_discharge=entry_discharge,
            _framework_formula=framework.reduced_formula,
        )

        # Step 4: add (optional) hull and muO2 data
        vpair.decomp_e_charge = entry_charge.data.get("decomposition_energy", None)
        vpair.decomp_e_discharge = entry_discharge.data.get("decomposition_energy", None)

        vpair.muO2_charge = entry_charge.data.get("muO2", None)
        vpair.muO2_discharge = entry_discharge.data.get("muO2", None)

        return vpair
Пример #18
0
 def test_get_defectsite_coordinated_elements(self):
     struct_el = self._mgo_uc.composition.elements
     for i in range(self._mgo_interstitial.defectsite_count()):
         for el in self._mgo_interstitial.get_coordinated_elements(i):
             self.assertTrue(
                 Element(el) in struct_el, "Coordinated elements are wrong")
Пример #19
0
    def __str__(self):
        out = []
        site_descriptions = {}

        if self.pseudo is not None:
            site_descriptions = self.pseudo
        else:
            c = 1
            for site in self.structure:
                name = None
                for k, v in site_descriptions.items():
                    if site.properties == v:
                        name = k

                if name is None:
                    name = site.specie.symbol + str(c)
                    site_descriptions[name] = site.properties
                    c += 1

        def to_str(v):
            if isinstance(v, str):
                return "'%s'" % v
            if isinstance(v, float):
                return "%s" % str(v).replace("e", "d")
            if isinstance(v, bool):
                if v:
                    return ".TRUE."
                return ".FALSE."
            return v

        for k1 in ["control", "system", "electrons", "ions", "cell"]:
            v1 = self.sections[k1]
            out.append("&%s" % k1.upper())
            sub = []
            for k2 in sorted(v1.keys()):
                if isinstance(v1[k2], list):
                    n = 1
                    for l in v1[k2][: len(site_descriptions)]:
                        sub.append("  %s(%d) = %s" % (k2, n, to_str(v1[k2][n - 1])))
                        n += 1
                else:
                    sub.append("  %s = %s" % (k2, to_str(v1[k2])))
            if k1 == "system":
                if "ibrav" not in self.sections[k1]:
                    sub.append("  ibrav = 0")
                if "nat" not in self.sections[k1]:
                    sub.append("  nat = %d" % len(self.structure))
                if "ntyp" not in self.sections[k1]:
                    sub.append("  ntyp = %d" % len(site_descriptions))
            sub.append("/")
            out.append(",\n".join(sub))

        out.append("ATOMIC_SPECIES")
        for k, v in sorted(site_descriptions.items(), key=lambda i: i[0]):
            e = re.match(r"[A-Z][a-z]?", k).group(0)
            if self.pseudo is not None:
                p = v
            else:
                p = v["pseudo"]
            out.append("  %s  %.4f %s" % (k, Element(e).atomic_mass, p))

        out.append("ATOMIC_POSITIONS crystal")
        if self.pseudo is not None:
            for site in self.structure:
                out.append(
                    "  %s %.6f %.6f %.6f" % (site.specie.symbol, site.a, site.b, site.c)
                )
        else:
            for site in self.structure:
                name = None
                for k, v in sorted(site_descriptions.items(), key=lambda i: i[0]):
                    if v == site.properties:
                        name = k
                out.append("  %s %.6f %.6f %.6f" % (name, site.a, site.b, site.c))

        out.append("K_POINTS %s" % self.kpoints_mode)
        kpt_str = ["%s" % i for i in self.kpoints_grid]
        kpt_str.extend(["%s" % i for i in self.kpoints_shift])
        out.append("  %s" % " ".join(kpt_str))
        out.append("CELL_PARAMETERS angstrom")
        for vec in self.structure.lattice.matrix:
            out.append("  %f %f %f" % (vec[0], vec[1], vec[2]))
        return "\n".join(out)
Пример #20
0
    def from_string(data):
        """
        Reads the exciting input from a string
        """
       
        root=ET.fromstring(data)
        speciesnode=root.find('structure').iter('species')
        elements = []
        positions = []
        vectors=[]
        lockxyz=[]
        # get title
        title_in=str(root.find('title').text)
        # Read elements and coordinates
        for nodes in speciesnode:
            symbol = nodes.get('speciesfile').split('.')[0]
            if len(symbol.split('_'))==2:
              symbol=symbol.split('_')[0]
            if Element.is_valid_symbol(symbol):
                # Try to recognize the element symbol
                element = symbol
            else:
                raise NLValueError("Unknown element!")
            natoms = nodes.getiterator('atom')
            for atom in natoms:
                x, y, z = atom.get('coord').split()
                positions.append([float(x), float(y), float(z)])
                elements.append(element)
                # Obtain lockxyz for each atom
                if atom.get('lockxyz') is not None:
                    lxy=[]
                    for l in atom.get('lockxyz').split():
                        if l=='True' or l=='true':
                            lxyz.append(True)
                        else:
                            lxyz.append(False)
                    lockxyz.append(lxyz)
                else:
                    lockxyz.append([False, False, False])
        #check the atomic positions type
        if 'cartesian' in root.find('structure').attrib.keys():
          if root.find('structure').attrib['cartesian']:
            cartesian=True
            for i in range(len(positions)):
                for j in range(3):
                    positions[i][j]=positions[i][j]*ExcitingInput.bohr2ang
            print(positions)
        else:
          cartesian=False
        # get the scale attribute
        scale_in=root.find('structure').find('crystal').get('scale')
        if scale_in:
            scale=float(scale_in)*ExcitingInput.bohr2ang
        else:
            scale=ExcitingInput.bohr2ang
       # get the stretch attribute
        stretch_in=root.find('structure').find('crystal').get('stretch')
        if stretch_in:
          stretch=np.array([float(a) for a in stretch_in])
        else:
          stretch=np.array([1.0,1.0,1.0])
        # get basis vectors and scale them accordingly
        basisnode=root.find('structure').find('crystal').iter('basevect')
        for vect in basisnode:
          x, y, z=vect.text.split()
          vectors.append([float(x)*stretch[0]*scale,
                          float(y)*stretch[1]*scale,
                          float(z)*stretch[2]*scale])
        # create lattice and structure object
        lattice_in=Lattice(vectors)
        structure_in=Structure(lattice_in,elements,positions,coords_are_cartesian=cartesian)

        return ExcitingInput(structure_in, title_in, lockxyz)
Пример #21
0
 def test_init(self):
     d = {'Fe': 1, Element('Fe'): 1}
     self.assertRaises(ValueError, ChemicalPotential, d)
     for k in ChemicalPotential(Fe=1).keys():
         self.assertIsInstance(k, Element)
Пример #22
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 sites to speed up oxidation state guesses. If the
                composition cannot be reduced to this many sites a ValueError
                will be raised. Set to -1 to just reduce fully. If set to a
                number less than -1, the formula will be fully reduced but a
                ValueError will be thrown if the number of atoms in the reduced
                formula is greater than abs(max_sites).

        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 and max_sites < 0:
            comp = self.reduced_composition

            if max_sites < -1 and comp.num_atoms > abs(max_sites):
                raise ValueError("Composition {} cannot accommodate max_sites "
                                 "setting!".format(comp))

        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
Пример #23
0
def predict_k_g_list(material_id_list, api_key=API_KEY, query_engine=None):
    """
    Predict bulk (K) and shear (G) moduli for a list of materials.
    :param material_id_list: list of material-ID strings
    :param api_key: The API key used by pymatgen.matproj.rest.MPRester to connect to Materials Project
    :param query_engine: (Optional) QueryEngine object used to query materials instead of MPRester
 
    :return: (matid_list, predicted_k_list, predicted_g_list, caveats_list)
    Note that len(matid_list) may be less than len(material_id_list),
    if any requested material-IDs are not found.
    """

    if len(material_id_list) == 0 or not isinstance(material_id_list, list):
        return (None, None, None, None
                )  # material_id_list not properly specified

    lvpa_list = []
    cepa_list = []
    rowH1A_list = []
    rowHn3A_list = []
    xH4A_list = []
    xHn4A_list = []
    matid_list = []
    k_list = []
    g_list = []
    caveats_list = []
    aiab_problem_list = []

    # TODO: figure out if closing the query engine (using 'with' ctx mgr) is an issue
    # If it is a problem then try manually doing a session.close() for MPRester, but ignore for qe

    mpr = _get_mp_query(api_key, query_engine)
    for entry in mpr.query(criteria={"task_id": {
            "$in": material_id_list
    }},
                           properties=[
                               "material_id", "pretty_formula", "nsites",
                               "volume", "energy_per_atom", "is_hubbard"
                           ]):

        caveats_str = ''
        aiab_flag = False
        f_block_flag = False
        weight_list = []
        energy_list = []
        row_list = []
        x_list = []

        # Construct per-element lists for this material
        composition = Composition(str(entry["pretty_formula"]))
        for element_key, amount in composition.get_el_amt_dict().iteritems():
            element = Element(element_key)
            weight_list.append(composition.get_atomic_fraction(element))
            aiab_energy = get_element_aiab_energy(
                element_key)  # aiab = atom-in-a-box
            if aiab_energy is None:
                aiab_flag = True
                break
            energy_list.append(aiab_energy)
            if element.block == 'f':
                f_block_flag = True
            row_list.append(element.row)
            x_list.append(element.X)

        # On error, add material to aiab_problem_list and continue with next material
        if aiab_flag:
            aiab_problem_list.append(str(entry["material_id"]))
            continue

        # Check caveats
        if bool(entry["is_hubbard"]):
            if len(caveats_str) > 0: caveats_str += " "
            caveats_str += CAVEAT_HUBBARD
        if f_block_flag:
            if len(caveats_str) > 0: caveats_str += " "
            caveats_str += CAVEAT_F_BLOCK

        # Calculate intermediate weighted averages (WA) for this material
        ewa = np.average(energy_list,
                         weights=weight_list)  # atom-in-a-box energy WA

        print str(entry["material_id"])

        # Append descriptors for this material to descriptor lists
        lvpa_list.append(
            math.log10(float(entry["volume"]) / float(entry["nsites"])))
        cepa_list.append(float(entry["energy_per_atom"]) - ewa)
        rowH1A_list.append(holder_mean(row_list, 1.0, weights=weight_list))
        rowHn3A_list.append(holder_mean(row_list, -3.0, weights=weight_list))
        xH4A_list.append(holder_mean(x_list, 4.0, weights=weight_list))
        xHn4A_list.append(holder_mean(x_list, -4.0, weights=weight_list))
        matid_list.append(str(entry["material_id"]))
        caveats_list.append(caveats_str)

    if isinstance(mpr, MPRester):
        mpr.session.close()

    # Check that at least one valid material was provided
    num_predictions = len(matid_list)
    if num_predictions > 0:
        # Construct descriptor arrays
        if (len(lvpa_list) != num_predictions
                or len(cepa_list) != num_predictions
                or len(rowH1A_list) != num_predictions
                or len(rowHn3A_list) != num_predictions
                or len(xH4A_list) != num_predictions
                or len(xHn4A_list) != num_predictions):
            return (None, None, None, None)
        k_descriptors = np.ascontiguousarray(
            [lvpa_list, rowH1A_list, cepa_list, xHn4A_list], dtype=float)
        g_descriptors = np.ascontiguousarray(
            [cepa_list, lvpa_list, rowHn3A_list, xH4A_list], dtype=float)

        # Allocate prediction arrays
        k_predictions = np.empty(num_predictions)
        g_predictions = np.empty(num_predictions)

        # Make predictions
        k_filename = os.path.join(os.path.dirname(__file__), DATAFILE_K)
        g_filename = os.path.join(os.path.dirname(__file__), DATAFILE_G)
        gbml.core.predict(k_filename, num_predictions, k_descriptors,
                          k_predictions)
        gbml.core.predict(g_filename, num_predictions, g_descriptors,
                          g_predictions)

        k_list = np.power(10.0, k_predictions).tolist()
        g_list = np.power(10.0, g_predictions).tolist()

    # Append aiab problem cases
    for entry in aiab_problem_list:
        matid_list.append(entry)
        k_list.append(None)
        g_list.append(None)
        caveats_list.append(CAVEAT_AIAB)

    if len(matid_list) == 0:
        return (None, None, None, None)
    else:
        return (matid_list, k_list, g_list, caveats_list)
Пример #24
0
    def get_correction(self, entry) -> float:
        """
        :param entry: A ComputedEntry/ComputedStructureEntry
        :return: Correction.
        """
        comp = entry.composition
        if len(comp) == 1:  # Skip element entry
            return 0

        correction = 0
        # Check for sulfide corrections
        if Element("S") in comp:
            sf_type = "sulfide"
            if entry.data.get("sulfide_type"):
                sf_type = entry.data["sulfide_type"]
            elif hasattr(entry, "structure"):
                sf_type = sulfide_type(entry.structure)
            if sf_type in self.sulfide_correction:
                correction += self.sulfide_correction[sf_type] * comp["S"]

        # Check for oxide, peroxide, superoxide, and ozonide corrections.
        if Element("O") in comp:
            if self.correct_peroxide:
                if entry.data.get("oxide_type"):
                    if entry.data["oxide_type"] in self.oxide_correction:
                        ox_corr = self.oxide_correction[
                            entry.data["oxide_type"]]
                        correction += ox_corr * comp["O"]
                    if entry.data["oxide_type"] == "hydroxide":
                        ox_corr = self.oxide_correction["oxide"]
                        correction += ox_corr * comp["O"]

                elif hasattr(entry, "structure"):
                    ox_type, nbonds = oxide_type(entry.structure,
                                                 1.05,
                                                 return_nbonds=True)
                    if ox_type in self.oxide_correction:
                        correction += self.oxide_correction[ox_type] * \
                                      nbonds
                    elif ox_type == "hydroxide":
                        correction += self.oxide_correction["oxide"] * \
                                      comp["O"]
                else:
                    warnings.warn(
                        "No structure or oxide_type parameter present. Note "
                        "that peroxide/superoxide corrections are not as "
                        "reliable and relies only on detection of special"
                        "formulas, e.g., Li2O2.")
                    rform = entry.composition.reduced_formula
                    if rform in UCorrection.common_peroxides:
                        correction += self.oxide_correction["peroxide"] * \
                                      comp["O"]
                    elif rform in UCorrection.common_superoxides:
                        correction += self.oxide_correction["superoxide"] * \
                                      comp["O"]
                    elif rform in UCorrection.ozonides:
                        correction += self.oxide_correction["ozonide"] * \
                                      comp["O"]
                    elif Element("O") in comp.elements and len(comp.elements) \
                            > 1:
                        correction += self.oxide_correction['oxide'] * \
                                      comp["O"]
            else:
                correction += self.oxide_correction['oxide'] * comp["O"]

        return correction
Пример #25
0
    def test_get_data(self):
        props = [
            "energy", "energy_per_atom", "formation_energy_per_atom", "nsites",
            "unit_cell_formula", "pretty_formula", "is_hubbard", "elements",
            "nelements", "e_above_hull", "hubbards", "is_compatible",
            "task_ids", "density", "icsd_ids", "total_magnetization"
        ]
        # unicode literals have been reintroduced in py>3.2
        expected_vals = [
            -191.33812137, -6.833504334642858, -2.551358929370749, 28,
            {k: v
             for k, v in {
                 'P': 4,
                 'Fe': 4,
                 'O': 16,
                 'Li': 4
             }.items()}, "LiFePO4", True, ['Li', 'O', 'P', 'Fe'], 4, 0.0,
            {
                k: v
                for k, v in {
                    'Fe': 5.3,
                    'Li': 0.0,
                    'O': 0.0,
                    'P': 0.0
                }.items()
            }, True,
            [
                u'mp-601412', u'mp-19017', u'mp-796535', u'mp-797820',
                u'mp-540081', u'mp-797269'
            ], 3.4662026991351147,
            [
                159107, 154117, 160776, 99860, 181272, 166815, 260571, 92198,
                165000, 155580, 38209, 161479, 153699, 260569, 260570, 200155,
                260572, 181341, 181342, 72545, 56291, 97764, 162282, 155635
            ], 16.0002716
        ]

        for (i, prop) in enumerate(props):
            if prop not in [
                    'hubbards', 'unit_cell_formula', 'elements', 'icsd_ids',
                    'task_ids'
            ]:
                val = self.rester.get_data("mp-19017", prop=prop)[0][prop]
                self.assertAlmostEqual(expected_vals[i], val)
            elif prop in ["elements", "icsd_ids", "task_ids"]:
                self.assertEqual(
                    set(expected_vals[i]),
                    set(self.rester.get_data("mp-19017", prop=prop)[0][prop]))
            else:
                self.assertEqual(
                    expected_vals[i],
                    self.rester.get_data("mp-19017", prop=prop)[0][prop])

        props = ['structure', 'initial_structure', 'final_structure', 'entry']
        for prop in props:
            obj = self.rester.get_data("mp-19017", prop=prop)[0][prop]
            if prop.endswith("structure"):
                self.assertIsInstance(obj, Structure)
            elif prop == "entry":
                obj = self.rester.get_data("mp-19017", prop=prop)[0][prop]
                self.assertIsInstance(obj, ComputedEntry)

        #Test chemsys search
        data = self.rester.get_data('Fe-Li-O', prop='unit_cell_formula')
        self.assertTrue(len(data) > 1)
        elements = {Element("Li"), Element("Fe"), Element("O")}
        for d in data:
            self.assertTrue(
                set(Composition(
                    d['unit_cell_formula']).elements).issubset(elements))

        self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3",
                          "badmethod")
Пример #26
0
 def test_dict(self):
     fe = Element.Fe
     d = fe.as_dict()
     self.assertEqual(fe, Element.from_dict(d))
Пример #27
0
    def test_get_critical_compositions(self):
        c1 = Composition("Fe2O3")
        c2 = Composition("Li3FeO4")
        c3 = Composition("Li2O")

        comps = self.pd.get_critical_compositions(c1, c2)
        expected = [
            Composition("Fe2O3"),
            Composition("Li0.3243244Fe0.1621621O0.51351349") * 7.4,
            Composition("Li3FeO4"),
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.pd.get_critical_compositions(c1, c3)
        expected = [
            Composition("Fe2O3"),
            Composition("LiFeO2"),
            Composition("Li5FeO4") / 3,
            Composition("Li2O"),
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # Don't fail silently if input compositions aren't in phase diagram
        # Can be very confusing if you're working with a GrandPotentialPD
        self.assertRaises(
            ValueError,
            self.pd.get_critical_compositions,
            Composition("Xe"),
            Composition("Mn"),
        )

        # For the moment, should also fail even if compositions are in the gppd
        # because it isn't handled properly
        gppd = GrandPotentialPhaseDiagram(self.pd.all_entries, {"Xe": 1}, self.pd.elements + [Element("Xe")])
        self.assertRaises(
            ValueError,
            gppd.get_critical_compositions,
            Composition("Fe2O3"),
            Composition("Li3FeO4Xe"),
        )

        # check that the function still works though
        comps = gppd.get_critical_compositions(c1, c2)
        expected = [
            Composition("Fe2O3"),
            Composition("Li0.3243244Fe0.1621621O0.51351349") * 7.4,
            Composition("Li3FeO4"),
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # case where the endpoints are identical
        self.assertEqual(self.pd.get_critical_compositions(c1, c1 * 2), [c1, c1 * 2])
Пример #28
0
 def setUp(self):
     comp = Composition("LiFeO2")
     self.entry = PDEntry(comp, 53)
     self.gpentry = GrandPotPDEntry(self.entry, {Element('O'): 1.5})
Пример #29
0
 def setUp(self):
     self.entries = EntrySet.from_csv(str(module_dir / "pdentries_test.csv"))
     self.pd = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -5})
     self.pd6 = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -6})
Пример #30
0
 def amu_symbol(self):
     """Atomic mass units"""
     amu_list = self.reader.read_value("atomic_mass_units")
     atomic_numbers = self.reader.read_value("atomic_numbers")
     amu = {Element.from_Z(at).symbol: a for at, a in zip(atomic_numbers, amu_list)}
     return amu
Пример #31
0
def periodic_table_heatmap(elemental_data, cbar_label="",
                           show_plot=False, cmap="YlOrRd", blank_color="grey",
                           value_format=None, max_row=9):
    """
    A static method that generates a heat map overlapped on a periodic table.

    Args:
         elemental_data (dict): A dictionary with the element as a key and a
            value assigned to it, e.g. surface energy and frequency, etc.
            Elements missing in the elemental_data will be grey by default
            in the final table elemental_data={"Fe": 4.2, "O": 5.0}.
         cbar_label (string): Label of the colorbar. Default is "".
         figure_name (string): Name of the plot (absolute path) being saved
            if not None.
         show_plot (bool): Whether to show the heatmap. Default is False.
         value_format (str): Formatting string to show values. If None, no value
            is shown. Example: "%.4f" shows float to four decimals.
         cmap (string): Color scheme of the heatmap. Default is 'coolwarm'.
         blank_color (string): Color assigned for the missing elements in
            elemental_data. Default is "grey".
         max_row (integer): Maximum number of rows of the periodic table to be
            shown. Default is 9, which means the periodic table heat map covers
            the first 9 rows of elements.
    """

    # Convert elemental data in the form of numpy array for plotting.
    max_val = max(elemental_data.values())
    min_val = min(elemental_data.values())
    max_row = min(max_row, 9)

    if max_row <= 0:
        raise ValueError("The input argument 'max_row' must be positive!")

    value_table = np.empty((max_row, 18)) * np.nan
    blank_value = min_val - 0.01

    for el in Element:
        if el.row > max_row: continue
        value = elemental_data.get(el.symbol, blank_value)
        value_table[el.row - 1, el.group - 1] = value

    # Initialize the plt object
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    plt.gcf().set_size_inches(12, 8)

    # We set nan type values to masked values (ie blank spaces)
    data_mask = np.ma.masked_invalid(value_table.tolist())
    heatmap = ax.pcolor(data_mask, cmap=cmap, edgecolors='w', linewidths=1,
                        vmin=min_val-0.001, vmax=max_val+0.001)
    cbar = fig.colorbar(heatmap)

    # Grey out missing elements in input data
    cbar.cmap.set_under(blank_color)
    cbar.set_label(cbar_label, rotation=270, labelpad=15)
    cbar.ax.tick_params(labelsize=14)

    # Refine and make the table look nice
    ax.axis('off')
    ax.invert_yaxis()

    # Label each block with corresponding element and value
    for i, row in enumerate(value_table):
        for j, el in enumerate(row):
            if not np.isnan(el):
                symbol = Element.from_row_and_group(i+1, j+1).symbol
                plt.text(j + 0.5, i + 0.25, symbol,
                         horizontalalignment='center',
                         verticalalignment='center', fontsize=14)
                if el != blank_value and value_format is not None:
                    plt.text(j + 0.5, i + 0.5, value_format % el,
                             horizontalalignment='center',
                             verticalalignment='center', fontsize=10)

    plt.tight_layout()

    if show_plot:
        plt.show()

    return plt
Пример #32
0
    def parse_oxide(self):
        """
        Determines if an oxide is a peroxide/superoxide/ozonide/normal oxide.

        Returns:
            oxide_type (str): Type of oxide
            ozonide/peroxide/superoxide/hydroxide/None.
            nbonds (int): Number of peroxide/superoxide/hydroxide bonds in
            structure.
        """
        structure = self.structure
        relative_cutoff = self.relative_cutoff
        o_sites_frac_coords = []
        h_sites_frac_coords = []
        lattice = structure.lattice

        if isinstance(structure.composition.elements[0], Element):
            comp = structure.composition
        elif isinstance(structure.composition.elements[0], Species):
            elmap = collections.defaultdict(float)
            for site in structure:
                for species, occu in site.species.items():
                    elmap[species.element] += occu
            comp = Composition(elmap)
        if Element("O") not in comp or comp.is_element:
            return "None", 0

        for site in structure:
            syms = [sp.symbol for sp in site.species.keys()]
            if "O" in syms:
                o_sites_frac_coords.append(site.frac_coords)
            if "H" in syms:
                h_sites_frac_coords.append(site.frac_coords)

        if h_sites_frac_coords:
            dist_matrix = lattice.get_all_distances(o_sites_frac_coords,
                                                    h_sites_frac_coords)
            if np.any(dist_matrix < relative_cutoff * 0.93):
                return (
                    "hydroxide",
                    len(np.where(dist_matrix < relative_cutoff * 0.93)[0]) /
                    2.0,
                )
        dist_matrix = lattice.get_all_distances(o_sites_frac_coords,
                                                o_sites_frac_coords)
        np.fill_diagonal(dist_matrix, 1000)
        is_superoxide = False
        is_peroxide = False
        is_ozonide = False
        if np.any(dist_matrix < relative_cutoff * 1.35):
            bond_atoms = np.where(dist_matrix < relative_cutoff * 1.35)[0]
            is_superoxide = True
        elif np.any(dist_matrix < relative_cutoff * 1.49):
            is_peroxide = True
            bond_atoms = np.where(dist_matrix < relative_cutoff * 1.49)[0]
        if is_superoxide:
            if len(bond_atoms) > len(set(bond_atoms)):
                is_superoxide = False
                is_ozonide = True
        try:
            nbonds = len(set(bond_atoms))
        except UnboundLocalError:
            nbonds = 0.0
        if is_ozonide:
            str_oxide = "ozonide"
        elif is_superoxide:
            str_oxide = "superoxide"
        elif is_peroxide:
            str_oxide = "peroxide"
        else:
            str_oxide = "oxide"
        if str_oxide == "oxide":
            nbonds = comp["O"]
        return str_oxide, nbonds
Пример #33
0
 def test_print_periodic_table(self):
     Element.print_periodic_table()
def get_ranked_list_goldschmidt_halffill():
    #TODO: get the oxidation state right!!!
    
    filename = "goldschmidt_rank_halffill.p"
    if os.path.exists(filename):
        with open(filename) as f:
            return pickle.load(f)
    
    from ga_optimization_ternary.fitness_evaluators import FitnessEvaluator, eval_fitness_simple
    all_AB = FitnessEvaluator(eval_fitness_simple, 10)._reverse_dict.keys()
    print 'generating goldschmidt ranks...'
    cand_score = {}  # dictionary of cand_tuple:score. a high score is BAD
    
    for a in all_AB:
        for b in all_AB:
            for x in range(7):
                r_a = Element.from_Z(a).average_ionic_radius  # TODO: get the correct oxidation state!
                r_b = Element.from_Z(b).average_ionic_radius
                r_x = None
                if x == 0:
                    r_x = Element("O").ionic_radii[-2]
                elif x == 1:
                    r_x = Element("O").ionic_radii[-2] * 2/3 + Element("N").ionic_radii[-3] * 1/3
                elif x == 2:
                    r_x = Element("O").ionic_radii[-2] * 1/3 + Element("N").ionic_radii[-3] * 2/3
                elif x == 3:
                    r_x = Element("N").ionic_radii[-3]
                elif x == 4:
                    r_x = Element("O").ionic_radii[-2] * 2/3 + Element("F").ionic_radii[-1] * 1/3
                elif x == 5:
                    r_x = Element("O").ionic_radii[-2] * 1/3 + Element("F").ionic_radii[-1] * 1/3 + Element("N").ionic_radii[-3] * 1/3
                elif x == 6:
                    r_x = Element("O").ionic_radii[-2] * 2/3 + Element("S").ionic_radii[-2] * 1/3
                
                goldschmidt = (r_a + r_x)/(math.sqrt(2) *(r_b+r_x))
                score = abs(goldschmidt - 1)  # a high score is bad, like golf
                
                #nelectrons must be even
                ne_a = Element.from_Z(a).Z
                ne_b = Element.from_Z(b).Z
                ne_x = None
                if x == 0:
                    ne_x = Element("O").Z * 3
                elif x == 1:
                    ne_x = Element("O").Z * 2 + Element("N").Z
                elif x == 2:
                    ne_x = Element("O").Z + Element("N").Z * 2
                elif x == 3:
                    ne_x = Element("N").Z
                elif x == 4:
                    ne_x = Element("O").Z * 2 + Element("F").Z
                elif x == 5:
                    ne_x = Element("O").Z + Element("F").Z + Element("N").Z
                elif x == 6:
                    ne_x = Element("O").Z * 2 + Element("S").Z
                
                #modify the score based on charge-balance
                even_found = False
                el_a = Element.from_Z(a)
                el_b = Element.from_Z(b)
                val_x = 0
                if x == 0:
                    val_x = -2 * 3
                elif x == 1:
                    val_x = (-2 * 2) + (-3 * 1)
                elif x == 2:
                    val_x = (-2 * 1) + (-3 * 2)
                elif x == 3:
                    val_x = -3 * 3
                elif x == 4:
                    val_x = (-2 * 2) + (-1 * 1)
                elif x == 5:
                    val_x = (-2 * 1) + (-1 * 1) + (-3 * 1)
                elif x == 6:
                    val_x = (-2 * 2) + (-2 * 1)
                
                for a_oxi in el_a.oxidation_states:
                    for b_oxi in el_b.oxidation_states:
                        if (ne_a + ne_b + ne_x) % 2 == 0 and (a_oxi + b_oxi + val_x) == 0:
                            even_found = True
                
                if not even_found:
                    score = score + 100
                cand_score[(a, b, x)] = score
            
    results = sorted(cand_score, key=cand_score.get)
    with open(filename, "wb") as f:
        pickle.dump(results, f)
    return results
Пример #35
0
 def test_is(self):
     self.assertTrue(Element("Bi").is_post_transition_metal, True)
Пример #36
0
    def get_relaxed_cell(self, atom_dump_path, data_in_path, element_symbols):
        """
        Parses the relaxed cell from the dump.atom file.

        Returns the relaxed cell as a Cell object.

        Args:
            atom_dump_path: the path (as a string) to the dump.atom file

            in_data_path: the path (as a string) to the in.data file

            element_symbols: a tuple containing the set of chemical symbols of
                all the elements in the compositions space
        """

        # read the dump.atom file as a list of strings
        with open(atom_dump_path, 'r') as atom_dump:
            lines = atom_dump.readlines()

        # get the lattice vectors
        a_data = lines[5].split()
        b_data = lines[6].split()
        c_data = lines[7].split()

        # parse the tilt factors
        xy = float(a_data[2])
        xz = float(b_data[2])
        yz = float(c_data[2])

        # parse the bounds
        xlo_bound = float(a_data[0])
        xhi_bound = float(a_data[1])
        ylo_bound = float(b_data[0])
        yhi_bound = float(b_data[1])
        zlo_bound = float(c_data[0])
        zhi_bound = float(c_data[1])

        # compute xlo, xhi, ylo, yhi, zlo and zhi according to the conversion
        # given by LAMMPS
        # http://lammps.sandia.gov/doc/Section_howto.html#howto-12
        xlo = xlo_bound - min([0.0, xy, xz, xy + xz])
        xhi = xhi_bound - max([0.0, xy, xz, xy + xz])
        ylo = ylo_bound - min(0.0, yz)
        yhi = yhi_bound - max([0.0, yz])
        zlo = zlo_bound
        zhi = zhi_bound

        # construct a Lattice object from the lo's and hi's and tilts
        a = [xhi - xlo, 0.0, 0.0]
        b = [xy, yhi - ylo, 0.0]
        c = [xz, yz, zhi - zlo]
        relaxed_lattice = Lattice([a, b, c])

        # get the number of atoms
        num_atoms = int(lines[3])

        # get the atom types and their Cartesian coordinates
        types = []
        relaxed_cart_coords = []
        for i in range(num_atoms):
            atom_info = lines[9 + i].split()
            types.append(int(atom_info[1]))
            relaxed_cart_coords.append([
                float(atom_info[2]) - xlo,
                float(atom_info[3]) - ylo,
                float(atom_info[4]) - zlo
            ])

        # read the atom types and corresponding atomic masses from in.data
        with open(data_in_path, 'r') as data_in:
            lines = data_in.readlines()
        types_masses = {}
        for i in range(len(lines)):
            if 'Masses' in lines[i]:
                for j in range(len(element_symbols)):
                    types_masses[int(lines[i + j + 2].split()[0])] = float(
                        lines[i + j + 2].split()[1])

        # map the atom types to chemical symbols
        types_symbols = {}
        for symbol in element_symbols:
            for atom_type in types_masses:
                # round the atomic masses to one decimal point for comparison
                if format(float(Element(symbol).atomic_mass),
                          '.1f') == format(types_masses[atom_type], '.1f'):
                    types_symbols[atom_type] = symbol

        # make a list of chemical symbols (one for each site)
        relaxed_symbols = []
        for atom_type in types:
            relaxed_symbols.append(types_symbols[atom_type])

        return Cell(relaxed_lattice,
                    relaxed_symbols,
                    relaxed_cart_coords,
                    coords_are_cartesian=True)
Пример #37
0
 def test_print_periodic_table(self):
     Element.print_periodic_table()
Пример #38
0
    def _parse(self, filename):

        start_patt = re.compile(" \(Enter \S+l101\.exe\)")
        route_patt = re.compile(" #[pPnNtT]*.*")
        charge_mul_patt = re.compile("Charge\s+=\s*([-\\d]+)\s+" "Multiplicity\s+=\s*(\d+)")
        num_basis_func_patt = re.compile("([0-9]+)\s+basis functions")
        pcm_patt = re.compile("Polarizable Continuum Model")
        stat_type_patt = re.compile("imaginary frequencies")
        scf_patt = re.compile("E\(.*\)\s*=\s*([-\.\d]+)\s+")
        mp2_patt = re.compile("EUMP2\s*=\s*(.*)")
        oniom_patt = re.compile("ONIOM:\s+extrapolated energy\s*=\s*(.*)")
        termination_patt = re.compile("(Normal|Error) termination of Gaussian")
        std_orientation_patt = re.compile("Standard orientation")
        end_patt = re.compile("--+")
        orbital_patt = re.compile("Alpha\s*\S+\s*eigenvalues --(.*)")
        thermo_patt = re.compile("(Zero-point|Thermal) correction(.*)=" "\s+([\d\.-]+)")

        self.properly_terminated = False
        self.is_pcm = False
        self.stationary_type = "Minimum"
        self.structures = []
        self.corrections = {}
        self.energies = []

        coord_txt = []
        read_coord = 0
        orbitals_txt = []
        parse_stage = 0
        num_basis_found = False
        terminated = False

        with zopen(filename, "r") as f:
            for line in f:
                if parse_stage == 0:
                    if start_patt.search(line):
                        parse_stage = 1
                    elif route_patt.search(line):
                        self.route = {}
                        for tok in line.split():
                            sub_tok = tok.strip().split("=")
                            key = sub_tok[0].upper()
                            self.route[key] = sub_tok[1].upper() if len(sub_tok) > 1 else ""
                            m = re.match("(\w+)/([^/]+)", key)
                            if m:
                                self.functional = m.group(1)
                                self.basis_set = m.group(2)
                elif parse_stage == 1:
                    if charge_mul_patt.search(line):
                        m = charge_mul_patt.search(line)
                        self.charge = int(m.group(1))
                        self.spin_mult = int(m.group(2))
                        parse_stage = 2
                elif parse_stage == 2:

                    if self.is_pcm:
                        self._check_pcm(line)

                    if "FREQ" in self.route and thermo_patt.search(line):
                        m = thermo_patt.search(line)
                        if m.group(1) == "Zero-point":
                            self.corrections["Zero-point"] = float(m.group(3))
                        else:
                            key = m.group(2).strip(" to ")
                            self.corrections[key] = float(m.group(3))

                    if read_coord:
                        if not end_patt.search(line):
                            coord_txt.append(line)
                        else:
                            read_coord = (read_coord + 1) % 4
                            if not read_coord:
                                sp = []
                                coords = []
                                for l in coord_txt[2:]:
                                    toks = l.split()
                                    sp.append(Element.from_Z(int(toks[1])))
                                    coords.append(map(float, toks[3:6]))
                                self.structures.append(Molecule(sp, coords))
                    elif termination_patt.search(line):
                        m = termination_patt.search(line)
                        if m.group(1) == "Normal":
                            self.properly_terminated = True
                        terminated = True
                    elif (not num_basis_found) and num_basis_func_patt.search(line):
                        m = num_basis_func_patt.search(line)
                        self.num_basis_func = int(m.group(1))
                        num_basis_found = True
                    elif (not self.is_pcm) and pcm_patt.search(line):
                        self.is_pcm = True
                        self.pcm = {}
                    elif "FREQ" in self.route and "OPT" in self.route and stat_type_patt.search(line):
                        self.stationary_type = "Saddle"
                    elif mp2_patt.search(line):
                        m = mp2_patt.search(line)
                        self.energies.append(float(m.group(1).replace("D", "E")))
                    elif oniom_patt.search(line):
                        m = oniom_patt.matcher(line)
                        self.energies.append(float(m.group(1)))
                    elif scf_patt.search(line):
                        m = scf_patt.search(line)
                        self.energies.append(float(m.group(1)))
                    elif std_orientation_patt.search(line):
                        coord_txt = []
                        read_coord = 1
                    elif orbital_patt.search(line):
                        orbitals_txt.append(line)
        if not terminated:
            raise IOError("Bad Gaussian output file.")
Пример #39
0
    def test_attributes(self):
        is_true = {
            ("Xe", "Kr"): "is_noble_gas",
            ("Fe", "Ni"): "is_transition_metal",
            ("Li", "Cs"): "is_alkali",
            ("Ca", "Mg"): "is_alkaline",
            ("F", "Br", "I"): "is_halogen",
            ("La",): "is_lanthanoid",
            ("U", "Pu"): "is_actinoid",
            ("Si", "Ge"): "is_metalloid",
            ("O", "Te"): "is_chalcogen",
        }

        for k, v in is_true.items():
            for sym in k:
                self.assertTrue(getattr(Element(sym), v), sym + " is false")

        keys = [
            "mendeleev_no",
            "atomic_mass",
            "electronic_structure",
            "atomic_radius",
            "min_oxidation_state",
            "max_oxidation_state",
            "electrical_resistivity",
            "velocity_of_sound",
            "reflectivity",
            "refractive_index",
            "poissons_ratio",
            "molar_volume",
            "thermal_conductivity",
            "melting_point",
            "boiling_point",
            "liquid_range",
            "critical_temperature",
            "superconduction_temperature",
            "bulk_modulus",
            "youngs_modulus",
            "brinell_hardness",
            "rigidity_modulus",
            "mineral_hardness",
            "vickers_hardness",
            "density_of_solid",
            "atomic_orbitals",
            "coefficient_of_linear_thermal_expansion",
            "oxidation_states",
            "common_oxidation_states",
            "average_ionic_radius",
            "average_cationic_radius",
            "average_anionic_radius",
            "ionic_radii",
            "long_name",
            "metallic_radius",
            "iupac_ordering",
        ]

        # Test all elements up to Uranium
        for i in range(1, 104):
            el = Element.from_Z(i)
            d = el.data
            for k in keys:
                k_str = k.capitalize().replace("_", " ")
                if k_str in d and (not str(d[k_str]).startswith("no data")):
                    self.assertIsNotNone(getattr(el, k))
                elif k == "long_name":
                    self.assertEqual(getattr(el, "long_name"), d["Name"])
                elif k == "iupac_ordering":
                    self.assertTrue("IUPAC ordering" in d)
                    self.assertIsNotNone(getattr(el, k))
            el = Element.from_Z(i)
            if len(el.oxidation_states) > 0:
                self.assertEqual(max(el.oxidation_states), el.max_oxidation_state)
                self.assertEqual(min(el.oxidation_states), el.min_oxidation_state)

            if el.symbol not in ["He", "Ne", "Ar"]:
                self.assertTrue(el.X > 0, "No electroneg for %s" % el)

        self.assertRaises(ValueError, Element.from_Z, 1000)
Пример #40
0
    def from_string(data):
        """
        Reads the exciting input from a string
        """
       
        root=ET.fromstring(data)
        speciesnode=root.find('structure').iter('species')
        elements = []
        positions = []
        vectors=[]
        lockxyz=[]
        # get title
        title_in=str(root.find('title').text)
        # Read elements and coordinates
        for nodes in speciesnode:
            symbol = nodes.get('speciesfile').split('.')[0]
            if len(symbol.split('_'))==2:
              symbol=symbol.split('_')[0]
            if Element.is_valid_symbol(symbol):
                # Try to recognize the element symbol
                element = symbol
            else:
                raise NLValueError("Unknown element!")
            natoms = nodes.getiterator('atom')
            for atom in natoms:
                x, y, z = atom.get('coord').split()
                positions.append([float(x), float(y), float(z)])
                elements.append(element)
                # Obtain lockxyz for each atom
                if atom.get('lockxyz') is not None:
                    lxy=[]
                    for l in atom.get('lockxyz').split():
                        if l=='True' or l=='true':
                            lxyz.append(True)
                        else:
                            lxyz.append(False)
                    lockxyz.append(lxyz)
                else:
                    lockxyz.append([False, False, False])
        #check the atomic positions type
        if 'cartesian' in root.find('structure').attrib.keys():
          if root.find('structure').attrib['cartesian']:
            cartesian=True
            for i in range(len(positions)):
                for j in range(3):
                    positions[i][j]=positions[i][j]*ExcitingInput.bohr2ang
            print(positions)
        else:
          cartesian=False
        # get the scale attribute
        scale_in=root.find('structure').find('crystal').get('scale')
        if scale_in:
            scale=float(scale_in)*ExcitingInput.bohr2ang
        else:
            scale=ExcitingInput.bohr2ang
       # get the stretch attribute
        stretch_in=root.find('structure').find('crystal').get('stretch')
        if stretch_in:
          stretch=np.array([float(a) for a in stretch_in])
        else:
          stretch=np.array([1.0,1.0,1.0])
        # get basis vectors and scale them accordingly
        basisnode=root.find('structure').find('crystal').iter('basevect')
        for vect in basisnode:
          x, y, z=vect.text.split()
          vectors.append([float(x)*stretch[0]*scale,
                          float(y)*stretch[1]*scale,
                          float(z)*stretch[2]*scale])
        # create lattice and structure object
        lattice_in=Lattice(vectors)
        structure_in=Structure(lattice_in,elements,positions,coords_are_cartesian=cartesian)

        return ExcitingInput(structure_in, title_in, lockxyz)
Пример #41
0
def get_structure_type(structure, write_poscar_from_cluster=False):
    """
    This is a topology-scaling algorithm used to describe the
    periodicity of bonded clusters in a bulk structure.

    Args:
        structure (structure): Pymatgen structure object to classify.
        write_poscar_from_cluster (bool): Set to True to write a
            POSCAR from the sites in the cluster.

    Returns:
        string. 'molecular' (0D), 'chain', 'layered', 'heterogeneous'
            (intercalated 3D), or 'conventional' (3D)
    """

    # The conventional standard structure is much easier to work
    # with.

    structure = SpacegroupAnalyzer(
        structure).get_conventional_standard_structure()

    # Noble gases don't have well-defined bonding radii.
    if not len([
            e for e in structure.composition
            if e.symbol in ['He', 'Ne', 'Ar', 'Kr', 'Xe']
    ]) == 0:
        type = 'noble gas'
    else:
        if len(structure.sites) < 45:
            structure.make_supercell(2)

        # Create a dict of sites as keys and lists of their
        # bonded neighbors as values.
        sites = structure.sites
        bonds = {}
        for site in sites:
            bonds[site] = []

        for i in range(len(sites)):
            site_1 = sites[i]
            for site_2 in sites[i + 1:]:
                if (site_1.distance(site_2) < float(
                        Element(site_1.specie).atomic_radius +
                        Element(site_2.specie).atomic_radius) * 1.1):
                    bonds[site_1].append(site_2)
                    bonds[site_2].append(site_1)

        # Assimilate all bonded atoms in a cluster; terminate
        # when it stops growing.
        cluster_terminated = False
        while not cluster_terminated:
            original_cluster_size = len(bonds[sites[0]])
            for site in bonds[sites[0]]:
                bonds[sites[0]] += [
                    s for s in bonds[site] if s not in bonds[sites[0]]
                ]
            if len(bonds[sites[0]]) == original_cluster_size:
                cluster_terminated = True

        original_cluster = bonds[sites[0]]

        if len(bonds[sites[0]]) == 0:  # i.e. the cluster is a single atom.
            type = 'molecular'
        elif len(bonds[sites[0]]) == len(sites):  # i.e. all atoms are bonded.
            type = 'conventional'
        else:
            # If the cluster's composition is not equal to the
            # structure's overall composition, it is a heterogeneous
            # compound.
            cluster_composition_dict = {}
            for site in bonds[sites[0]]:
                if Element(site.specie) in cluster_composition_dict:
                    cluster_composition_dict[Element(site.specie)] += 1
                else:
                    cluster_composition_dict[Element(site.specie)] = 1
            uniform = True
            if len(cluster_composition_dict):
                cmp = Composition.from_dict(cluster_composition_dict)
                if cmp.reduced_formula != structure.composition.reduced_formula:
                    uniform = False
            if not uniform:
                type = 'heterogeneous'
            else:
                # Make a 2x2x2 supercell and recalculate the
                # cluster's new size. If the new cluster size is
                # the same as the old size, it is a non-periodic
                # molecule. If it is 2x as big, it's a 1D chain.
                # If it's 4x as big, it is a layered material.
                old_cluster_size = len(bonds[sites[0]])
                structure.make_supercell(2)
                sites = structure.sites
                bonds = {}
                for site in sites:
                    bonds[site] = []

                for i in range(len(sites)):
                    site_1 = sites[i]
                    for site_2 in sites[i + 1:]:
                        if (site_1.distance(site_2) < float(
                                Element(site_1.specie).atomic_radius +
                                Element(site_2.specie).atomic_radius) * 1.1):
                            bonds[site_1].append(site_2)
                            bonds[site_2].append(site_1)

                cluster_terminated = False
                while not cluster_terminated:
                    original_cluster_size = len(bonds[sites[0]])
                    for site in bonds[sites[0]]:
                        bonds[sites[0]] += [
                            s for s in bonds[site] if s not in bonds[sites[0]]
                        ]
                    if len(bonds[sites[0]]) == original_cluster_size:
                        cluster_terminated = True

                if len(bonds[sites[0]]) != 4 * old_cluster_size:
                    type = 'molecular'
                else:
                    type = 'layered'

    if write_poscar_from_cluster:
        Structure.from_sites(original_cluster).to('POSCAR', 'POSCAR')

    return type
Пример #42
0
def eval_vasp_xml(file="vasprun.xml", recip=False, norm_fermi=True, print_out=False):
    dft = pymatgen.io.vasp.outputs.Vasprun(file, parse_projected_eigen=False)
    orbital_energy = pd.read_csv("element_orbital_energy.csv").set_index("element")

    lattice = jnp.asarray(dft.get_trajectory().as_dict()['lattice']).squeeze()
    lattice_normed = lattice / jnp.linalg.norm(lattice, axis=1, keepdims=True)
    lattice_recip = jnp.asarray(Lattice(lattice).reciprocal_lattice.matrix)  # wrong!

    positions_base = dft.get_trajectory().as_dict()['base_positions']
    positions = jnp.dot(positions_base, lattice)

    k_points = jnp.asarray(dft.actual_kpoints)

    weights = jnp.asarray(dft.actual_kpoints_weights)  # how to use ?

    species_dict = {}
    species_arr = np.asarray(dft.atomic_symbols)
    count = 0
    print(species_arr)
    for key in dict.fromkeys(set(dft.atomic_symbols), {}):
        species_dict["species_" + Element(key).long_name] = {"symbol": key,
                                                             "number": count,
                                                             "Es": orbital_energy.loc["C", "E_s"],
                                                             "Ep": orbital_energy.loc["C", "E_p"],
                                                             "Ed": orbital_energy.loc["C", "E_d"],
                                                             }
        species_arr[species_arr == key] = count  # cycles through elements but returns correct one anyway
        count += 1
    species_arr = jnp.asarray(species_arr.astype(int))

    for key in dft.eigenvalues.keys():
        key_last = key
    true_inp = np.zeros(
        (dft.eigenvalues[key_last][:, :, 0].shape[0], dft.eigenvalues[key_last][:, :, 0].shape[1], len(dft.eigenvalues.keys())))
    count = 0
    if len(dft.eigenvalues.keys()) != 1:
        print("only one spin direction supported but", len(dft.eigenvalues.keys()), "where given")
    for key in dft.eigenvalues.keys():  # OrderedDictionary might be nice
        true_inp[:, :, count] = dft.eigenvalues[key][:, :, 0]  # what is [:, :, 0] ???????????????????
        occupied = np.max(jnp.nonzero(dft.eigenvalues[key][:, :, 1])[1]) + 1
        fermi = find_fermi(true_inp, occupied)
        count += 1
    if norm_fermi:
        true_inp -= fermi
        print("E fermi calculated normed", find_fermi(true_inp, occupied, plot=False))
    if print_out:
        print("Lattice", type(lattice), lattice.shape, "\n", lattice)
        print("Lattice Normed", type(lattice_normed), lattice_normed.shape, lattice_normed)
        print("Lattice recip", type(lattice_recip), lattice_recip.shape, "\n", lattice_recip)
        print("Positions", type(positions_base), positions_base.shape, positions_base)
        print("Positions dot", type(positions), positions.shape, "\n", positions)
        print("kpts", k_points.shape, k_points)
        print("weights", weights.shape, weights)
        print("True shape", true_inp.shape, true_inp)
        print("species", species_arr.shape, species_arr, "\n", species_dict)
        # print("true", dft.eigenvalues[:].shape, "\n", dft.eigenvalues[dft.eigenvalues.keys()[0]][0, :, 0], "\n",
        #       dft.eigenvalues[dft.eigenvalues.keys()[0]][0, :, 1])
        print("E fermi vasp", dft.efermi)
        print("Highest occupied", occupied)
        print("E fermi calculated", fermi)
    if recip:
        return k_points, weights, lattice_recip, positions, species_arr, species_dict, true_inp, occupied
    else:
        return k_points, weights, lattice, positions, species_arr, species_dict, true_inp, occupied
Пример #43
0
 def test_is_metal(self):
     for metal in ["Fe", "Eu", "Li", "Ca", "In"]:
         self.assertTrue(Element(metal).is_metal)
     for non_metal in ["Ge", "Si", "O", "He"]:
         self.assertFalse(Element(non_metal).is_metal)
Пример #44
0
 def validate(self, value):
     if not all(Element.is_valid_symbol(k) for k in value.keys()):
         self.error('Keys should be element symbols')
     if not all(isinstance(v, (float, int)) for v in value.values()):
         self.error('Values should be numbers')
     super(DictField, self).validate(value)
Пример #45
0
 def test_getmu_range_stability_phase(self):
     results = self.pd.get_chempot_range_stability_phase(
         Composition("LiFeO2"), Element("O"))
     self.assertAlmostEqual(results[Element("O")][1], -4.4501812249999997)
     self.assertAlmostEqual(results[Element("Fe")][0], -6.5961470999999996)
     self.assertAlmostEqual(results[Element("Li")][0], -3.6250022625000007)
Пример #46
0
    def write_xdatcar(self, filepath="XDATCAR", groupby_type=True, overwrite=False, to_unit_cell=False):
        """
        Write Xdatcar file with unit cell and atomic positions to file ``filepath``.

        Args:
            filepath: Xdatcar filename. If None, a temporary file is created.
            groupby_type: If True, atoms are grouped by type. Note that this option
                may change the order of the atoms. This option is needed because
                there are post-processing tools (e.g. ovito) that do not work as expected
                if the atoms in the structure are not grouped by type.
            overwrite: raise RuntimeError, if False and filepath exists.
            to_unit_cell (bool): Whether to translate sites into the unit cell.

        Return:
            path to Xdatcar file.
        """
        if filepath is not None and os.path.exists(filepath) and not overwrite:
            raise RuntimeError("Cannot overwrite pre-existing file `%s`" % filepath)
        if filepath is None:
            import tempfile
            fd, filepath = tempfile.mkstemp(text=True, suffix="_XDATCAR")

        # int typat[natom], double znucl[npsp]
        # NB: typat is double in the HIST.nc file
        typat = self.reader.read_value("typat").astype(int)
        znucl = self.reader.read_value("znucl")
        ntypat = self.reader.read_dimvalue("ntypat")
        num_pseudos = self.reader.read_dimvalue("npsp")
        if num_pseudos != ntypat:
            raise NotImplementedError("Alchemical mixing is not supported, num_pseudos != ntypat")
        #print("znucl:", znucl, "\ntypat:", typat)

        symb2pos = OrderedDict()
        symbols_atom = []
        for iatom, itype in enumerate(typat):
            itype = itype - 1
            symbol = Element.from_Z(int(znucl[itype])).symbol
            if symbol not in symb2pos: symb2pos[symbol] = []
            symb2pos[symbol].append(iatom)
            symbols_atom.append(symbol)

        if not groupby_type:
            group_ids = np.arange(self.reader.natom)
        else:
            group_ids = []
            for pos_list in symb2pos.values():
                group_ids.extend(pos_list)
            group_ids = np.array(group_ids, dtype=np.int)

        comment = " %s\n" % self.initial_structure.formula
        with open(filepath, "wt") as fh:
            # comment line  + scaling factor set to 1.0
            fh.write(comment)
            fh.write("1.0\n")
            for vec in self.initial_structure.lattice.matrix:
                fh.write("%.12f %.12f %.12f\n" % (vec[0], vec[1], vec[2]))
            if not groupby_type:
                fh.write(" ".join(symbols_atom) + "\n")
                fh.write("1 " * len(symbols_atom) + "\n")
            else:
                fh.write(" ".join(symb2pos.keys()) + "\n")
                fh.write(" ".join(str(len(p)) for p in symb2pos.values()) + "\n")

            # Write atomic positions in reduced coordinates.
            xred_list = self.reader.read_value("xred")
            if to_unit_cell:
                xred_list = xred_list % 1

            for step in range(self.num_steps):
                fh.write("Direct configuration= %d\n" % (step + 1))
                frac_coords = xred_list[step, group_ids]
                for fs in frac_coords:
                    fh.write("%.12f %.12f %.12f\n" % (fs[0], fs[1], fs[2]))

        return filepath
Пример #47
0
 def is_element(self, element):
     try:
         Element(element)
         return True
     except:
         return False
Пример #48
0
    def __init__(self, entry1, entry2, working_ion_entry):
        # initialize some internal variables
        working_element = working_ion_entry.composition.elements[0]

        entry_charge = entry1
        entry_discharge = entry2
        if entry_charge.composition.get_atomic_fraction(working_element) \
                > entry2.composition.get_atomic_fraction(working_element):
            (entry_charge, entry_discharge) = (entry_discharge, entry_charge)

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        ion_sym = working_element.symbol

        frame_charge_comp = Composition({
            el: comp_charge[el]
            for el in comp_charge if el.symbol != ion_sym
        })
        frame_discharge_comp = Composition({
            el: comp_discharge[el]
            for el in comp_discharge if el.symbol != ion_sym
        })

        # Data validation

        # check that the ion is just a single element
        if not working_ion_entry.composition.is_element:
            raise ValueError("VoltagePair: The working ion specified must be "
                             "an element")

        # check that at least one of the entries contains the working element
        if not comp_charge.get_atomic_fraction(working_element) > 0 and \
                not comp_discharge.get_atomic_fraction(working_element) > 0:
            raise ValueError("VoltagePair: The working ion must be present in "
                             "one of the entries")

        # check that the entries do not contain the same amount of the workin
        # element
        if comp_charge.get_atomic_fraction(working_element) == \
                comp_discharge.get_atomic_fraction(working_element):
            raise ValueError("VoltagePair: The working ion atomic percentage "
                             "cannot be the same in both the entries")

        # check that the frameworks of the entries are equivalent
        if not frame_charge_comp.reduced_formula == \
                frame_discharge_comp.reduced_formula:
            raise ValueError("VoltagePair: the specified entries must have the"
                             " same compositional framework")

        # Initialize normalization factors, charged and discharged entries

        valence_list = Element(ion_sym).oxidation_states
        working_ion_valence = abs(max(valence_list))

        (self.framework,
         norm_charge) = frame_charge_comp.get_reduced_composition_and_factor()
        norm_discharge = \
            frame_discharge_comp.get_reduced_composition_and_factor()[1]

        self._working_ion_entry = working_ion_entry

        # Initialize normalized properties
        self._vol_charge = entry_charge.structure.volume / norm_charge
        self._vol_discharge = entry_discharge.structure.volume / norm_discharge

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        self._mass_charge = comp_charge.weight / norm_charge
        self._mass_discharge = comp_discharge.weight / norm_discharge

        self._num_ions_transferred = \
            (comp_discharge[working_element] / norm_discharge) \
            - (comp_charge[working_element] / norm_charge)

        self._voltage = \
            (((entry_charge.energy / norm_charge) -
              (entry_discharge.energy / norm_discharge)) / \
             self._num_ions_transferred + working_ion_entry.energy_per_atom) / working_ion_valence
        self._mAh = self._num_ions_transferred * Charge(1, "e").to("C") * \
                    Time(1, "s").to("h") * N_A * 1000 * working_ion_valence

        # Step 4: add (optional) hull and muO2 data
        self.decomp_e_charge = \
            entry_charge.data.get("decomposition_energy", None)
        self.decomp_e_discharge = \
            entry_discharge.data.get("decomposition_energy", None)

        self.muO2_charge = entry_charge.data.get("muO2", None)
        self.muO2_discharge = entry_discharge.data.get("muO2", None)

        self.entry_charge = entry_charge
        self.entry_discharge = entry_discharge
        self.normalization_charge = norm_charge
        self.normalization_discharge = norm_discharge
        self._frac_charge = comp_charge.get_atomic_fraction(working_element)
        self._frac_discharge = \
            comp_discharge.get_atomic_fraction(working_element)
Пример #49
0
def periodic_table_heatmap(
    elemental_data,
    cbar_label="",
    cbar_label_size=14,
    show_plot=False,
    cmap="YlOrRd",
    cmap_range=None,
    blank_color="grey",
    value_format=None,
    max_row=9,
):
    """
    A static method that generates a heat map overlayed on a periodic table.

    Args:
         elemental_data (dict): A dictionary with the element as a key and a
            value assigned to it, e.g. surface energy and frequency, etc.
            Elements missing in the elemental_data will be grey by default
            in the final table elemental_data={"Fe": 4.2, "O": 5.0}.
         cbar_label (string): Label of the colorbar. Default is "".
         cbar_label_size (float): Font size for the colorbar label. Default is 14.
         cmap_range (tuple): Minimum and maximum value of the colormap scale.
            If None, the colormap will autotmatically scale to the range of the
            data.
         show_plot (bool): Whether to show the heatmap. Default is False.
         value_format (str): Formatting string to show values. If None, no value
            is shown. Example: "%.4f" shows float to four decimals.
         cmap (string): Color scheme of the heatmap. Default is 'YlOrRd'.
            Refer to the matplotlib documentation for other options.
         blank_color (string): Color assigned for the missing elements in
            elemental_data. Default is "grey".
         max_row (integer): Maximum number of rows of the periodic table to be
            shown. Default is 9, which means the periodic table heat map covers
            the first 9 rows of elements.
    """

    # Convert primitive_elemental data in the form of numpy array for plotting.
    if cmap_range is not None:
        max_val = cmap_range[1]
        min_val = cmap_range[0]
    else:
        max_val = max(elemental_data.values())
        min_val = min(elemental_data.values())

    max_row = min(max_row, 9)

    if max_row <= 0:
        raise ValueError("The input argument 'max_row' must be positive!")

    value_table = np.empty((max_row, 18)) * np.nan
    blank_value = min_val - 0.01

    for el in Element:
        if el.row > max_row:
            continue
        value = elemental_data.get(el.symbol, blank_value)
        value_table[el.row - 1, el.group - 1] = value

    # Initialize the plt object
    import matplotlib.pyplot as plt

    fig, ax = plt.subplots()
    plt.gcf().set_size_inches(12, 8)

    # We set nan type values to masked values (ie blank spaces)
    data_mask = np.ma.masked_invalid(value_table.tolist())
    heatmap = ax.pcolor(
        data_mask,
        cmap=cmap,
        edgecolors="w",
        linewidths=1,
        vmin=min_val - 0.001,
        vmax=max_val + 0.001,
    )
    cbar = fig.colorbar(heatmap)

    # Grey out missing elements in input data
    cbar.cmap.set_under(blank_color)

    # Set the colorbar label and tick marks
    cbar.set_label(cbar_label, rotation=270, labelpad=25, size=cbar_label_size)
    cbar.ax.tick_params(labelsize=cbar_label_size)

    # Refine and make the table look nice
    ax.axis("off")
    ax.invert_yaxis()

    # Label each block with corresponding element and value
    for i, row in enumerate(value_table):
        for j, el in enumerate(row):
            if not np.isnan(el):
                symbol = Element.from_row_and_group(i + 1, j + 1).symbol
                plt.text(
                    j + 0.5,
                    i + 0.25,
                    symbol,
                    horizontalalignment="center",
                    verticalalignment="center",
                    fontsize=14,
                )
                if el != blank_value and value_format is not None:
                    plt.text(
                        j + 0.5,
                        i + 0.5,
                        value_format % el,
                        horizontalalignment="center",
                        verticalalignment="center",
                        fontsize=10,
                    )

    plt.tight_layout()

    if show_plot:
        plt.show()

    return plt
Пример #50
0
 def test_dict(self):
     fe = Element("Fe")
     d = fe.as_dict()
     self.assertEqual(fe, Element.from_dict(d))
Пример #51
0
def van_arkel_triangle(list_of_materials, annotate=True):
    """
    A static method that generates a binary van Arkel-Ketelaar triangle to
        quantify the ionic, metallic and covalent character of a compound
        by plotting the electronegativity difference (y) vs average (x).
        See:
            A.E. van Arkel, Molecules and Crystals in Inorganic Chemistry,
                Interscience, New York (1956)
        and
            J.A.A Ketelaar, Chemical Constitution (2nd edn.), An Introduction
                to the Theory of the Chemical Bond, Elsevier, New York (1958)

    Args:
         list_of_materials (list): A list of computed entries of binary
            materials or a list of lists containing two elements (str).
         annotate (bool): Whether or not to lable the points on the
            triangle with reduced formula (if list of entries) or pair
            of elements (if list of list of str).
    """

    # F-Fr has the largest X difference. We set this
    # as our top corner of the triangle (most ionic)
    pt1 = np.array([(Element("F").X + Element("Fr").X) / 2, abs(Element("F").X - Element("Fr").X)])
    # Cs-Fr has the lowest average X. We set this as our
    # bottom left corner of the triangle (most metallic)
    pt2 = np.array(
        [
            (Element("Cs").X + Element("Fr").X) / 2,
            abs(Element("Cs").X - Element("Fr").X),
        ]
    )
    # O-F has the highest average X. We set this as our
    # bottom right corner of the triangle (most covalent)
    pt3 = np.array([(Element("O").X + Element("F").X) / 2, abs(Element("O").X - Element("F").X)])

    # get the parameters for the lines of the triangle
    d = np.array(pt1) - np.array(pt2)
    slope1 = d[1] / d[0]
    b1 = pt1[1] - slope1 * pt1[0]
    d = pt3 - pt1
    slope2 = d[1] / d[0]
    b2 = pt3[1] - slope2 * pt3[0]

    # Initialize the plt object
    import matplotlib.pyplot as plt

    # set labels and appropriate limits for plot
    plt.xlim(pt2[0] - 0.45, -b2 / slope2 + 0.45)
    plt.ylim(-0.45, pt1[1] + 0.45)
    plt.annotate("Ionic", xy=[pt1[0] - 0.3, pt1[1] + 0.05], fontsize=20)
    plt.annotate("Covalent", xy=[-b2 / slope2 - 0.65, -0.4], fontsize=20)
    plt.annotate("Metallic", xy=[pt2[0] - 0.4, -0.4], fontsize=20)
    plt.xlabel(r"$\frac{\chi_{A}+\chi_{B}}{2}$", fontsize=25)
    plt.ylabel(r"$|\chi_{A}-\chi_{B}|$", fontsize=25)

    # Set the lines of the triangle
    chi_list = [el.X for el in Element]
    plt.plot(
        [min(chi_list), pt1[0]],
        [slope1 * min(chi_list) + b1, pt1[1]],
        "k-",
        linewidth=3,
    )
    plt.plot([pt1[0], -b2 / slope2], [pt1[1], 0], "k-", linewidth=3)
    plt.plot([min(chi_list), -b2 / slope2], [0, 0], "k-", linewidth=3)
    plt.xticks(fontsize=15)
    plt.yticks(fontsize=15)

    # Shade with appropriate colors corresponding to ionic, metallci and covalent
    ax = plt.gca()
    # ionic filling
    ax.fill_between(
        [min(chi_list), pt1[0]],
        [slope1 * min(chi_list) + b1, pt1[1]],
        facecolor=[1, 1, 0],
        zorder=-5,
        edgecolor=[1, 1, 0],
    )
    ax.fill_between(
        [pt1[0], -b2 / slope2],
        [pt1[1], slope2 * min(chi_list) - b1],
        facecolor=[1, 1, 0],
        zorder=-5,
        edgecolor=[1, 1, 0],
    )
    # metal filling
    XPt = Element("Pt").X
    ax.fill_between(
        [min(chi_list), (XPt + min(chi_list)) / 2],
        [0, slope1 * (XPt + min(chi_list)) / 2 + b1],
        facecolor=[1, 0, 0],
        zorder=-3,
        alpha=0.8,
    )
    ax.fill_between(
        [(XPt + min(chi_list)) / 2, XPt],
        [slope1 * ((XPt + min(chi_list)) / 2) + b1, 0],
        facecolor=[1, 0, 0],
        zorder=-3,
        alpha=0.8,
    )
    # covalent filling
    ax.fill_between(
        [(XPt + min(chi_list)) / 2, ((XPt + min(chi_list)) / 2 + -b2 / slope2) / 2],
        [0, slope2 * (((XPt + min(chi_list)) / 2 + -b2 / slope2) / 2) + b2],
        facecolor=[0, 1, 0],
        zorder=-4,
        alpha=0.8,
    )
    ax.fill_between(
        [((XPt + min(chi_list)) / 2 + -b2 / slope2) / 2, -b2 / slope2],
        [slope2 * (((XPt + min(chi_list)) / 2 + -b2 / slope2) / 2) + b2, 0],
        facecolor=[0, 1, 0],
        zorder=-4,
        alpha=0.8,
    )

    # Label the triangle with datapoints
    for entry in list_of_materials:
        if type(entry).__name__ not in ["ComputedEntry", "ComputedStructureEntry"]:
            X_pair = [Element(el).X for el in entry]
            formatted_formula = "%s-%s" % tuple(entry)
        else:
            X_pair = [Element(el).X for el in entry.composition.as_dict().keys()]
            formatted_formula = format_formula(entry.composition.reduced_formula)
        plt.scatter(np.mean(X_pair), abs(X_pair[0] - X_pair[1]), c="b", s=100)
        if annotate:
            plt.annotate(
                formatted_formula,
                fontsize=15,
                xy=[np.mean(X_pair) + 0.005, abs(X_pair[0] - X_pair[1])],
            )

    plt.tight_layout()
    return plt
Пример #52
0
 def from_dict(cls, d):
     entries = [ComputedEntry.from_dict(dd) for dd in d["all_entries"]]
     elements = [Element.from_dict(dd) for dd in d["elements"]]
     return cls(entries, elements)
Пример #53
0
    def from_string(data):
        """
        Reads the exciting input from a string
        """

        root = ET.fromstring(data)
        speciesnode = root.find("structure").iter("species")
        elements = []
        positions = []
        vectors = []
        lockxyz = []
        # get title
        title_in = str(root.find("title").text)
        # Read elements and coordinates
        for nodes in speciesnode:
            symbol = nodes.get("speciesfile").split(".")[0]
            if len(symbol.split("_")) == 2:
                symbol = symbol.split("_")[0]
            if Element.is_valid_symbol(symbol):
                # Try to recognize the element symbol
                element = symbol
            else:
                raise ValueError("Unknown element!")
            for atom in nodes.iter("atom"):
                x, y, z = atom.get("coord").split()
                positions.append([float(x), float(y), float(z)])
                elements.append(element)
                # Obtain lockxyz for each atom
                if atom.get("lockxyz") is not None:
                    lxyz = []
                    for l in atom.get("lockxyz").split():
                        if l in ("True", "true"):
                            lxyz.append(True)
                        else:
                            lxyz.append(False)
                    lockxyz.append(lxyz)
                else:
                    lockxyz.append([False, False, False])
        # check the atomic positions type
        if "cartesian" in root.find("structure").attrib.keys():
            if root.find("structure").attrib["cartesian"]:
                cartesian = True
                for i, p in enumerate(positions):
                    for j in range(3):
                        p[j] = p[j] * ExcitingInput.bohr2ang
                print(positions)
        else:
            cartesian = False
        # get the scale attribute
        scale_in = root.find("structure").find("crystal").get("scale")
        if scale_in:
            scale = float(scale_in) * ExcitingInput.bohr2ang
        else:
            scale = ExcitingInput.bohr2ang
        # get the stretch attribute
        stretch_in = root.find("structure").find("crystal").get("stretch")
        if stretch_in:
            stretch = np.array([float(a) for a in stretch_in])
        else:
            stretch = np.array([1.0, 1.0, 1.0])
        # get basis vectors and scale them accordingly
        basisnode = root.find("structure").find("crystal").iter("basevect")
        for vect in basisnode:
            x, y, z = vect.text.split()
            vectors.append([
                float(x) * stretch[0] * scale,
                float(y) * stretch[1] * scale,
                float(z) * stretch[2] * scale,
            ])
        # create lattice and structure object
        lattice_in = Lattice(vectors)
        structure_in = Structure(lattice_in,
                                 elements,
                                 positions,
                                 coords_are_cartesian=cartesian)

        return ExcitingInput(structure_in, title_in, lockxyz)