示例#1
0
    def __init__(self, structure):
        """
        Conservative defect charge generator based on the oxidation statess 
        determined by bond valence. Targetted materials are wideband 
        semiconductors and insulators. AxBy where A is cation and B is 
        anion will have charge assignments {A: [0:y], B:[-x:0]}. For these 
        systems, antisites typically have very high formation energies and 
        are ignored.
        Args:
            structure: pymatgen structure object 
        """
        struct_species = structure.types_of_specie
        if len(struct_species) == 1:
            oxi_states = {struct_species[0].symbol: 0}
        else:
            vir = VIRE(structure)
            oxi_states = vir.valences
        self.oxi_states = {}
        for key, val in oxi_states.items():
            strip_key = ''.join([s for s in key if s.isalpha()])
            self.oxi_states[str2unicode(strip_key)] = val

        self.min_max_oxi = {}
        for s in struct_species:
            if isinstance(s, Specie):
                el = s.element
            elif isinstance(s, Element):
                el = s
            else:
                continue
            max_oxi = max(el.common_oxidation_states)
            min_oxi = min(el.common_oxidation_states)
            self.min_max_oxi[str2unicode(el.symbol)] = (min_oxi, max_oxi)
示例#2
0
def write_structure(structure, filename):
    """
    Write a structure to a file based on file extension. For example, anything
    ending in a "cif" is assumed to be a Crystallographic Information Format
    file. Supported formats include CIF, POSCAR, CSSR and pymatgen's JSON
    serialized structures.

    Args:
        structure (Structure/IStructure): Structure to write
        filename (str): A filename to write to.
    """
    fname = os.path.basename(filename)
    if fnmatch(fname, "*.cif*"):
        writer = CifWriter(structure)
    elif fnmatch(fname, "POSCAR*") or fnmatch(fname, "CONTCAR*"):
        writer = Poscar(structure)
    elif fnmatch(fname.lower(), "*.cssr*"):
        writer = Cssr(structure)
    elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"):
        with zopen(filename, "wt") as f:
            f.write(str2unicode(json.dumps(structure, cls=MontyEncoder)))
            return
    else:
        raise ValueError("Unrecognized file extension!")

    writer.write_file(filename)
示例#3
0
def write_mol(mol, filename):
    """
    Write a molecule to a file based on file extension. For example, anything
    ending in a "xyz" is assumed to be a XYZ file. Supported formats include
    xyz, Gaussian input (gjf|g03|g09|com|inp), and pymatgen's JSON serialized
    molecules.

    Args:
        mol (Molecule/IMolecule): Molecule to write
        filename (str): A filename to write to.
    """
    fname = os.path.basename(filename)
    if fnmatch(fname.lower(), "*.xyz*"):
        return XYZ(mol).write_file(filename)
    elif any([fnmatch(fname.lower(), "*.{}*".format(r))
              for r in ["gjf", "g03", "g09", "com", "inp"]]):
        return GaussianInput(mol).write_file(filename)
    elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"):
        with zopen(filename, "wt") as f:
            return f.write(str2unicode(json.dumps(mol, cls=MontyEncoder)))
    else:
        m = re.search("\.(pdb|mol|mdl|sdf|sd|ml2|sy2|mol2|cml|mrv)",
                      filename.lower())
        if m:
            return BabelMolAdaptor(mol).write_file(filename, m.group(1))

    raise ValueError("Unrecognized file extension!")
示例#4
0
def write_structure(structure, filename):
    """
    Write a structure to a file based on file extension. For example, anything
    ending in a "cif" is assumed to be a Crystallographic Information Format
    file. Supported formats include CIF, POSCAR, CSSR and pymatgen's JSON
    serialized structures.

    Args:
        structure (Structure/IStructure): Structure to write
        filename (str): A filename to write to.
    """
    fname = os.path.basename(filename)
    if fnmatch(fname, "*.cif*"):
        writer = CifWriter(structure)
    elif fnmatch(fname, "POSCAR*") or fnmatch(fname, "CONTCAR*"):
        writer = Poscar(structure)
    elif fnmatch(fname.lower(), "*.cssr*"):
        writer = Cssr(structure)
    elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"):
        with zopen(filename, "wt") as f:
            f.write(str2unicode(json.dumps(structure, cls=MontyEncoder)))
            return
    else:
        raise ValueError("Unrecognized file extension!")

    writer.write_file(filename)
