def setUp(self): latt = Lattice.cubic(4.17) species = ["Ni", "O"] coords = [[0, 0, 0], [0.5, 0.5, 0.5]] self.NiO = Structure.from_spacegroup(225, latt, species, coords) latt = Lattice([[2.085, 2.085, 0.0], [0.0, -2.085, -2.085], [-2.085, 2.085, -4.17]]) species = ["Ni", "Ni", "O", "O"] coords = [[0.5, 0, 0.5], [0, 0, 0], [0.25, 0.5, 0.25], [0.75, 0.5, 0.75]] self.NiO_AFM_111 = Structure(latt, species, coords) self.NiO_AFM_111.add_spin_by_site([-5, 5, 0, 0]) latt = Lattice([[2.085, 2.085, 0], [0, 0, -4.17], [-2.085, 2.085, 0]]) species = ["Ni", "Ni", "O", "O"] coords = [[0.5, 0.5, 0.5], [0, 0, 0], [0, 0.5, 0], [0.5, 0, 0.5]] self.NiO_AFM_001 = Structure(latt, species, coords) self.NiO_AFM_001.add_spin_by_site([-5, 5, 0, 0]) parser = CifParser(os.path.join(PymatgenTest.TEST_FILES_DIR, "Fe3O4.cif")) self.Fe3O4 = parser.get_structures()[0] trans = AutoOxiStateDecorationTransformation() self.Fe3O4_oxi = trans.apply_transformation(self.Fe3O4) parser = CifParser(os.path.join(PymatgenTest.TEST_FILES_DIR, "Li8Fe2NiCoO8.cif")) self.Li8Fe2NiCoO8 = parser.get_structures()[0] self.Li8Fe2NiCoO8.remove_oxidation_states() warnings.simplefilter("ignore")
def substitution_structures(self, mp_struct, mpid): ''' Predicts substituted compounds from a given Materials Project structure (mp_struct) and ID (mpid) ''' ''' flat_predictions: a list of predicted structures as pymatgen.alchemy.materials.TransformedStructure objects ''' auto_oxi = AutoOxiStateDecorationTransformation() #initialize the automatic oxidation state transformation structures = [] try: oxi_struct = auto_oxi.apply_transformation(mp_struct) # get oxidized reference structure subs = SubstitutionPredictor().composition_prediction(oxi_struct.composition) # from a given charge-balanced trial_subs = [list(sub['substitutions'].keys()) for sub in subs] # returns the potential substitutions sbr = Substitutor() for sub in trial_subs: run = True for specie in sub: if str(specie.element) not in self.elements: run = False if run == True: structs = sbr.pred_from_structures(sub, [{'structure':oxi_struct, 'id':mpid}]) structures.append(structs) except: pass flat_predictions = [structure for substitution in structures for structure in substitution] return flat_predictions
def __init__(self, structure_templates, requests, crystals, query=None, **kwargs): """ Predict structures given a list of elements and their oxidation states. Args: structure_templates (Store): store of template structures to predict from requests (Store): store of structure prediction requests crystals (Store): predicted crystal structures and their info (XRD, spacegroup, etc.) """ self.structure_templates = structure_templates self.requests = requests self.crystals = crystals self.query = query if query else {} self.kwargs = kwargs self.auto_oxi = AutoOxiStateDecorationTransformation() super().__init__( sources=[structure_templates, requests], targets=[requests, crystals], **kwargs, )
def _evaluate(self, symbol_values): s = symbol_values['s'] trans = AutoOxiStateDecorationTransformation() s_oxi = trans.apply_transformation(s) return {'s_oxi': s_oxi}
def test_apply_transformation(self): p = Poscar.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "POSCAR.LiFePO4"), check_for_POTCAR=False) t = AutoOxiStateDecorationTransformation() s = t.apply_transformation(p.structure) expected_oxi = {"Li": 1, "P": 5, "O": -2, "Fe": 2} for site in s: self.assertEqual(site.specie.oxi_state, expected_oxi[site.specie.symbol])
def create_crystal(cifpath, primitive=False): """ Convert cif to pymatgen struture """ structure = CifParser(cifpath).get_structures(primitive=primitive)[0] if structure.is_ordered: return structure order_transformer = OrderDisorderedStructureTransformation() oxid_transformer = AutoOxiStateDecorationTransformation() a = oxid_transformer.apply_transformation(structure) b = order_transformer.apply_transformation(a) return b
def get_Species_Substitution_Order(structure, dummies, species, to_ignore): els_removed = species.copy() for element in to_ignore: els_removed.remove(Element(element)) subs = [list(p) for p in permutations(els_removed)] diff_coords = [] struct_copies = [] for sub in subs: copy = structure.copy() for dummy_ind in range(len(dummies)): copy[dummies[dummy_ind].symbol] = sub[dummy_ind] struct_copies.append(copy) coordination_numbers = [] CN = CrystalNN(weighted_cn=True) for element_ind in range(len(copy.species)): cn = CN.get_cn(copy, element_ind, use_weights=True) coordination_numbers.append((copy.species[element_ind], cn)) diff_coords.append(coordination_numbers) unique_cn_els = [] for cn in diff_coords: cns = [] for i in range(len(cn)): if cn[i] not in cns: cns.append(cn[i]) unique_cn_els.append(cns) substitution_ind = None check_len = 300 # arbitrarily high number to start, used to get substitution scheme with lowest number of unique sites for unique_cn_el_ind in range(len(unique_cn_els)): if len(unique_cn_els[unique_cn_el_ind]) < check_len: substitution_ind = unique_cn_el_ind check_len = len(unique_cn_els[unique_cn_el_ind]) else: continue ox_transform = AutoOxiStateDecorationTransformation() ox_species = ox_transform.apply_transformation( struct_copies[substitution_ind]).types_of_specie for specie in ox_species: for element in to_ignore: if specie.symbol == Element(element).symbol: ox_species.remove(specie) return subs[substitution_ind], ox_species
def setUp(self): latt = Lattice.cubic(4.17) species = ["Ni", "O"] coords = [[0, 0, 0], [0.5, 0.5, 0.5]] self.NiO = Structure.from_spacegroup(225, latt, species, coords) latt = Lattice([[2.085, 2.085, 0.0], [0.0, -2.085, -2.085], [-2.085, 2.085, -4.17]]) species = ["Ni", "Ni", "O", "O"] coords = [[0.5, 0, 0.5], [0, 0, 0], [0.25, 0.5, 0.25], [0.75, 0.5, 0.75]] self.NiO_AFM_111 = Structure(latt, species, coords) self.NiO_AFM_111.add_spin_by_site([-5, 5, 0, 0]) latt = Lattice([[2.085, 2.085, 0], [0, 0, -4.17], [-2.085, 2.085, 0]]) species = ["Ni", "Ni", "O", "O"] coords = [[0.5, 0.5, 0.5], [0, 0, 0], [0, 0.5, 0], [0.5, 0, 0.5]] self.NiO_AFM_001 = Structure(latt, species, coords) self.NiO_AFM_001.add_spin_by_site([-5, 5, 0, 0]) parser = CifParser(os.path.join(test_dir, 'Fe3O4.cif')) self.Fe3O4 = parser.get_structures()[0] trans = AutoOxiStateDecorationTransformation() self.Fe3O4_oxi = trans.apply_transformation(self.Fe3O4) parser = CifParser(os.path.join(test_dir, 'Li8Fe2NiCoO8.cif')) self.Li8Fe2NiCoO8 = parser.get_structures()[0] self.Li8Fe2NiCoO8.remove_oxidation_states() warnings.simplefilter("ignore")
# j = st.SiteCollection.get_distance(0,4) # j = st.lattice_vectors # print(j) # list = pymatgen.structure_prediction.pred_from_structures(st) # print(dir(sp)) # print(sp.__path__) # print(sp.__doc__) # print(dir(pymatgen.core.structure)) # print(dir(stt.SiteCollection)) # print(sp.__builtins__) # print(dir(test)) # i = test.SubstitutionPredictor(st.composition) Subs = substitutor.Substitutor() # Subs._threshold = 0.001 # Any threshold beyond 0.001 brings it out of the default hence it doesn't work: has to figure out how to explicitly set the threshold probability struc = AutoOxiStateDecorationTransformation().apply_transformation(st) spgr = struc.get_spacegroup_info() print(struc.get_spacegroup_info()) # print(struc) cations = ['Co', 'Rh', 'Ir'] #cations = ['Li', 'Na', 'K', 'Rb', 'Cs'] # cations2 = ['C','Si','Ge','Sn', 'Pb'] anions = ['F', 'Cl', 'Br', 'I'] counter = 0 for cation in cations: # for cation2 in cations2: for anion in anions: try: # list = Subs.pred_from_structures(target_species=[Specie(cation, +1), Specie(cation2, +2), Specie(anion, -1)], structures_list=[{'structure': struc, 'id': 'TEST'}]) list = Subs.pred_from_structures(
def __init__( self, structure: Structure, overwrite_magmom_mode: Union[OverwriteMagmomMode, str] = "none", round_magmoms: bool = False, detect_valences: bool = False, make_primitive: bool = True, default_magmoms: bool = None, set_net_positive: bool = True, threshold: float = 0.1, ): """ A class which provides a few helpful methods to analyze collinear magnetic structures. If magnetic moments are not defined, moments will be taken either from default_magmoms.yaml (similar to the default magmoms in MPRelaxSet, with a few extra definitions) or from a specie:magmom dict provided by the default_magmoms kwarg. Input magmoms can be replaced using the 'overwrite_magmom_mode' kwarg. This can be: * "none" to do nothing, * "respect_sign" which will overwrite existing magmoms with those from default_magmoms but will keep sites with positive magmoms positive, negative magmoms negative and zero magmoms zero, * "respect_zeros", which will give a ferromagnetic structure (all positive magmoms from default_magmoms) but still keep sites with zero magmoms as zero, * "replace_all" which will try to guess initial magmoms for all sites in the structure irrespective of input structure (this is most suitable for an initial DFT calculation), * "replace_all_if_undefined" is the same as "replace_all" but only if no magmoms are defined in input structure, otherwise it will respect existing magmoms. * "normalize" will normalize magmoms to unity, but will respect sign (used for comparing orderings), magmoms < theshold will be set to zero :param structure: Structure object :param overwrite_magmom_mode (str): default "none" :param round_magmoms (int or bool): will round input magmoms to specified number of decimal places if integer is supplied, if set to a float will try and group magmoms together using a kernel density estimator of provided width, and extracting peaks of the estimator :param detect_valences (bool): if True, will attempt to assign valences to input structure :param make_primitive (bool): if True, will transform to primitive magnetic cell :param default_magmoms (dict): (optional) dict specifying default magmoms :param set_net_positive (bool): if True, will change sign of magnetic moments such that the net magnetization is positive. Argument will be ignored if mode "respect_sign" is used. :param threshold (float): number (in Bohr magnetons) below which magmoms will be rounded to zero, default of 0.1 can probably be increased for many magnetic systems, depending on your application """ if default_magmoms: self.default_magmoms = default_magmoms else: self.default_magmoms = DEFAULT_MAGMOMS structure = structure.copy() # check for disorder if not structure.is_ordered: raise NotImplementedError( "Not implemented for disordered structures, " "make ordered approximation first.") if detect_valences: trans = AutoOxiStateDecorationTransformation() bva = BVAnalyzer() try: structure = trans.apply_transformation(structure) except ValueError: warnings.warn("Could not assign valences " "for {}".format( structure.composition.reduced_formula)) # check to see if structure has magnetic moments # on site properties or species spin properties, # prioritize site properties has_magmoms = bool(structure.site_properties.get("magmom", False)) has_spin = False for comp in structure.species_and_occu: for sp, occu in comp.items(): if getattr(sp, "spin", False): has_spin = True # perform input sanitation ... # rest of class will assume magnetic moments # are stored on site properties: # this is somewhat arbitrary, arguments can # be made for both approaches if has_magmoms and has_spin: raise ValueError("Structure contains magnetic moments on both " "magmom site properties and spin species " "properties. This is ambiguous. Remove one or " "the other.") elif has_magmoms: if None in structure.site_properties["magmom"]: warnings.warn("Be careful with mixing types in your magmom " "site properties. Any 'None' magmoms have been " "replaced with zero.") magmoms = [ m if m else 0 for m in structure.site_properties["magmom"] ] elif has_spin: magmoms = [getattr(sp, "spin", 0) for sp in structure.species] structure.remove_spin() else: # no magmoms present, add zero magmoms for now magmoms = [0] * len(structure) # and overwrite magmoms with default magmoms later unless otherwise stated if overwrite_magmom_mode == "replace_all_if_undefined": overwrite_magmom_mode = "replace_all" # test to see if input structure has collinear magmoms self.is_collinear = Magmom.are_collinear(magmoms) if not self.is_collinear: warnings.warn( "This class is not designed to be used with " "non-collinear structures. If your structure is " "only slightly non-collinear (e.g. canted) may still " "give useful results, but use with caution.") # this is for collinear structures only, make sure magmoms # are all floats magmoms = list(map(float, magmoms)) # set properties that should be done /before/ we process input magmoms self.total_magmoms = sum(magmoms) self.magnetization = sum(magmoms) / structure.volume # round magmoms below threshold to zero magmoms = [m if abs(m) > threshold else 0 for m in magmoms] # overwrite existing magmoms with default_magmoms if overwrite_magmom_mode not in ( "none", "respect_sign", "respect_zeros", "replace_all", "replace_all_if_undefined", "normalize", ): raise ValueError("Unsupported mode.") for idx, site in enumerate(structure): if site.species_string in self.default_magmoms: # look for species first, e.g. Fe2+ default_magmom = self.default_magmoms[site.species_string] elif (isinstance(site.specie, Specie) and str(site.specie.element) in self.default_magmoms): # look for element, e.g. Fe default_magmom = self.default_magmoms[str(site.specie.element)] else: default_magmom = 0 # overwrite_magmom_mode = "respect_sign" will change magnitude of # existing moments only, and keep zero magmoms as # zero: it will keep the magnetic ordering intact if overwrite_magmom_mode == "respect_sign": set_net_positive = False if magmoms[idx] > 0: magmoms[idx] = default_magmom elif magmoms[idx] < 0: magmoms[idx] = -default_magmom # overwrite_magmom_mode = "respect_zeros" will give a ferromagnetic # structure but will keep zero magmoms as zero elif overwrite_magmom_mode == "respect_zeros": if magmoms[idx] != 0: magmoms[idx] = default_magmom # overwrite_magmom_mode = "replace_all" will ignore input magmoms # and give a ferromagnetic structure with magnetic # moments on *all* atoms it thinks could be magnetic elif overwrite_magmom_mode == "replace_all": magmoms[idx] = default_magmom # overwrite_magmom_mode = "normalize" set magmoms magnitude to 1 elif overwrite_magmom_mode == "normalize": if magmoms[idx] != 0: magmoms[idx] = int(magmoms[idx] / abs(magmoms[idx])) # round magmoms, used to smooth out computational data magmoms = (self._round_magmoms(magmoms, round_magmoms) if round_magmoms else magmoms) if set_net_positive: sign = np.sum(magmoms) if sign < 0: magmoms = -np.array(magmoms) structure.add_site_property("magmom", magmoms) if make_primitive: structure = structure.get_primitive_structure(use_site_props=True) self.structure = structure
def __init__(self, structure, overwrite_magmom_mode="none", round_magmoms=False, detect_valences=False, make_primitive=True, default_magmoms=None, threshold=0.1): """ A class which provides a few helpful methods to analyze collinear magnetic structures. If magnetic moments are not defined, moments will be taken either from default_magmoms.yaml (similar to the default magmoms in MPRelaxSet, with a few extra definitions) or from a specie:magmom dict provided by the default_magmoms kwarg. Input magmoms can be replaced using the 'overwrite_magmom_mode' kwarg. This can be: * "none" to do nothing, * "respect_sign" which will overwrite existing magmoms with those from default_magmoms but will keep sites with positive magmoms positive, negative magmoms negative and zero magmoms zero, * "respect_zeros", which will give a ferromagnetic structure (all positive magmoms from default_magmoms) but still keep sites with zero magmoms as zero, * "replace_all" which will try to guess initial magmoms for all sites in the structure irrespective of input structure (this is most suitable for an initial DFT calculation), * "replace_all_if_undefined" is the same as "replace_all" but only if no magmoms are defined in input structure, otherwise it will respect existing magmoms. :param structure: Structure object :param overwrite_magmom_mode (str): default "none" :param round_magmoms (int): will round input magmoms to specified number of decimal places, suggest value of 1 or False for typical DFT calculations depending on application :param detect_valences (bool): if True, will attempt to assign valences to input structure :param make_primitive (bool): if True, will transform to primitive magnetic cell :param default_magmoms (dict): (optional) dict specifying default magmoms :param threshold (float): number (in Bohr magnetons) below which magmoms will be rounded to zero, default of 0.1 can probably be increased for many magnetic systems, depending on your application """ if default_magmoms: self.default_magmoms = default_magmoms else: self.default_magmoms = DEFAULT_MAGMOMS structure = structure.copy() # check for disorder if not structure.is_ordered: raise NotImplementedError("Not implemented for disordered structures, " "make ordered approximation first.") if detect_valences: trans = AutoOxiStateDecorationTransformation() bva = BVAnalyzer() try: structure = trans.apply_transformation(structure) except ValueError: warnings.warn("Could not assign valences " "for {}".format(structure.composition.reduced_formula)) # check to see if structure has magnetic moments # on site properties or species spin properties, # prioritize site properties has_magmoms = bool(structure.site_properties.get('magmom', False)) has_spin = False for comp in structure.species_and_occu: for sp, occu in comp.items(): if getattr(sp, 'spin', False): has_spin = True # perform input sanitation ... # rest of class will assume magnetic moments # are stored on site properties: # this is somewhat arbitrary, arguments can # be made for both approaches if has_magmoms and has_spin: raise ValueError("Structure contains magnetic moments on both " "magmom site properties and spin species " "properties. This is ambiguous. Remove one or " "the other.") elif has_magmoms: if None in structure.site_properties['magmom']: warnings.warn("Be careful with mixing types in your magmom " "site properties. Any 'None' magmoms have been " "replaced with zero.") magmoms = [m if m else 0 for m in structure.site_properties['magmom']] elif has_spin: magmoms = [getattr(sp, 'spin', 0) for sp in structure.species] structure.remove_spin() else: # no magmoms present, add zero magmoms for now magmoms = [0]*len(structure) # and overwrite magmoms with default magmoms later unless otherwise stated if overwrite_magmom_mode == "replace_all_if_undefined": overwrite_magmom_mode = "replace_all" # test to see if input structure has collinear magmoms self.is_collinear = Magmom.are_collinear(magmoms) if not self.is_collinear: warnings.warn("This class is not designed to be used with " "non-collinear structures. If your structure is " "only slightly non-collinear (e.g. canted) may still " "give useful results, but use with caution.") # this is for collinear structures only, make sure magmoms # are all floats magmoms = list(map(float, magmoms)) # set properties that should be done /before/ we process input magmoms self.total_magmoms = sum(magmoms) self.magnetization = sum(magmoms)/structure.volume # round magmoms below threshold to zero magmoms = [m if abs(m) > threshold else 0 for m in magmoms] # overwrite existing magmoms with default_magmoms if overwrite_magmom_mode not in ("none", "respect_sign", "respect_zeros", "replace_all", "replace_all_if_undefined"): raise ValueError("Unsupported mode.") for idx, site in enumerate(structure): if site.species_string in self.default_magmoms: # look for species first, e.g. Fe2+ default_magmom = self.default_magmoms[site.species_string] elif isinstance(site.specie, Specie) and \ str(site.specie.element) in self.default_magmoms: # look for element, e.g. Fe default_magmom = self.default_magmoms[str(site.specie.element)] else: default_magmom = 0 # overwrite_magmom_mode = "respect_sign" will change magnitude of # existing moments only, and keep zero magmoms as # zero: it will keep the magnetic ordering intact if overwrite_magmom_mode == "respect_sign": if magmoms[idx] > 0: magmoms[idx] = default_magmom elif magmoms[idx] < 0: magmoms[idx] = -default_magmom # overwrite_magmom_mode = "respect_zeros" will give a ferromagnetic # structure but will keep zero magmoms as zero elif overwrite_magmom_mode == "respect_zeros": if magmoms[idx] != 0: magmoms[idx] = default_magmom # overwrite_magmom_mode = "replace_all" will ignore input magmoms # and give a ferromagnetic structure with magnetic # moments on *all* atoms it thinks could be magnetic elif overwrite_magmom_mode == "replace_all": magmoms[idx] = default_magmom # round magmoms to specified number of # decimal places, used to smooth out # computational data # TODO: be a bit smarter about rounding magmoms! if round_magmoms: magmoms = np.around(structure.site_properties['magmom'], decimals=round_magmoms) structure.add_site_property(magmoms) structure.add_site_property('magmom', magmoms) if make_primitive: structure = structure.get_primitive_structure(use_site_props=True) self.structure = structure
def test_as_from_dict(self): t = AutoOxiStateDecorationTransformation() d = t.as_dict() t = AutoOxiStateDecorationTransformation.from_dict(d) self.assertEqual(t.analyzer.dist_scale_factor, 1.015)
def _generate_radii(self): structure = self.structure_graph.structure # don't calculate radius if one is explicitly supplied if 'display_radius' in structure.site_properties: return if self.radius_strategy is 'bvanalyzer_ionic': trans = AutoOxiStateDecorationTransformation() try: structure = trans.apply_transformation( self.structure_graph.structure) except: # if we can't assign valences use average ionic self.radius_strategy = 'average_ionic' radii = [] for site_idx, site in enumerate(structure): site_radii = [] for comp_idx, (sp, occu) in enumerate(site.species_and_occu.items()): radius = None if self.radius_strategy not in self.available_radius_strategies: raise ValueError( "Unknown radius strategy {}, choose from: {}".format( self.radius_strategy, self.available_radius_strategies)) if self.radius_strategy is 'atomic': radius = sp.atomic_radius elif self.radius_strategy is 'bvanalyzer_ionic' and isinstance( sp, Specie): radius = sp.ionic_radius elif self.radius_strategy is 'average_ionic': radius = sp.average_ionic_radius elif self.radius_strategy is 'covalent': el = str(getattr(sp, 'element', sp)) radius = CovalentRadius.radius[el] elif self.radius_strategy is 'van_der_waals': radius = sp.van_der_waals_radius elif self.radius_strategy is 'atomic_calculated': radius = sp.atomic_radius_calculated if not radius: warnings.warn('Radius unknown for {} and strategy {}, ' 'setting to 1.0.'.format( sp, self.radius_strategy)) radius = 1.0 site_radii.append(radius) radii.append(site_radii) self.structure_graph.structure.add_site_property( 'display_radius', radii)
class StructurePredictionBuilder(Builder): def __init__(self, structure_templates, requests, crystals, query=None, **kwargs): """ Predict structures given a list of elements and their oxidation states. Args: structure_templates (Store): store of template structures to predict from requests (Store): store of structure prediction requests crystals (Store): predicted crystal structures and their info (XRD, spacegroup, etc.) """ self.structure_templates = structure_templates self.requests = requests self.crystals = crystals self.query = query if query else {} self.kwargs = kwargs self.auto_oxi = AutoOxiStateDecorationTransformation() super().__init__( sources=[structure_templates, requests], targets=[requests, crystals], **kwargs, ) def get_items(self): """ Gets all structure predictions ready to run Returns: Generator of request and relevant structure templates to run structure prediction tasks """ self.logger.info("Structure Prediction Builder started") requests = self.requests.query(criteria={"state": "READY"}) for request in requests: elements = request["elements"] oxi_states = request["element_oxidation_states"] threshold = request["threshold"] max_num_subs = request["max_num_subs"] original_species = [ Specie(e, o) for e, o in zip(elements, map(int, oxi_states)) ] request["original_species"] = original_species all_chemsys_to_consider = list( self.find_all_chemsys(original_species, threshold, max_num_subs)) self.logger.info( f"Considering the following chemical systems: {all_chemsys_to_consider}" ) templates = [ struct for struct in self.structure_templates.query( {"chemsys": { "$in": all_chemsys_to_consider }}) ] self.logger.info( f"Acquired {len(templates)} structure templates for {original_species}" ) yield {"request": request, "templates": templates} def process_item(self, item): """ Finds all predicted structures for given item Args: item (dict): structure prediction request and relevant oxidation state-labeled structure templates Returns: (dict, dict): A tuple containing updated request doc and a list of crystal docs to update """ request = item["request"] templates = item["templates"] self.logger.info( f"Labeling oxidation states for {len(templates)} structure templates" ) oxi_labeled_templates = [] for template in templates: struct = Structure.from_dict(template["structure"]) try: oxi_labeled_templates.append({ "structure": self.auto_oxi.apply_transformation(struct), "id": template[self.structure_templates.key], }) except: continue # if auto-oxidation fails, try next structure self.logger.info( f"Successfully labeled oxidation states for {len(oxi_labeled_templates)} structures" ) self.logger.info("Substituting original species into structures") predicted_structs = Substitutor( threshold=request["threshold"]).pred_from_structures( request["original_species"], oxi_labeled_templates, remove_duplicates=True, remove_existing=True, ) predicted_structs.sort(key=lambda s: s.other_parameters["proba"], reverse=True) structure_prediction_id = request[self.requests.key] crystal_docs = [] summaries = [] self.logger.info( f"Found {len(predicted_structs)} predicted structures. Generating crystal docs (XRDs, CIFs, etc." ) for number_id, struct in enumerate(predicted_structs): crystal = {} summary = {} xrd_dict = {} final_structure = struct.final_structure sga = SpacegroupAnalyzer(final_structure, symprec=0.1) for rad_source in ["CuKa", "AgKa", "MoKa", "FeKa"]: xrdc = XRDCalculator(wavelength=rad_source) pattern = xrdc.get_pattern(final_structure, two_theta_range=None) xrd_dict[rad_source] = pattern transformed_structure = struct.to_snl( f"{request['name']} <{request['email']}>", remarks=["Created by MP Structure Predictor"], ) crystal[self.requests.key] = structure_prediction_id crystal[self.crystals.key] = number_id crystal["probability"] = struct.other_parameters["proba"] crystal["transformed_structure"] = transformed_structure crystal["xrd"] = xrd_dict crystal["space_group_info"] = { "symbol": sga.get_space_group_symbol(), "number": sga.get_space_group_number(), "hall": sga.get_hall(), "crystal_system": sga.get_crystal_system(), } summary[self.crystals.key] = number_id summary["probability"] = struct.other_parameters["proba"] summary[ "pretty_formula"] = final_structure.composition.reduced_formula summary["nsites"] = len(final_structure) summary["space_group"] = sga.get_space_group_symbol() summary["cif"] = str(CifWriter(final_structure)) crystal_docs.append(jsanitize(crystal, strict=True)) summaries.append(jsanitize(summary, strict=True)) self.logger.info( f"Successfully generated {len(crystal_docs)} crystal docs for request {request['original_species']}" ) request.update({ "state": "COMPLETE", "completed_at": datetime.utcnow(), "num_crystals": len(crystal_docs), "crystals": summaries, }) return request, crystal_docs def update_targets(self, items): """ Update the request doc and insert the predicted crystals into the collection Args: items [(dict, dict)]: A list containing tuples with request, crystal docs to update """ request_docs = [] crystal_docs = [] for item in items: request_docs.append(item[0]) crystal_docs.extend(item[1]) if len(request_docs) > 0: self.logger.info(f"Updating {len(request_docs)} request docs") self.requests.update(request_docs) else: self.logger.info("No requests to update") if len(crystal_docs) > 0: self.logger.info(f"Updating {len(crystal_docs)} crystal docs") self.crystals.update(crystal_docs, key=[self.requests.key, self.crystals.key]) else: self.logger.info("No crystals to update") @staticmethod def find_all_chemsys(original_species, threshold=0.0001, max_num_subs=5): """ Determines chemical systems to consider via data-mined ionic substitution probabilities (see lambda.json) Args: original_species (list): a list of species, e.g. [Specie('Li',1), Specie('Ni',2), Specie('O',-2)] threshold (float): Probability threshold for generating ionic substitutions. Defaults to 0.0001 max_num_subs (int): Limits maximum number of substitutions wanted. Defaults to 5 Returns: (set): A set of all chemical systems to predict from """ num_species = len(original_species) sub_elems = set() for specie in original_species: subs = SubstitutionPredictor(threshold=threshold).list_prediction( [specie]) # sort and cap number of substitutions subs.sort(key=lambda x: x["probability"], reverse=True) subs = subs[0:max_num_subs] for sub in subs: for new_species in sub["substitutions"]: sub_elems.add(new_species.element) all_chemsys = set() for chemsys in combinations(sub_elems, num_species): all_chemsys.add("-".join(sorted([str(el) for el in chemsys]))) return all_chemsys
# j = st.SiteCollection.get_distance(0,4) # j = st.lattice_vectors # print(j) # list = pymatgen.structure_prediction.pred_from_structures(st) # print(dir(sp)) # print(sp.__path__) # print(sp.__doc__) # print(dir(pymatgen.core.structure)) # print(dir(stt.SiteCollection)) # print(sp.__builtins__) # print(dir(test)) # i = test.SubstitutionPredictor(st.composition) Subs = substitutor.Substitutor() # Subs._threshold = 0.001 # Any threshold beyond 0.001 brings it out of the default hence it doesn't work: has to figure out how to explicitly set the threshold probability struc = AutoOxiStateDecorationTransformation().apply_transformation(st) spgr = struc.get_spacegroup_info() print(struc.get_spacegroup_info()) # print(struc) cations = ['Co', 'Rh', 'Ir'] #cations = ['Li', 'Na', 'K', 'Rb', 'Cs'] # cations2 = ['C','Si','Ge','Sn', 'Pb'] anions = ['F', 'Cl', 'Br', 'I'] counter = 0 for cation in cations: # for cation2 in cations2: for anion in anions: try: # list = Subs.pred_from_structures(target_species=[Specie(cation, +1), Specie(cation2, +2), Specie(anion, -1)], structures_list=[{'structure': struc, 'id': 'TEST'}]) list = Subs.pred_from_structures(target_species=[Specie(cation, +3), Specie(anion, -1)], structures_list=[{'structure': struc, 'id': 'TEST'}])