def _get_graphs(cutoff, ordered_structures): """ Generate graph representations of magnetic structures with nearest neighbor bonds. Right now this only works for MinimumDistanceNN. Args: cutoff (float): Cutoff in Angstrom for nearest neighbor search. ordered_structures (list): Structure objects. Returns: sgraphs (list): StructureGraph objects. """ # Strategy for finding neighbors if cutoff: strategy = MinimumDistanceNN(cutoff=cutoff, get_all_sites=True) else: strategy = MinimumDistanceNN() # only NN # Generate structure graphs sgraphs = [ StructureGraph.with_local_env_strategy(s, strategy=strategy) for s in ordered_structures ] return sgraphs
def test_get_local_order_params(self): nn = MinimumDistanceNN() ops = nn.get_local_order_parameters(self.diamond, 0) self.assertAlmostEqual(ops['tetrahedral'], 0.9999934389036574) ops = nn.get_local_order_parameters(self.nacl, 0) self.assertAlmostEqual(ops['octahedral'], 0.9999995266669)
def test_substitute(self): structure = Structure.from_file(os.path.join(os.path.dirname(__file__), "..", "..", "..", "test_files", "Li2O.cif")) molecule = FunctionalGroups["methyl"] structure_copy = copy.deepcopy(structure) structure_copy_graph = copy.deepcopy(structure) sg = StructureGraph.with_local_env_strategy(structure, MinimumDistanceNN()) sg_copy = copy.deepcopy(sg) # Ensure that strings and molecules lead to equivalent substitutions sg.substitute_group(1, molecule, MinimumDistanceNN) sg_copy.substitute_group(1, "methyl", MinimumDistanceNN) self.assertEqual(sg, sg_copy) # Ensure that the underlying structure has been modified as expected structure_copy.substitute(1, "methyl") self.assertEqual(structure_copy, sg.structure) # Test inclusion of graph dictionary graph_dict = {(0, 1): {"weight": 0.5}, (0, 2): {"weight": 0.5}, (0, 3): {"weight": 0.5}, } sg_with_graph = StructureGraph.with_local_env_strategy(structure_copy_graph, MinimumDistanceNN()) sg_with_graph.substitute_group(1, "methyl", MinimumDistanceNN, graph_dict=graph_dict) edge = sg_with_graph.graph.get_edge_data(11, 13)[0] self.assertEqual(edge["weight"], 0.5)
def shifted_sites_after_prismatic_disto(struct, metal_index, angle): mini = MinimumDistanceNN(tol=0.3) print("rotating around index : ", metal_index) neighbor_O = mini.get_nn_info(struct, metal_index) [X, Y, Z] = struct[metal_index].coords print("metal coords : ", [X, Y, Z]) trueSites = [] # list of dict with index and shifted coordinates O_above = [O['site_index'] for O in neighbor_O if O['site'].z - Z > 0] O_below = [O['site_index'] for O in neighbor_O if O['site'].z - Z < 0] symOperation = find_symmop(struct, angle, translation=(0, 0, 0), center=[X, Y, Z]) trueSites += find_translated_sites(struct, O_above, symOperation) symOperation = find_symmop(struct, -angle, translation=(0, 0, 0), center=[X, Y, Z]) trueSites += find_translated_sites(struct, O_below, symOperation) return (trueSites)
def test_mul(self): square_sg_mul = self.square_sg * (2, 1, 1) square_sg_mul_ref_str = """Structure Graph Structure: Full Formula (H2) Reduced Formula: H2 abc : 10.000000 5.000000 50.000000 angles: 90.000000 90.000000 90.000000 Sites (2) # SP a b c --- ---- --- --- --- 0 H 0 0 0 1 H 0.5 0 -0 Graph: bonds from to to_image ---- ---- ------------ 0 0 (0, 1, 0) 0 0 (0, -1, 0) 0 1 (0, 0, 0) 0 1 (-1, 0, 0) 1 1 (0, 1, 0) 1 1 (0, -1, 0) """ square_sg_mul_actual_str = str(square_sg_mul) # only testing bonds portion, # the c frac_coord of the second H can vary from # 0 to -0 depending on machine precision square_sg_mul_ref_str = "\n".join( square_sg_mul_ref_str.splitlines()[11:]) square_sg_mul_actual_str = "\n".join( square_sg_mul_actual_str.splitlines()[11:]) self.assertEqual(square_sg_mul_actual_str, square_sg_mul_ref_str) # test sequential multiplication sq_sg_1 = self.square_sg * (2, 2, 1) sq_sg_1 = sq_sg_1 * (2, 2, 1) sq_sg_2 = self.square_sg * (4, 4, 1) self.assertEqual(sq_sg_1.graph.number_of_edges(), sq_sg_2.graph.number_of_edges()) mos2_sg_mul = self.mos2_sg * (3, 3, 1) for idx in mos2_sg_mul.structure.indices_from_symbol("Mo"): self.assertEqual(mos2_sg_mul.get_coordination_of_site(idx), 6) mos2_sg_premul = StructureGraph.with_local_env_strategy( self.structure * (3, 3, 1), MinimumDistanceNN()) self.assertTrue(mos2_sg_mul == mos2_sg_premul) # test 3D Structure nio_sg = StructureGraph.with_local_env_strategy( self.NiO, MinimumDistanceNN()) nio_sg = nio_sg * 3 for n in range(len(nio_sg)): self.assertEqual(nio_sg.get_coordination_of_site(n), 6)
def test_get_local_order_params(self): nn = MinimumDistanceNN() ops = nn.get_local_order_parameters(self.diamond, 0) self.assertAlmostEqual(ops['tetrahedral'], 0.9999934389036574) ops = nn.get_local_order_parameters(self.nacl, 0) self.assertAlmostEqual(ops['octahedral'], 0.9999995266669)
def get_MO_pairs(struct, metal_str="Mn"): print("getting {} - O pairs".format(metal_str)) atom_pair_list = [] mini = MinimumDistanceNN(tol=0.3) for center_index in struct.indices_from_symbol(metal_str): neighbor_O = mini.get_nn_info(struct, center_index) atom_pair_list += [[center_index, O['site_index']] for O in neighbor_O if O['site'].species_string in ["O", "S"]] return (atom_pair_list)
def test_all_nn_classes(self): self.assertAlmostEqual(MinimumDistanceNN().get_cn(self.diamond, 0), 4) self.assertAlmostEqual(MinimumDistanceNN().get_cn(self.nacl, 0), 6) self.assertAlmostEqual( MinimumDistanceNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( MinimumDistanceNN(tol=0.1).get_cn(self.mos2, 0), 6) for image in MinimumDistanceNN(tol=0.1).get_nn_images(self.mos2, 0): self.assertTrue(image in [(0, 0, 0), (0, 1, 0), (-1, 0, 0), (0, 0, 0), (0, 1, 0), (-1, 0, 0)]) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( MinimumVIRENN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(MinimumVIRENN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(MinimumVIRENN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( BrunnerNN_reciprocal(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual( BrunnerNN_reciprocal(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual( BrunnerNN_reciprocal(tol=0.01).get_cn(self.cscl, 0), 14) self.assertAlmostEqual( BrunnerNN_relative(tol=0.01).get_cn(self.diamond, 0), 16) self.assertAlmostEqual( BrunnerNN_relative(tol=0.01).get_cn(self.nacl, 0), 18) self.assertAlmostEqual( BrunnerNN_relative(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( BrunnerNN_real(tol=0.01).get_cn(self.diamond, 0), 16) self.assertAlmostEqual( BrunnerNN_real(tol=0.01).get_cn(self.nacl, 0), 18) self.assertAlmostEqual( BrunnerNN_real(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual(EconNN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(EconNN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(EconNN(tol=0.01).get_cn(self.cscl, 0), 14) self.assertAlmostEqual(VoronoiNN(tol=0.5).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(VoronoiNN(tol=0.5).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(VoronoiNN(tol=0.5).get_cn(self.cscl, 0), 8)
def metal_envt(struc): # Counts the Me environment in a structure # argument : pymatgen.core.structure # return : 2elt list of (7 element list) : the i th element is the # propotion of A with i A neighbors A = "Mg" B = "Mn" C = "Na" # neighborA = [0 for i in range(0, 7, 1)] # neighborB = [0 for i in range(0, 7, 1)] # A_indices = struc.indices_from_symbol(A) nbC = len(struc.indices_from_symbol(C)) neighborC = np.zeros(6) #print("{0} {1}".format(nbC,C)) sCopy = RemoveSpeciesTransformation(["O"]).apply_transformation(struc) # print(sCopy) mini = MinimumDistanceNN(tol=0.3) for i, site in enumerate(sCopy): # print(str(i)) # print(site) siteName = site.species_string if siteName == C: neigList = mini.get_nn_info(sCopy, i) #print("{0} closest neighboors \n".format(len(neigList))) coordA = 0 coordB = 0 for neighbor in neigList: # index = neighbor['site_index'] neighborName = neighbor['site'].species_string #print(" ( {0} at {1} ) ".format(neighborName,index)) if neighborName == A: coordA += 1 if neighborName == B: coordB += 1 neighborC[coordA] += 1 #print("neighborC list :" , neighborC) if nbC == 0: normalizedNeighborC = [0 for i in neighborC] else: normalizedNeighborC = [i / nbC for i in neighborC] print("coordination {0} : {1} \nnormalized by {2} : {3} \n".format( C, neighborC, nbC, normalizedNeighborC)) return normalizedNeighborC
def get_layer_indices(struct, separator_specie, refIndex=-1): # fonction to gather the indexes of all the atoms in layers separated by Na atoms # input : the layered structure, the index of one non-Na reference atom # returns : an ordered list of indexes of all the atoms in the layer of # the reference atom A = "Mn" B = "Mg" C = "O" # if no reference is given, takes the first atom of A as a reference if refIndex == -1: layerIndices = [struct.indices_from_symbol(C)[0]] else: layerIndices = [refIndex] mini = MinimumDistanceNN(tol=0.5) # algorithm : 2 buffer list (prev and new atomList) and a definitive list # adds all neighbor of the old buffer atoms to a new buffer list # if they are non-Na and non-already counted in the definitive List # Stops when the new buffer is empty (all non-Na atoms next to the layer # have been counted) prevAtomList = layerIndices finish = False while not finish: newAtomList = [] for prevAtomIndex in prevAtomList: neigList = mini.get_nn_info(struct, prevAtomIndex) for neighbor in neigList: index = neighbor['site_index'] name = neighbor['site'].species_string # print(name) if not (name in separator_specie + [struct[prevAtomIndex].species_string]) and not ( index in layerIndices): layerIndices.append(index) if len(newAtomList) > 0: previousAtomList = newAtomList layerIndices = layerIndices + newAtomList else: finish = True layerIndices.sort() return layerIndices
def test_all_nn_classes(self): self.assertAlmostEqual(MinimumDistanceNN().get_cn(self.diamond, 0), 4) self.assertAlmostEqual(MinimumDistanceNN().get_cn(self.nacl, 0), 6) self.assertAlmostEqual( MinimumDistanceNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( MinimumDistanceNN(tol=0.1).get_cn(self.mos2, 0), 6) for image in MinimumDistanceNN(tol=0.1).get_nn_images(self.mos2, 0): self.assertTrue(image in [[0, 0, 0], [0, 1, 0], [-1, 0, 0], \ [0, 0, 0], [0, 1, 0], [-1, 0, 0]]) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( MinimumVIRENN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(MinimumVIRENN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(MinimumVIRENN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual(BrunnerNN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(BrunnerNN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(BrunnerNN(tol=0.01).get_cn(self.cscl, 0), 14) self.assertAlmostEqual( BrunnerNN(mode="real", tol=0.01).get_cn(self.diamond, 0), 16) self.assertAlmostEqual( BrunnerNN(mode="real", tol=0.01).get_cn(self.nacl, 0), 18) self.assertAlmostEqual( BrunnerNN(mode="real", tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( BrunnerNN(mode="relative", tol=0.01).get_cn(self.diamond, 0), 16) self.assertAlmostEqual( BrunnerNN(mode="relative", tol=0.01).get_cn(self.nacl, 0), 18) self.assertAlmostEqual( BrunnerNN(mode="relative", tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual(EconNN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(EconNN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(EconNN(tol=0.01).get_cn(self.cscl, 0), 14) self.assertAlmostEqual(VoronoiNN_modified().get_cn(self.diamond, 0), 4) self.assertAlmostEqual(VoronoiNN_modified().get_cn(self.nacl, 0), 6) self.assertAlmostEqual(VoronoiNN_modified().get_cn(self.cscl, 0), 8)
def get_local_env_method(method): # pylint:disable=too-many-return-statements """get a local environment method based on its name""" method = method.lower() if method.lower() == "crystalnn": # see eq. 15 and 16 in # https://pubs.acs.org/doi/full/10.1021/acs.inorgchem.0c02996 # for the x_diff_weight parameter. # in the paper it is called δen and it is set to 3 # we found better results by lowering this weight return CrystalNN(porous_adjustment=True, x_diff_weight=1.5, search_cutoff=4.5) if method.lower() == "econnn": return EconNN() if method.lower() == "brunnernn": return BrunnerNN_relative() if method.lower() == "minimumdistance": return MinimumDistanceNN() if method.lower() == "vesta": return VESTA_NN if method.lower() == "voronoinn": return VoronoiNN() if method.lower() == "atr": return ATR_NN if method.lower() == "li": return LI_NN return JmolNN()
def scene_dicts(): structure = Structure.from_file(str(Path(__file__).parent / "mgo_defects" / "Va_O1_0" / "CONTCAR-finish")) graph = MinimumDistanceNN().get_bonded_structure(structure=structure) return SceneDicts( {"125_up": {"vertices": np.array([[0] * 3, [0.1] * 3, [0.2] * 3]), "faces": np.array([[0, 1, 2]])}}, structure_graph=graph)
def test_no_duplicate_hops(self): test_structure_dict = { "@module": "pymatgen.core.structure", "@class": "Structure", "charge": None, "lattice": { "matrix": [[2.990355, -5.149042, 0.0], [2.990355, 5.149042, 0.0], [0.0, 0.0, 24.51998]] }, "sites": [ { "species": [{ "element": "Ba", "occu": 1 }], "abc": [0.005572, 0.994428, 0.151095], "properties": {} }, ], } test_structure = Structure.from_dict(test_structure_dict) nn = MinimumDistanceNN(cutoff=6, get_all_sites=True) sg = StructureGraph.with_local_env_strategy(test_structure, nn) self.assertEqual(sg.graph.number_of_edges(), 3)
def __init__(self, structure, migrating_specie, max_path_length=10, symprec=0.1, vac_mode=False): """ Args: structure: Input structure that contains all sites. migrating_specie (Specie-like): The specie that migrates. E.g., "Li". max_path_length (float): Maximum length of NEB path in the unit of Angstrom. Defaults to None, which means you are setting the value to the min cutoff until finding 1D or >1D percolating paths. symprec (float): Symmetry precision to determine equivalence. """ self.structure = structure self.migrating_specie = get_el_sp(migrating_specie) self.symprec = symprec self.a = SpacegroupAnalyzer(self.structure, symprec=self.symprec) self.symm_structure = self.a.get_symmetrized_structure() self.only_sites = self.get_only_sites() self.unique_hops = None # Generate the graph edges between these all the sites self.s_graph = StructureGraph.with_local_env_strategy( self.only_sites, MinimumDistanceNN( cutoff=max_path_length, get_all_sites=True)) # weights in this graph are the distances self.s_graph.set_node_attributes()
def test_all_nn_classes(self): self.assertEqual(MinimumDistanceNN(cutoff=5, get_all_sites=True).get_cn( self.cscl, 0), 14) self.assertEqual(MinimumDistanceNN().get_cn(self.diamond, 0), 4) self.assertEqual(MinimumDistanceNN().get_cn(self.nacl, 0), 6) self.assertEqual(MinimumDistanceNN().get_cn(self.lifepo4, 0), 6) self.assertEqual(MinimumDistanceNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertEqual(MinimumDistanceNN(tol=0.1).get_cn(self.mos2, 0), 6) for image in MinimumDistanceNN(tol=0.1).get_nn_images(self.mos2, 0): self.assertTrue(image in [(0, 0, 0), (0, 1, 0), (-1, 0, 0), (0, 0, 0), (0, 1, 0), (-1, 0, 0)]) okeeffe = MinimumOKeeffeNN(tol=0.01) self.assertEqual(okeeffe.get_cn(self.diamond, 0), 4) self.assertEqual(okeeffe.get_cn(self.nacl, 0), 6) self.assertEqual(okeeffe.get_cn(self.cscl, 0), 8) self.assertEqual(okeeffe.get_cn(self.lifepo4, 0), 2) virenn = MinimumVIRENN(tol=0.01) self.assertEqual(virenn.get_cn(self.diamond, 0), 4) self.assertEqual(virenn.get_cn(self.nacl, 0), 6) self.assertEqual(virenn.get_cn(self.cscl, 0), 8) self.assertEqual(virenn.get_cn(self.lifepo4, 0), 2) brunner_recip = BrunnerNN_reciprocal(tol=0.01) self.assertEqual(brunner_recip.get_cn(self.diamond, 0), 4) self.assertEqual(brunner_recip.get_cn(self.nacl, 0), 6) self.assertEqual(brunner_recip.get_cn(self.cscl, 0), 14) self.assertEqual(brunner_recip.get_cn(self.lifepo4, 0), 6) brunner_rel = BrunnerNN_relative(tol=0.01) self.assertEqual(brunner_rel.get_cn(self.diamond, 0), 4) self.assertEqual(brunner_rel.get_cn(self.nacl, 0), 6) self.assertEqual(brunner_rel.get_cn(self.cscl, 0), 14) self.assertEqual(brunner_rel.get_cn(self.lifepo4, 0), 6) brunner_real = BrunnerNN_real(tol=0.01) self.assertEqual(brunner_real.get_cn(self.diamond, 0), 4) self.assertEqual(brunner_real.get_cn(self.nacl, 0), 6) self.assertEqual(brunner_real.get_cn(self.cscl, 0), 14) self.assertEqual(brunner_real.get_cn(self.lifepo4, 0), 30) econn = EconNN() self.assertEqual(econn.get_cn(self.diamond, 0), 4) self.assertEqual(econn.get_cn(self.nacl, 0), 6) self.assertEqual(econn.get_cn(self.cscl, 0), 14) self.assertEqual(econn.get_cn(self.lifepo4, 0), 6) voroinn = VoronoiNN(tol=0.5) self.assertEqual(voroinn.get_cn(self.diamond, 0), 4) self.assertEqual(voroinn.get_cn(self.nacl, 0), 6) self.assertEqual(voroinn.get_cn(self.cscl, 0), 8) self.assertEqual(voroinn.get_cn(self.lifepo4, 0), 6) crystalnn = CrystalNN() self.assertEqual(crystalnn.get_cn(self.diamond, 0), 4) self.assertEqual(crystalnn.get_cn(self.nacl, 0), 6) self.assertEqual(crystalnn.get_cn(self.cscl, 0), 8) self.assertEqual(crystalnn.get_cn(self.lifepo4, 0), 6)
def get_metal_next_neighbor(struct, center_index): next_metal_neighbors = [] # gather the indices of all metal next neighbors # metals around neighbor of neighbor Os mini = MinimumDistanceNN(tol=0.3) neighbor_O = mini.get_nn_info(struct, center_index) for O_index in [O['site_index'] for O in neighbor_O]: next_metal_neighbors += [ M['site_index'] for M in mini.get_nn_info(struct, O_index) if (M['site'].specie.symbol not in ["Na", "Li", "O"]) ] # remove the doubles and the metal center next_metal_neighbors = set(next_metal_neighbors) next_metal_neighbors.discard(center_index) return (next_metal_neighbors)
def test_draw(self): # draw MoS2 graph self.mos2_sg.draw_graph_to_file('MoS2_single.pdf', image_labels=True, hide_image_edges=False) mos2_sg = self.mos2_sg * (9, 9, 1) mos2_sg.draw_graph_to_file('MoS2.pdf', algo='neato') # draw MoS2 graph that's been successively multiplied mos2_sg_2 = self.mos2_sg * (3, 3, 1) mos2_sg_2 = mos2_sg_2 * (3, 3, 1) mos2_sg_2.draw_graph_to_file('MoS2_twice_mul.pdf', algo='neato', hide_image_edges=True) # draw MoS2 graph that's generated from a pre-multiplied Structure mos2_sg_premul = StructureGraph.with_local_env_strategy( self.structure * (3, 3, 1), MinimumDistanceNN()) mos2_sg_premul.draw_graph_to_file('MoS2_premul.pdf', algo='neato', hide_image_edges=True) # draw graph for a square lattice self.square_sg.draw_graph_to_file('square_single.pdf', hide_image_edges=False) square_sg = self.square_sg * (5, 5, 1) square_sg.draw_graph_to_file('square.pdf', algo='neato', image_labels=True, node_labels=False) # draw graph for a body-centered square lattice self.bc_square_sg.draw_graph_to_file('bc_square_single.pdf', hide_image_edges=False) bc_square_sg = self.bc_square_sg * (9, 9, 1) bc_square_sg.draw_graph_to_file('bc_square.pdf', algo='neato', image_labels=False) # draw graph for a body-centered square lattice defined in an alternative way self.bc_square_sg_r.draw_graph_to_file('bc_square_r_single.pdf', hide_image_edges=False) bc_square_sg_r = self.bc_square_sg_r * (9, 9, 1) bc_square_sg_r.draw_graph_to_file('bc_square_r.pdf', algo='neato', image_labels=False) # delete generated test files test_files = ('bc_square_r_single.pdf', 'bc_square_r.pdf', 'bc_square_single.pdf', 'bc_square.pdf', 'MoS2_premul.pdf', 'MOS2_single.pdf', 'MoS2_twice_mul.pdf', 'MoS2.pdf', 'square_single.pdf', 'square.pdf') for test_file in test_files: os.remove(test_file)
def test_all_nn_classes(self): self.assertAlmostEqual(MinimumDistanceNN().get_cn(self.diamond, 0), 4) self.assertAlmostEqual(MinimumDistanceNN().get_cn(self.nacl, 0), 6) self.assertAlmostEqual( MinimumDistanceNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( MinimumDistanceNN(tol=0.1).get_cn(self.mos2, 0), 6) for image in MinimumDistanceNN(tol=0.1).get_nn_images(self.mos2, 0): self.assertTrue(image in [[0, 0, 0], [0, 1, 0], [-1, 0, 0], \ [0, 0, 0], [0, 1, 0], [-1, 0, 0]]) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual( MinimumOKeeffeNN(tol=0.01).get_cn(self.cscl, 0), 8) self.assertAlmostEqual( MinimumVIRENN(tol=0.01).get_cn(self.diamond, 0), 4) self.assertAlmostEqual(MinimumVIRENN(tol=0.01).get_cn(self.nacl, 0), 6) self.assertAlmostEqual(MinimumVIRENN(tol=0.01).get_cn(self.cscl, 0), 8)
def test_no_duplicate_hops(self): test_structure = Structure( lattice=[[2.990355, -5.149042, 0.0], [2.990355, 5.149042, 0.0], [0.0, 0.0, 24.51998]], species=["Ba"], coords=[[0.005572, 0.994428, 0.151095]], ) nn = MinimumDistanceNN(cutoff=6, get_all_sites=True) sg = StructureGraph.with_local_env_strategy(test_structure, nn) self.assertEqual(sg.graph.number_of_edges(), 3)
def test_from_local_env_and_equality_and_diff(self): nn = MinimumDistanceNN() sg = StructureGraph.with_local_env_strategy(self.structure, nn) self.assertEqual(sg.graph.number_of_edges(), 6) nn2 = MinimumOKeeffeNN() sg2 = StructureGraph.with_local_env_strategy(self.structure, nn2) self.assertTrue(sg == sg2) self.assertTrue(sg == self.mos2_sg) # TODO: find better test case where graphs are different diff = sg.diff(sg2) self.assertEqual(diff['dist'], 0)
def test_extract_molecules(self): structure_file = os.path.join(os.path.dirname(__file__), "..", "..", "..", 'test_files/H6PbCI3N_mp-977013_symmetrized.cif') s = Structure.from_file(structure_file) nn = MinimumDistanceNN() sg = StructureGraph.with_local_env_strategy(s, nn) molecules = sg.get_subgraphs_as_molecules() self.assertEqual(molecules[0].composition.formula, "H3 C1") self.assertEqual(len(molecules), 1) molecules = self.mos2_sg.get_subgraphs_as_molecules() self.assertEqual(len(molecules), 0)
def with_distance(cls, structure: Structure, migrating_specie: str, max_distance: float, **kwargs) -> "MigrationGraph": """ Using a specific nn strategy to get the connectivity graph between all the migrating ion sites. Args: max_distance: Maximum length of NEB path in the unit of Angstrom. Defaults to None, which means you are setting the value to the min cutoff until finding 1D or >1D percolating paths. Returns: A constructed MigrationGraph object """ only_sites = get_only_sites_from_structure(structure, migrating_specie) migration_graph = StructureGraph.with_local_env_strategy( only_sites, MinimumDistanceNN(cutoff=max_distance, get_all_sites=True), ) return cls(structure=structure, m_graph=migration_graph, **kwargs)
def __init__( self, structure, migrating_specie, max_path_length=10, symprec=0.1, vac_mode=False, name: str = None, ): """ Construct the FullPathMapper object using a structure will all mobile sites occupied. A structure graph is generated by connecting all sites withing max_path_length distance of each other The sites are decorated with Migration graph objects and then grouped together based on their equivalence. Args: structure: Input structure that contains all sites. migrating_specie (Specie-like): The specie that migrates. E.g., "Li". max_path_length (float): Maximum length of NEB path in the unit of Angstrom. Defaults to None, which means you are setting the value to the min cutoff until finding 1D or >1D percolating paths. symprec (float): Symmetry precision to determine equivalence. """ self.structure = structure self.migrating_specie = get_el_sp(migrating_specie) self.max_path_length = max_path_length self.symprec = symprec self.name = name self.a = SpacegroupAnalyzer(self.structure, symprec=self.symprec) self.symm_structure = self.a.get_symmetrized_structure() self.only_sites = self.get_only_sites() if vac_mode: raise NotImplementedError self.vac_mode = vac_mode self.unique_hops = None # Generate the graph edges between these all the sites self.s_graph = StructureGraph.with_local_env_strategy( self.only_sites, MinimumDistanceNN(cutoff=max_path_length, get_all_sites=True), ) # weights in this graph are the distances self.s_graph.set_node_attributes() self.populate_edges_with_migration_paths() self.group_and_label_hops() self._populate_unique_hops_dict()
def test_extract_molecules(self): structure_file = os.path.join( PymatgenTest.TEST_FILES_DIR, "H6PbCI3N_mp-977013_symmetrized.cif", ) s = Structure.from_file(structure_file) nn = MinimumDistanceNN() sg = StructureGraph.with_local_env_strategy(s, nn) molecules = sg.get_subgraphs_as_molecules() self.assertEqual(molecules[0].composition.formula, "H3 C1") self.assertEqual(len(molecules), 1) molecules = self.mos2_sg.get_subgraphs_as_molecules() self.assertEqual(len(molecules), 0)
def apply_transformation(self, structure): """ Apply the transformation. Args: structure: Structure or Molecule to apply the transformation to Returns: the transformed structure """ structure = structure.copy() site = structure[self.site_index] def f(x, r, r0): return x * r0 / r r0 = max([ site.distance(_["site"]) for _ in MinimumDistanceNN().get_nn_info( structure, self.site_index) ]) if hasattr(structure, "lattice"): m = structure.lattice.matrix m = (abs(m) > 1e-5) * m a, b, c = m[0], m[1], m[2] x = abs(np.dot(a, np.cross(b, c)) / np.linalg.norm(np.cross(b, c))) y = abs(np.dot(b, np.cross(a, c)) / np.linalg.norm(np.cross(a, c))) z = abs(np.dot(c, np.cross(a, b)) / np.linalg.norm(np.cross(a, b))) rmax = np.floor(min([x, y, z]) / 2) else: rmax = np.max(structure.distance_matrix) for vals in structure.get_neighbors(site, r=r0 if self.nn_only else rmax): site2, distance, index = vals[:3] v = site2.coords - site.coords kwargs = { "indices": [index], "vector": v * f(self.displacement, distance, r0) / np.linalg.norm(v) } if hasattr(structure, "lattice"): kwargs["frac_coords"] = False structure.translate_sites(**kwargs) return structure
def get_neighbors_of_site_with_index_future(struct, n, approach="min_dist", \ delta=0.1, cutoff=10.0): """ Returns the neighbors of a given site using a specific neighbor-finding method. Args: struct (Structure): input structure. n (int): index of site in Structure object for which motif type is to be determined. approach (str): type of neighbor-finding approach, where "min_dist" will use the MinimumDistanceNN class, "voronoi" the VoronoiNN class, "min_OKeeffe" the MinimumOKeeffe class, and "min_VIRE" the MinimumVIRENN class. delta (float): tolerance involved in neighbor finding. cutoff (float): (large) radius to find tentative neighbors. Returns: neighbor sites. """ warnings.warn('This function will go into Pymatgen very soon.') if approach == "min_dist": return MinimumDistanceNN(tol=delta, cutoff=cutoff).get_nn( struct, n) elif approach == "voronoi": return VoronoiNN(tol=delta, cutoff=cutoff).get_nn( struct, n) elif approach == "min_OKeeffe": return MinimumOKeeffeNN(tol=delta, cutoff=cutoff).get_nn( struct, n) elif approach == "min_VIRE": return MinimumVIRENN(tol=delta, cutoff=cutoff).get_nn( struct, n) else: raise RuntimeError("unsupported neighbor-finding method ({}).".format( approach))
def __init__( self, structure, structure_graph=None, larsen_dim=None, cheon_dim=None, gorai_dim=None, ): """ This class uses 3 algorithms implemented in pymatgen to automate recognition of low-dimensional materials. Args: structure (object): pmg Structure object. structure_graph (object): pmg StructureGraph object. larsen_dim (int): As defined in P. M. Larsen et al., Phys. Rev. Materials 3, 034003 (2019). cheon_dim (int): As defined in Cheon, G. et al., Nano Lett. 2017. gorai_dim (int): As defined in Gorai, P. et al., J. Mater. Chem. A 2, 4136 (2016). """ self.structure = structure self.structure_graph = structure_graph self.larsen_dim = larsen_dim self.cheon_dim = cheon_dim self.gorai_dim = gorai_dim # Default to MinimumDistanceNN for generating structure graph. sgraph = MinimumDistanceNN().get_bonded_structure(structure) self.structure_graph = sgraph # Get Larsen dimensionality self.larsen_dim = get_dimensionality_larsen(self.structure_graph)
def get_MMOO_quadruplets(struct): AB_list = get_Mn_Mn_pairs(struct) print("nb of pairs to test : {}".format(len(AB_list))) quadruplets = [] for A, B in AB_list: mini = MinimumDistanceNN(tol=0.35) O_A_indices = [O['site_index'] for O in mini.get_nn_info(struct, A)] O_pair = [ O['site_index'] for O in mini.get_nn_info(struct, B) if O["site_index"] in O_A_indices ] # O_pair = O_A_indices & O_B_indices nb_O = len(O_pair) if nb_O == 2: quadruplets.append({"metal_pair": [A, B], "oxygen_pair": O_pair}) elif nb_O > 2: O_pair_dict = [ O for O in mini.get_nn_info(struct, B) if O['site_index'] in O_pair ] print("common O between atoms {} are not a pair : {}".format( [A, B], O_pair)) for O in O_pair_dict: dA, imgA = O["site"].distance_and_image(struct[B], jimage=None) dB, imgB = O["site"].distance_and_image(struct[A], jimage=None) # print(imgA,imgB) O["hash"] = hash((tuple(imgA), tuple(imgB))) #print("O site : {}, dist : {}".format(O["site_index"],dA+dB)) O_pair = [ O["site_index"] for O in sorted(O_pair, key=itemgetter('hash')) ] print("sorted O list : {}".format( [dictA["site_index"] for dictA in O_pair])) while len(O_pair) > 0: O1 = O_pair.pop() O2 = O_pair.pop() quadruplets.append({ "metal_pair": [A, B], "oxygen_pair": [O1, O2] }) else: print("Not enough O in the pair !!") print("Valid MMOO quadruplets : {}".format(len(quadruplets))) return (quadruplets)
def get_NNs_pm(atoms, site_idx, NN_method): """ Get coordinating atoms to the adsorption site Args: atoms (Atoms object): atoms object of MOF site_idx (int): ASE index of adsorption site NN_method (string): string representing the desired Pymatgen nearest neighbor algorithm: refer to http://pymatgen.org/_modules/pymatgen/analysis/local_env.html Returns: neighbors_idx (list of ints): ASE indices of coordinating atoms """ #Convert ASE Atoms object to Pymatgen Structure object bridge = pm_ase.AseAtomsAdaptor() struct = bridge.get_structure(atoms) #Select Pymatgen NN algorithm NN_method = NN_method.lower() if NN_method == 'vire': nn_object = MinimumVIRENN() elif NN_method == 'voronoi': nn_object = VoronoiNN() elif NN_method == 'jmol': nn_object = JmolNN() elif NN_method == 'min_dist': nn_object = MinimumDistanceNN() elif NN_method == 'okeeffe': nn_object = MinimumOKeeffeNN() elif NN_method == 'brunner_real': nn_object = BrunnerNN_real() elif NN_method == 'brunner_recpirocal': nn_object = BrunnerNN_reciprocal() elif NN_method == 'brunner_relative': nn_object = BrunnerNN_relative() elif NN_method == 'econ': nn_object = EconNN() elif NN_method == 'dict': #requires a cutoff dictionary located in the pwd nn_object = CutOffDictNN(cut_off_dict='cut_off_dict.txt') elif NN_method == 'critic2': nn_object = Critic2NN() elif NN_method == 'openbabel': nn_object = OpenBabelNN() elif NN_method == 'covalent': nn_object = CovalentBondNN() elif NN_method == 'crystal': nn_object = CrystalNN(porous_adjustment=True) elif NN_method == 'crystal_nonporous': nn_object = CrystalNN(porous_adjustment=False) else: raise ValueError('Invalid NN algorithm specified') #Find coordinating atoms with warnings.catch_warnings(): warnings.simplefilter('ignore') neighbors = nn_object.get_nn_info(struct, site_idx) neighbors_idx = [] for neighbor in neighbors: neighbors_idx.append(neighbor['site_index']) return neighbors_idx
def metalBondCount(struc): # Counts the metal metal bounds in a structure # argument pymatgen.core.structure # return [ normalizedBonds, normalizedAA, normalizedBB] # normalizedBond = 3 elt list =[ nb AA bonds, nb BB bonds, nb AB bonds ] / nb of metal # normalizedAA = 7 element list : the ith elt is the number of A with i A-neighbor # (ie if there are 1/4 of the Mg surrounded by 3 Mg, normalizedAA[3] = 0.25) # normalizedBB = idem for B # Logically, if nbA = nbB, normalizedAA = normalizedBB A = "Mg" B = "Mn" countAA = 0 countAB = 0 countBB = 0 neighborAA = [0 for i in range(7)] neighborBB = [0 for i in range(7)] A_indices = struc.indices_from_symbol(A) B_indices = struc.indices_from_symbol(B) nbA = len(A_indices) nbB = len(B_indices) nbTot = nbA + nbB print("{0} {1}, {2} {3} : {4}".format(nbA, A, nbB, B, nbTot)) sCopy = RemoveSpeciesTransformation(["Na", "O"]).apply_transformation(struc) # struc.remove_species('O') mini = MinimumDistanceNN(tol=0.3) for i, site in enumerate(sCopy): # print(str(i)) neigList = mini.get_nn_info(sCopy, i) #print("{0} closest neighboors \n".format(len(neigList))) coord = 0 for neighbor in neigList: index = neighbor['site_index'] name = neighbor['site'].species_string # print(" ( {0} at {1} ) ".format(name,index)) if site.species_string == A: if name == A: countAA += 1 coord += 1 if name == B: countAB += 1 if site.species_string == B: if name == A: countAB += 1 if name == B: countBB += 1 coord += 1 if site.species_string == A: neighborAA[coord] += 1 elif site.species_string == B: neighborBB[coord] += 1 bonds = [countAA // 2, countBB // 2, countAB // 2] normalizedBonds = [i / nbTot for i in bonds] print("[{3}{3}, {4}{4}, {3}{4}] = {0} normalized by {1} : {2} \n".format( bonds, nbTot, normalizedBonds, A, B)) if nbA > 0: normalizedAA = [i / nbA for i in neighborAA] print( "[Nombre de {0} avec [1-6] {0} autour] = {1} normalized by {2} : {3} \n" .format(A, neighborAA, nbA, normalizedAA)) else: normalizedAA = neighborAA if nbB > 0: normalizedBB = [i / nbB for i in neighborBB] print( "[Nombre de {0} avec [1-6] {0} autour] = {1} normalized by {2} : {3} \n" .format(B, neighborBB, nbB, normalizedBB)) else: normalizedBB = neighborBB return [normalizedBonds, normalizedAA, normalizedBB]