示例#5
0
def write_mol(mol, filename):
    """
    Write a molecule to a file based on file extension. For example, anything
    ending in a "xyz" is assumed to be a XYZ file. Supported formats include
    xyz, Gaussian input (gjf|g03|g09|com|inp), and pymatgen's JSON serialized
    molecules.

    Args:
        mol (Molecule/IMolecule): Molecule to write
        filename (str): A filename to write to.
    """
    fname = os.path.basename(filename)
    if fnmatch(fname.lower(), "*.xyz*"):
        return XYZ(mol).write_file(filename)
    elif any([
            fnmatch(fname.lower(), "*.{}*".format(r))
            for r in ["gjf", "g03", "g09", "com", "inp"]
    ]):
        return GaussianInput(mol).write_file(filename)
    elif fnmatch(fname, "*.json*") or fnmatch(fname, "*.mson*"):
        with zopen(filename, "wt") as f:
            return f.write(str2unicode(json.dumps(mol, cls=MontyEncoder)))
    else:
        m = re.search("\.(pdb|mol|mdl|sdf|sd|ml2|sy2|mol2|cml|mrv)",
                      filename.lower())
        if m:
            return BabelMolAdaptor(mol).write_file(filename, m.group(1))

    raise ValueError("Unrecognized file extension!")
示例#6
0
    def get_charges(self, defect_type, site_specie=None, sub_specie=None):
        """
        Based on the type of defect, site and substitution (if any) species
        the defect charge states are generated.
        Args:
            defect_type (str): Options are vacancy, antisite, substitution,
                               and interstitial
            site_specie: Specie on the host lattice site
                         For interstitials, use this
            sub_specie: Specie that is replacing the site specie.
                        For antisites and substitution defects
        """
        if defect_type == 'vacancy':
            vac_symbol = get_el_sp(site_specie).symbol
            vac_oxi_state = self.oxi_states[str2unicode(vac_symbol)]
            if vac_oxi_state == 0:
                return [-1, 0, 1]
            else:
                minval = min(-vac_oxi_state, 0)
                maxval = max(-vac_oxi_state, 0)
                return [c for c in range(minval - 1, maxval + 2)]

        elif defect_type in ['antisite', 'substitution']:
            #TODO: may cause some weird states for substitutions. Worth updating in future.
            vac_symbol = get_el_sp(site_specie).symbol
            vac_oxi_state = self.oxi_states[str2unicode(vac_symbol)]
            as_symbol = get_el_sp(sub_specie).symbol
            as_oxi_state = self.oxi_states[str2unicode(as_symbol)]
            expected_oxi = as_oxi_state - vac_oxi_state
            if expected_oxi == 0:
                return [-1, 0, 1]
            else:
                minval = min(expected_oxi, 0)
                maxval = max(expected_oxi, 0)
                return [c for c in range(minval - 1, maxval + 2)]

        elif defect_type == 'interstitial':
            return [-1, 0, 1]
示例#7
0
 def __init__(self, structure):
     """
     Args:
         structure: pymatgen structure object
     """
     struct_species = structure.types_of_specie
     if len(struct_species) == 1:
         oxi_states = {struct_species[0].symbol: 0}
     else:
         vir = VIRE(structure)
         oxi_states = vir.valences
     self.oxi_states = {}
     for key, val in oxi_states.items():
         strip_key = ''.join([s for s in key if s.isalpha()])
         self.oxi_states[str2unicode(strip_key)] = val
示例#8
0
 def test_str2unicode(self):
     if sys.version_info.major < 3:
         self.assertEqual(type(str2unicode("a")), unicode)
     else:
         self.assertEqual(type(str2unicode("a")), str)
示例#9
0
 def test_str2unicode(self):
     if sys.version_info.major < 3:
         self.assertEqual(type(str2unicode("a")), unicode)
     else:
         self.assertEqual(type(str2unicode("a")), str)
示例#10
0
    def __init__(self,
                 structure,
                 max_min_oxi=None,
                 substitutions=None,
                 oxi_states=None,
                 cellmax=128,
                 antisites_flag=True,
                 include_interstitials=False,
                 interstitial_elements=None,
                 intersites=None,
                 standardized=False,
                 struct_type='semiconductor'):
        """
        Args:
            structure (Structure):
                the bulk structure.
            max_min_oxi (dict):
                The minimal and maximum oxidation state of each element as a
                dict. For instance {"O":(-2,0)}. If not given, the oxi-states
                of pymatgen are considered.
            substitutions (dict):
                The allowed substitutions of elements as a dict. If not given,
                intrinsic defects are computed. If given, intrinsic (e.g.,
                anti-sites) and extrinsic are considered explicitly specified.
                Example: {"Co":["Zn","Mn"]} means Co sites can be substituted
                by Mn or Zn.
            oxi_states (dict):
                The oxidation state of the elements in the compound e.g.
                {"Fe":2,"O":-2}. If not given, the oxidation state of each
                site is computed with bond valence sum. WARNING: Bond-valence
                method can fail for mixed-valence compounds.
            cellmax (int):
                Maximum number of atoms allowed in the supercell.
            antisites_flag (bool):
                If False, don't generate antisites.
            include_interstitials (bool):
                If true, do generate interstitial defect configurations
                (default: False).
            interstitial_elements ([str]):
                List of strings containing symbols of the elements that are
                to be considered for interstitial sites.  The default (None)
                triggers self-interstitial generation,
                given that include_interstitials is True.
            intersites ([PeriodicSite]):
                A list of PeriodicSites in the bulk structure on which we put
                interstitials.  Note that you still have to set flag
                include_interstitials to True in order to make use of this
                manual way of providing interstitial sites.
                If this is used, then no additional interstitials are generated
                beyond the list that is provided in intersites.
            standardized (bool):
                If True, use the primitive standard structure as unit cell
                for generating the defect configurations (default is False).
                The primitive standard structure is obtained from the
                SpacegroupAnalyzer class with a symprec of 0.01.
            struct_type (string):
                Options are 'semiconductor' and 'insulator'. If semiconductor
                is selected, charge states based on database of semiconductors
                is used to assign defect charges. For insulators, defect
                charges are conservatively assigned.
        """
        max_min_oxi = max_min_oxi if max_min_oxi is not None else {}
        substitutions = substitutions if substitutions is not None else {}
        oxi_states = oxi_states if oxi_states is not None else {}
        interstitial_elements = interstitial_elements if interstitial_elements is not None else []
        intersites = intersites if intersites is not None else []

        self.defects = []
        self.cellmax = cellmax
        self.substitutions = {}
        self.struct_type = struct_type
        for key, val in substitutions.items():
            self.substitutions[str2unicode(key)] = val

        spa = SpacegroupAnalyzer(structure, symprec=1e-2)
        prim_struct = spa.get_primitive_standard_structure()
        if standardized:
            self.struct = prim_struct
        else:
            self.struct = structure

        struct_species = self.struct.types_of_specie
        if self.struct_type == 'semiconductor':
            self.defect_charger = DefectChargerSemiconductor(
                self.struct, min_max_oxi=max_min_oxi)
        elif self.struct_type == 'insulator':
            self.defect_charger = DefectChargerInsulator(self.struct)
        elif self.struct_type == 'manual':
            self.defect_charger = DefectChargerUserCustom(
                self.struct, oxi_states=oxi_states)
        elif self.struct_type == 'ionic':
            self.defect_charger = DefectChargerIonic(self.struct)
        else:
            raise NotImplementedError

        if include_interstitials and interstitial_elements:
            for elem_str in interstitial_elements:
                if not Element.is_valid_symbol(elem_str):
                    raise ValueError("invalid interstitial element"
                                     " \"{}\"".format(elem_str))

        sc_scale = get_optimized_sc_scale(self.struct, cellmax)
        self.defects = {}
        sc = self.struct.copy()
        sc.make_supercell(sc_scale)
        self.defects['bulk'] = {
            'name': 'bulk',
            'supercell': {
                'size': sc_scale,
                'structure': sc
            }
        }

        # If interstitials are provided as a list of PeriodicSites,
        # make sure that the lattice has not changed.
        if include_interstitials and intersites:
            for intersite in intersites:  #list of PeriodicSite objects
                if intersite.lattice != self.struct.lattice:
                    raise RuntimeError(
                        "Discrepancy between lattices"
                        " underlying the input interstitials and"
                        " the bulk structure; possibly because of"
                        " standardizing the input structure.")

        vacancies = []
        as_defs = []
        sub_defs = []

        VG = VacancyGenerator(self.struct)
        print("Setting up defects...")
        for i, vac in enumerate(VG):
            vac_site = vac.site
            vac_symbol = vac.site.specie.symbol
            vac_sc = vac.generate_defect_structure(sc_scale)

            #create a trivial defect structure to find where supercell transformation moves the lattice
            struct_for_defect_site = Structure(
                vac.bulk_structure.copy().lattice, [vac.site.specie],
                [vac.site.frac_coords],
                to_unit_cell=True,
                coords_are_cartesian=False)
            struct_for_defect_site.make_supercell(sc_scale)
            vac_sc_site = struct_for_defect_site[0]

            charges_vac = self.defect_charger.get_charges(
                'vacancy', vac_symbol)
            vacancies.append({
                'name': "vac_{}_{}".format(i + 1, vac_symbol),
                'unique_site': vac_site,
                'bulk_supercell_site': vac_sc_site,
                'defect_type': 'vacancy',
                'site_specie': vac_symbol,
                'site_multiplicity': vac.multiplicity,
                'supercell': {
                    'size': sc_scale,
                    'structure': vac_sc
                },
                'charges': charges_vac
            })

        if antisites_flag:
            for as_specie in set(struct_species):
                SG = SubstitutionGenerator(self.struct, as_specie)
                for i, sub in enumerate(SG):
                    as_symbol = as_specie.symbol
                    as_sc = sub.generate_defect_structure(sc_scale)

                    # create a trivial defect structure to find where supercell transformation moves the defect
                    struct_for_defect_site = Structure(
                        sub.bulk_structure.copy().lattice, [sub.site.specie],
                        [sub.site.frac_coords],
                        to_unit_cell=True,
                        coords_are_cartesian=False)
                    struct_for_defect_site.make_supercell(sc_scale)
                    as_sc_site = struct_for_defect_site[0]

                    #get bulk_site (non sc)
                    poss_deflist = sorted(
                        sub.bulk_structure.get_sites_in_sphere(
                            sub.site.coords, 0.01, include_index=True),
                        key=lambda x: x[1])
                    if not len(poss_deflist):
                        raise ValueError(
                            "Could not find substitution site inside bulk structure for {}?"
                            .format(sub.name))
                    defindex = poss_deflist[0][2]
                    as_site = sub.bulk_structure[defindex]
                    vac_symbol = as_site.specie

                    charges_as = self.defect_charger.get_charges(
                        'antisite', vac_symbol, as_symbol)

                    as_defs.append({
                        'name':
                        "as_{}_{}_on_{}".format(i + 1, as_symbol, vac_symbol),
                        'unique_site':
                        as_site,
                        'bulk_supercell_site':
                        as_sc_site,
                        'defect_type':
                        'antisite',
                        'site_specie':
                        vac_symbol,
                        'substituting_specie':
                        as_symbol,
                        'site_multiplicity':
                        sub.multiplicity,
                        'supercell': {
                            'size': sc_scale,
                            'structure': as_sc
                        },
                        'charges':
                        charges_as
                    })

        for vac_symbol, subspecie_list in self.substitutions.items():
            for subspecie_symbol in subspecie_list:
                SG = SubstitutionGenerator(self.struct, subspecie_symbol)
                for i, sub in enumerate(SG):
                    sub_symbol = sub.site.specie.symbol

                    #get bulk_site (non sc)
                    poss_deflist = sorted(
                        sub.bulk_structure.get_sites_in_sphere(
                            sub.site.coords, 0.1, include_index=True),
                        key=lambda x: x[1])
                    if not len(poss_deflist):
                        raise ValueError(
                            "Could not find substitution site inside bulk structure for {}?"
                            .format(sub.name))
                    defindex = poss_deflist[0][2]
                    sub_site = self.struct[defindex]
                    this_vac_symbol = sub_site.specie.symbol

                    if (sub_symbol != subspecie_symbol) or (this_vac_symbol !=
                                                            vac_symbol):
                        continue
                    else:
                        sub_sc = sub.generate_defect_structure(sc_scale)

                        # create a trivial defect structure to find where supercell transformation moves the defect
                        struct_for_defect_site = Structure(
                            sub.bulk_structure.copy().lattice,
                            [sub.site.specie], [sub.site.frac_coords],
                            to_unit_cell=True,
                            coords_are_cartesian=False)
                        struct_for_defect_site.make_supercell(sc_scale)
                        sub_sc_site = struct_for_defect_site[0]

                        charges_sub = self.defect_charger.get_charges(
                            'substitution', vac_symbol, subspecie_symbol)
                        sub_defs.append({
                            'name':
                            "sub_{}_{}_on_{}".format(i + 1, subspecie_symbol,
                                                     vac_symbol),
                            'unique_site':
                            sub_site,
                            'bulk_supercell_site':
                            sub_sc_site,
                            'defect_type':
                            'substitution',
                            'site_specie':
                            vac_symbol,
                            'substitution_specie':
                            subspecie_symbol,
                            'site_multiplicity':
                            sub.multiplicity,
                            'supercell': {
                                'size': sc_scale,
                                'structure': sub_sc
                            },
                            'charges':
                            charges_sub
                        })

        self.defects['vacancies'] = vacancies
        self.defects['substitutions'] = sub_defs
        self.defects['substitutions'] += as_defs

        if include_interstitials:
            interstitials = []

            if interstitial_elements:
                inter_elems = interstitial_elements
            else:
                inter_elems = [elem.symbol for elem in \
                        self.struct.composition.elements]
            if len(inter_elems) == 0:
                raise RuntimeError("empty element list for interstitials")

            if intersites:
                #manual specification of interstitials
                for i, intersite in enumerate(intersites):
                    for elt in inter_elems:
                        name = "inter_{}_{}".format(i + 1, elt)

                        if intersite.lattice != self.struct.lattice:
                            err_msg = "Lattice matching error occurs between provided interstitial and the bulk structure."
                            if standardized:
                                err_msg += "\nLikely because the standardized flag was used. Turn this flag off or reset " \
                                           "your interstitial PeriodicSite to match the standardized form of the bulk structure."
                            raise ValueError(err_msg)
                        else:
                            intersite_object = Interstitial(
                                self.struct, intersite)

                        # create a trivial defect structure to find where supercell transformation moves the defect site
                        struct_for_defect_site = Structure(
                            intersite_object.bulk_structure.copy().lattice,
                            [intersite_object.site.specie],
                            [intersite_object.site.frac_coords],
                            to_unit_cell=True,
                            coords_are_cartesian=False)
                        struct_for_defect_site.make_supercell(sc_scale)
                        site_sc = struct_for_defect_site[0]

                        sc_with_inter = intersite_object.generate_defect_structure(
                            sc_scale)
                        charges_inter = self.defect_charger.get_charges(
                            'interstitial', elt)

                        interstitials.append({
                            'name':
                            name,
                            'unique_site':
                            intersite_object.site,
                            'bulk_supercell_site':
                            site_sc,
                            'defect_type':
                            'interstitial',
                            'site_specie':
                            intersite_object.site.specie.symbol,
                            'site_multiplicity':
                            intersite_object.multiplicity,
                            'supercell': {
                                'size': sc_scale,
                                'structure': sc_with_inter
                            },
                            'charges':
                            charges_inter
                        })

            else:
                print(
                    "Searching for interstitial sites (this can take awhile)..."
                )
                for elt in inter_elems:
                    #TODO: Add ability to use other interstitial finding methods in pymatgen
                    IG = InterstitialGenerator(self.struct, elt)
                    for i, intersite_object in enumerate(IG):
                        name = intersite_object.name

                        # create a trivial defect structure to find where supercell transformation moves the defect site
                        struct_for_defect_site = Structure(
                            intersite_object.bulk_structure.copy().lattice,
                            [intersite_object.site.specie],
                            [intersite_object.site.frac_coords],
                            to_unit_cell=True,
                            coords_are_cartesian=False)
                        struct_for_defect_site.make_supercell(sc_scale)
                        site_sc = struct_for_defect_site[0]

                        sc_with_inter = intersite_object.generate_defect_structure(
                            sc_scale)
                        charges_inter = self.defect_charger.get_charges(
                            'interstitial', elt)

                        interstitials.append({
                            'name':
                            name,
                            'unique_site':
                            intersite_object.site,
                            'bulk_supercell_site':
                            site_sc,
                            'defect_type':
                            'interstitial',
                            'site_specie':
                            intersite_object.site.specie.symbol,
                            'site_multiplicity':
                            intersite_object.multiplicity,
                            'supercell': {
                                'size': sc_scale,
                                'structure': sc_with_inter
                            },
                            'charges':
                            charges_inter
                        })

            self.defects['interstitials'] = interstitials

        print("\nNumber of jobs created:")
        tottmp = 0
        for j in self.defects.keys():
            if j == 'bulk':
                print("    bulk = 1")
                tottmp += 1
            else:
                print("    {}:".format(j))
                for lis in self.defects[j]:
                    print("        {} = {}".format(lis['name'],
                                                   len(lis['charges'])))
                    tottmp += len(lis['charges'])
        print("Total (non dielectric) jobs created = {}\n".format(tottmp))
示例#11
0
    def get_charges(self, defect_type, site_specie=None, sub_specie=None):
        """
        Based on the type of defect, site and substitution (if any) species
        the defect charge states are generated.
        Args:
            defect_type (str): Options are vacancy, antisite, substitution,
                               and interstitial
            site_specie: Specie on the host lattice site 
                         For interstitials, use this
            sub_specie: Specie that is replacing the site specie.
                        For antisites and substitution defects
        """
        if defect_type == 'vacancy':
            vac_symbol = get_el_sp(site_specie).symbol
            vac_oxi_state = self.oxi_states[str2unicode(vac_symbol)]
            if vac_oxi_state < 0:
                min_oxi = max(vac_oxi_state, self.min_max_oxi[vac_symbol][0])
                max_oxi = 0
            elif vac_oxi_state > 0:
                max_oxi = min(vac_oxi_state, self.min_max_oxi[vac_symbol][1])
                min_oxi = 0
            else:  # most probably single element
                oxi_states = get_el_sp(site_specie).common_oxidation_states
                min_oxi = min(oxi_states)
                max_oxi = max(oxi_states)
            return [-c for c in range(min_oxi, max_oxi + 1)]

        elif defect_type == 'antisite':
            vac_symbol = get_el_sp(site_specie).symbol
            vac_oxi_state = self.oxi_states[str2unicode(vac_symbol)]
            as_symbol = get_el_sp(sub_specie).symbol
            if vac_oxi_state > 0:
                oxi_max = max(self.min_max_oxi[as_symbol][1], 0)
                oxi_min = 0
            else:
                oxi_max = 0
                oxi_min = min(self.min_max_oxi[as_symbol][0], 0)
            return [c - vac_oxi_state for c in range(oxi_min, oxi_max + 1)]

        elif defect_type == 'substitution':
            site_specie = get_el_sp(site_specie)
            sub_specie = get_el_sp(sub_specie)
            vac_symbol = site_specie.symbol
            vac_oxi_state = self.oxi_states[str2unicode(vac_symbol)]

            max_oxi_sub = max(sub_specie.common_oxidation_states)
            min_oxi_sub = min(sub_specie.common_oxidation_states)
            if vac_oxi_state > 0:
                if max_oxi_sub < 0:
                    raise ValueError("Substitution seems not possible")
                else:
                    if max_oxi_sub > vac_oxi_state:
                        return list(range(max_oxi_sub - vac_oxi_state + 1))
                    else:
                        return [max_oxi_sub - vac_oxi_state]
            else:
                if min_oxi_sub > 0:
                    raise ValueError("Substitution seems not possible")
                else:
                    if min_oxi_sub < vac_oxi_state:
                        return list(range(min_oxi_sub - vac_oxi_state, 1))
                    else:
                        return [min_oxi_sub - vac_oxi_state]

        elif defect_type == 'interstitial':
            site_specie = get_el_sp(site_specie)
            min_oxi = min(min(site_specie.common_oxidation_states), 0)
            max_oxi = max(max(site_specie.common_oxidation_states), 0)

            return list(range(min_oxi, max_oxi + 1))