def analyse_local_coord_env(atoms=None, ): """ """ #| - analyse_local_coord_env out_dict = dict() Ir_indices = [] for i, s in enumerate(atoms.get_chemical_symbols()): if s == "Ir": Ir_indices.append(i) struct = AseAtomsAdaptor.get_structure(atoms) lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments(maximum_distance_factor=1.41, only_cations=False) strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters() lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) isite = 0 cor_env = [] coor_env_dict = dict() for isite in Ir_indices: c_env = lse.coordination_environments[isite] coor_env_dict[isite] = c_env cor_env += [c_env[0]['ce_symbol']] out_dict["coor_env_dict"] = coor_env_dict return (out_dict)
def from_dict(cls, d): """ Args: d (): Returns: """ # Reconstructs the graph with integer as nodes (json's as_dict replaces integer keys with str keys) cgraph = nx.from_dict_of_dicts(d["connectivity_graph"], create_using=nx.MultiGraph, multigraph_input=True) cgraph = nx.relabel_nodes( cgraph, int ) # Just relabel the nodes using integer casting (maps str->int) # Relabel multiedges (removes multiedges with str keys and adds them back with int keys) edges = set(cgraph.edges()) for n1, n2 in edges: new_edges = { int(iedge): edata for iedge, edata in cgraph[n1][n2].items() } cgraph.remove_edges_from([ (n1, n2, iedge) for iedge, edata in cgraph[n1][n2].items() ]) cgraph.add_edges_from([(n1, n2, iedge, edata) for iedge, edata in new_edges.items()]) return cls( LightStructureEnvironments.from_dict( d["light_structure_environments"]), connectivity_graph=cgraph, environment_subgraphs=None, )
def test_serialization(self): BaTiO3_se_fpath = os.path.join( self.TEST_FILES_DIR, "chemenv", "structure_environments_files", "se_mp-5020.json", ) with open(BaTiO3_se_fpath) as f: dd = json.load(f) se = StructureEnvironments.from_dict(dd) lse = LightStructureEnvironments.from_structure_environments( strategy=SimplestChemenvStrategy(), structure_environments=se ) cf = ConnectivityFinder() sc = cf.get_structure_connectivity(light_structure_environments=lse) sc_from_dict = StructureConnectivity.from_dict(sc.as_dict()) assert sc.light_structure_environments == sc_from_dict.light_structure_environments assert set(sc._graph.nodes()) == set(sc_from_dict._graph.nodes()) assert set(sc._graph.edges()) == set(sc_from_dict._graph.edges()) sc_from_json = StructureConnectivity.from_dict(json.loads(json.dumps(sc.as_dict()))) assert sc.light_structure_environments == sc_from_json.light_structure_environments assert set(sc._graph.nodes()) == set(sc_from_json._graph.nodes()) assert set(sc._graph.edges()) == set(sc_from_json._graph.edges()) if bson is not None: bson_data = bson.BSON.encode(sc.as_dict()) sc_from_bson = StructureConnectivity.from_dict(bson_data.decode()) assert sc.light_structure_environments == sc_from_bson.light_structure_environments assert set(sc._graph.nodes()) == set(sc_from_bson._graph.nodes()) assert set(sc._graph.edges()) == set(sc_from_bson._graph.edges())
def setUp(self): with open("mp-7000.json", "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) struct = lse.structure bva = BVAnalyzer() valences = bva.get_valences(structure=struct) lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments(maximum_distance_factor=1.41, only_cations=False, valences=valences) strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters() self.lse = LightStructureEnvironments.from_structure_environments(strategy=strategy, structure_environments=se) with open("mp-5634.json", "r") as f: dict_lse2 = json.load(f) self.lse2 = LightStructureEnvironments.from_dict(dict_lse2)
def setUp(self): self.paulingconnetion = PaulingConnection(DISTANCE=8.0) self.matlist = ["mp-7000.json"] self.lse_dict = {} for mat in self.matlist: with open(mat, "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) self.lse_dict[mat] = lse
def setUp(self): self.matlist = ["mp-1788.json", "mp-7000.json", "mp-19359.json", "mp-306.json", "mp-886.json", "mp-2605.json"] self.lse_dict = {} self.Pauling_dict = {} for mat in self.matlist: with open(mat, "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) self.lse_dict[mat] = lse self.Pauling_dict[mat] = Pauling0(self.lse_dict[mat])
def test_read_structure_environments(self): f = open("{}/{}".format(json_files_dir, 'test_T--4_FePO4_icsd_4266.json'), 'r') dd = json.load(f) f.close() atom_indices = dd['atom_indices'] struct = Structure.from_dict(dd['structure']) self.lgf.setup_structure(struct) se = self.lgf.compute_structure_environments_detailed_voronoi(only_indices=atom_indices, maximum_distance_factor=2.25) f = open('tmp_dir/se.json', 'w') json.dump(se.as_dict(), f) f.close() f = open('tmp_dir/se.json', 'r') dd = json.load(f) f.close() se2 = StructureEnvironments.from_dict(dd) self.assertEqual(se, se2) _strategy = SimplestChemenvStrategy() light_se = LightStructureEnvironments(_strategy, se) f = open('tmp_dir/light_se.json', 'w') json.dump(light_se.as_dict(), f) f.close() f = open('tmp_dir/light_se.json', 'r') dd = json.load(f) f.close() light_se2 = LightStructureEnvironments.from_dict(dd) self.assertEqual(light_se._strategy, light_se2._strategy) self.assertEqual(light_se._structure, light_se2._structure) self.assertEqual(light_se._bva_valences, light_se2._bva_valences) self.assertEqual(light_se._coordination_environments, light_se2._coordination_environments) self.assertEqual(light_se._neighbors, light_se2._neighbors) self.assertEqual(light_se, light_se2)
def test_read_write_structure_environments(self): f = open( "{}/{}".format(json_files_dir, "test_T--4_FePO4_icsd_4266.json"), "r") dd = json.load(f) f.close() atom_indices = dd["atom_indices"] struct = Structure.from_dict(dd["structure"]) self.lgf.setup_structure(struct) se = self.lgf.compute_structure_environments( only_indices=atom_indices, maximum_distance_factor=2.25, get_from_hints=True) f = open("tmp_dir/se.json", "w") json.dump(se.as_dict(), f) f.close() f = open("tmp_dir/se.json", "r") dd = json.load(f) f.close() se2 = StructureEnvironments.from_dict(dd) self.assertEqual(se, se2) strategy = SimplestChemenvStrategy() lse = LightStructureEnvironments.from_structure_environments( structure_environments=se, strategy=strategy, valences="undefined") f = open("tmp_dir/lse.json", "w") json.dump(lse.as_dict(), f) f.close() f = open("tmp_dir/lse.json", "r") dd = json.load(f) f.close() lse2 = LightStructureEnvironments.from_dict(dd) self.assertEqual(lse, lse2)
def setUp(self): self.matlist = ["mp-7000.json", "mp-19359.json", "mp-1788.json", "mp-12236.json", "mp-5986.json"] self.lse_dict = {} self.Pauling_List = {} for mat in self.matlist: with open(mat, "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) self.lse_dict[mat] = lse self.Pauling_List[mat] = Pauling5() self.Pauling_List[mat].newsetup(lse=lse, save_to_file=False)
def test_read_write_structure_environments(self): f = open( "{}/{}".format(json_files_dir, 'test_T--4_FePO4_icsd_4266.json'), 'r') dd = json.load(f) f.close() atom_indices = dd['atom_indices'] struct = Structure.from_dict(dd['structure']) self.lgf.setup_structure(struct) se = self.lgf.compute_structure_environments( only_indices=atom_indices, maximum_distance_factor=2.25) f = open('tmp_dir/se.json', 'w') json.dump(se.as_dict(), f) f.close() f = open('tmp_dir/se.json', 'r') dd = json.load(f) f.close() se2 = StructureEnvironments.from_dict(dd) self.assertEqual(se, se2) strategy = SimplestChemenvStrategy() lse = LightStructureEnvironments.from_structure_environments( structure_environments=se, strategy=strategy, valences='undefined') f = open('tmp_dir/lse.json', 'w') json.dump(lse.as_dict(), f) f.close() f = open('tmp_dir/lse.json', 'r') dd = json.load(f) f.close() lse2 = LightStructureEnvironments.from_dict(dd) self.assertEqual(lse, lse2)
def setUp(self): # test it for further candidates -> differing in CN, differing in val self.matlist = ["mp-7000.json", "mp-19359.json", "mp-1788.json", "mp-5986.json", "mp-19418.json"] self.lse_dict = {} self.Pauling_List = {} for mat in self.matlist: with open(mat, "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) self.lse_dict[mat] = lse self.Pauling_List[mat] = Pauling4() self.Pauling_List[mat].newsetup(lse=lse, save_to_file=False)
def test_read_write_structure_environments(self): f = open("{}/{}".format(json_files_dir, 'test_T--4_FePO4_icsd_4266.json'), 'r') dd = json.load(f) f.close() atom_indices = dd['atom_indices'] struct = Structure.from_dict(dd['structure']) self.lgf.setup_structure(struct) se = self.lgf.compute_structure_environments(only_indices=atom_indices, maximum_distance_factor=2.25, get_from_hints=True) f = open('tmp_dir/se.json', 'w') json.dump(se.as_dict(), f) f.close() f = open('tmp_dir/se.json', 'r') dd = json.load(f) f.close() se2 = StructureEnvironments.from_dict(dd) self.assertEqual(se, se2) strategy = SimplestChemenvStrategy() lse = LightStructureEnvironments.from_structure_environments(structure_environments=se, strategy=strategy, valences='undefined') f = open('tmp_dir/lse.json', 'w') json.dump(lse.as_dict(), f) f.close() f = open('tmp_dir/lse.json', 'r') dd = json.load(f) f.close() lse2 = LightStructureEnvironments.from_dict(dd) self.assertEqual(lse, lse2)
def setUp(self): self.matlist = ["mp-7000.json", "mp-19418.json"] self.lse_dict = {} for mat in self.matlist: with open(mat, "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) self.lse_dict[mat] = lse self.pauling3and4_dist1 = Pauling3and4() self.pauling3and4_dist1.newsetup(self.lse_dict["mp-7000.json"], save_to_file=False, filename=False, distance=4.5) self.pauling3and4_dist2 = Pauling3and4() self.pauling3and4_dist2.newsetup(self.lse_dict["mp-7000.json"], save_to_file=False, filename=False) self.pauling3and4_dict3 = Pauling3and4() self.pauling3and4_dict3.newsetup(self.lse_dict["mp-19418.json"], save_to_file=False, filename=False, distance=4)
def setUp(self): # TODO: initilaize normal pauling as well self.matlist = ["mp-7000.json", "mp-566090.json", "mp-2605.json", "mp-4056.json"] self.lse_dict = {} self.Pauling_dict = {} self.Pauling_dict_perc1 = {} self.Pauling_dict_perc02 = {} self.Pauling_dict_realtwo = {} for mat in self.matlist: with open(mat, "r") as f: dict_lse = json.load(f) lse = LightStructureEnvironments.from_dict(dict_lse) self.lse_dict[mat] = lse self.Pauling_dict[mat] = Pauling2_optimized_environments(self.lse_dict[mat], perc=0.3) self.Pauling_dict_perc02[mat] = Pauling2_optimized_environments(self.lse_dict[mat], perc=0.2) self.Pauling_dict_perc1[mat] = Pauling2_optimized_environments(self.lse_dict[mat], perc=1.0) self.Pauling_dict_realtwo[mat] = Pauling2(self.lse_dict[mat])
def getSimplestChemenvStrategy(struct): # Setup the local geometry finder lgf = LocalGeometryFinder() lgf.setup_parameters(centering_type='centroid', include_central_site_in_centroid=True) #you can also save the logging to a file, just remove the comment # logging.basicConfig(#filename='chemenv_structure_environments.log', # format='%(levelname)s:%(module)s:%(funcName)s:%(message)s', # level=logging.DEBUG) lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments(maximum_distance_factor=1.41, only_cations=False) strategy = SimplestChemenvStrategy(distance_cutoff=1.4, angle_cutoff=0.3) lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) return lse.coordination_environments
def run_task(self, fw_spec): logging.basicConfig(filename='chemenv_light_structure_environments.log', format='%(levelname)s:%(module)s:%(funcName)s:%(message)s', level=logging.DEBUG) identifier = fw_spec['identifier'] criteria = {'identifier': identifier} # Where to get the full structure environments object se_database = fw_spec['structure_environments_database'] entry = se_database.collection.find_one(criteria) if fw_spec['structure_environments_setup'] == 'from_gridfs': gfs_fileobject = se_database.gridfs.get(entry['structure_environments']) dd = json.load(gfs_fileobject) se = StructureEnvironments.from_dict(dd) elif fw_spec['structure_environments_setup'] == 'from_storedfile': se_storage_server = fw_spec['se_storage_server'] se_filepath = entry['structure_environments_file'] se_storage_server.get(se_filepath, 'se.json') f = open('se.json', 'r') dd = json.load(f) f.close() se = StructureEnvironments.from_dict(dd) else: raise RuntimeError('Wrong structure_environments_setup : ' '"{}" is not allowed'.format(fw_spec['structure_environments_setup'])) # Compute the light structure environments chemenv_strategy = fw_spec['chemenv_strategy'] if 'valences' in fw_spec: valences = fw_spec['valences'] valences_origin = fw_spec['valences_origin'] else: valences = 'undefined' valences_origin = 'None' lse = LightStructureEnvironments.from_structure_environments(strategy=chemenv_strategy, structure_environments=se, valences=valences, valences_origin=valences_origin) # Write to json file if 'json_file' in fw_spec: json_file = fw_spec['json_file'] else: json_file = 'light_structure_environments.json' f = open(json_file, 'w') json.dump(lse.as_dict(), f) f.close() # Save to database if 'mongo_database' in fw_spec: database = fw_spec['mongo_database'] entry = {'identifier': identifier, 'elements': [elmt.symbol for elmt in lse.structure.composition.elements], 'nelements': len(lse.structure.composition.elements), 'pretty_formula': lse.structure.composition.reduced_formula, 'nsites': len(lse.structure), 'chemenv_statistics': lse.get_statistics(statistics_fields='ALL', bson_compatible=True) } saving_option = fw_spec['saving_option'] if saving_option == 'gridfs': gridfs_msonables = {'structure': lse.structure, 'light_structure_environments': lse} elif saving_option == 'storefile': gridfs_msonables = None if 'lse_prefix' in fw_spec: lse_prefix = fw_spec['lse_prefix'] if not lse_prefix.isalpha(): raise ValueError('Prefix for light_structure_environments file is "{}" ' 'while it should be alphabetic'.format(lse_prefix)) else: lse_prefix = '' if lse_prefix: lse_rfilename = '{}_{}.json'.format(lse_prefix, fw_spec['storefile_basename']) else: lse_rfilename = '{}.json'.format(fw_spec['storefile_basename']) lse_rfilepath = '{}/{}'.format(fw_spec['storefile_dirpath'], lse_rfilename) storage_server = fw_spec['storage_server'] storage_server.put(localpath=json_file, remotepath=lse_rfilepath, overwrite=True, makedirs=False) entry['light_structure_environments_file'] = lse_rfilepath else: raise ValueError('Saving option is "{}" while it should be ' '"gridfs" or "storefile"'.format(saving_option)) criteria = {'identifier': identifier} if database.collection.find(criteria).count() == 1: database.update_entry(query=criteria, entry_update=entry, gridfs_msonables=gridfs_msonables) else: database.insert_entry(entry=entry, gridfs_msonables=gridfs_msonables)
def from_dict(cls, d): return cls(LightStructureEnvironments.from_dict(d['light_structure_environments']), connectivity_graph=nx.from_dict_of_dicts(d['connectivity_graph'], multigraph_input=True), environment_subgraphs={env_key: nx.from_dict_of_dicts(subgraph, multigraph_input=True) for env_key, subgraph in d['environment_subgraphs'].items()})
def test_light_structure_environments(self): with ScratchDir("."): with open(f"{se_files_dir}/se_mp-7000.json") as f: dd = json.load(f) se = StructureEnvironments.from_dict(dd) strategy = SimplestChemenvStrategy() lse = LightStructureEnvironments.from_structure_environments( structure_environments=se, strategy=strategy, valences="undefined") isite = 6 nb_set = lse.neighbors_sets[isite][0] neighb_coords = [ np.array([0.2443798, 1.80409653, -1.13218359]), np.array([1.44020353, 1.11368738, 1.13218359]), np.array([2.75513098, 2.54465207, -0.70467298]), np.array([0.82616785, 3.65833945, 0.70467298]), ] neighb_indices = [0, 3, 5, 1] neighb_images = [[0, 0, -1], [0, 0, 0], [0, 0, -1], [0, 0, 0]] np.testing.assert_array_almost_equal(neighb_coords, nb_set.neighb_coords) np.testing.assert_array_almost_equal( neighb_coords, [s.coords for s in nb_set.neighb_sites]) nb_sai = nb_set.neighb_sites_and_indices np.testing.assert_array_almost_equal( neighb_coords, [sai["site"].coords for sai in nb_sai]) np.testing.assert_array_almost_equal( neighb_indices, [sai["index"] for sai in nb_sai]) nb_iai = nb_set.neighb_indices_and_images np.testing.assert_array_almost_equal( neighb_indices, [iai["index"] for iai in nb_iai]) np.testing.assert_array_equal( neighb_images, [iai["image_cell"] for iai in nb_iai]) self.assertEqual(nb_set.__len__(), 4) self.assertEqual(nb_set.__hash__(), 4) self.assertFalse(nb_set.__ne__(nb_set)) self.assertEqual( nb_set.__str__(), "Neighbors Set for site #6 :\n" " - Coordination number : 4\n" " - Neighbors sites indices : 0, 1, 2, 3\n", ) stats = lse.get_statistics() neighbors = lse.strategy.get_site_neighbors( site=lse.structure[isite]) self.assertArrayAlmostEqual( neighbors[0].coords, np.array([0.2443798, 1.80409653, -1.13218359])) self.assertArrayAlmostEqual( neighbors[1].coords, np.array([1.44020353, 1.11368738, 1.13218359])) self.assertArrayAlmostEqual( neighbors[2].coords, np.array([2.75513098, 2.54465207, -0.70467298])) self.assertArrayAlmostEqual( neighbors[3].coords, np.array([0.82616785, 3.65833945, 0.70467298])) equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform( neighbors[0]) self.assertEqual(equiv_site_index_and_transform[0], 0) self.assertArrayAlmostEqual(equiv_site_index_and_transform[1], [0.0, 0.0, 0.0]) self.assertArrayAlmostEqual(equiv_site_index_and_transform[2], [0.0, 0.0, -1.0]) equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform( neighbors[1]) self.assertEqual(equiv_site_index_and_transform[0], 3) self.assertArrayAlmostEqual(equiv_site_index_and_transform[1], [0.0, 0.0, 0.0]) self.assertArrayAlmostEqual(equiv_site_index_and_transform[2], [0.0, 0.0, 0.0]) self.assertEqual(stats["atom_coordination_environments_present"], {"Si": { "T:4": 3.0 }}) self.assertEqual(stats["coordination_environments_atom_present"], {"T:4": { "Si": 3.0 }}) self.assertEqual( stats["fraction_atom_coordination_environments_present"], {"Si": { "T:4": 1.0 }}, ) site_info_ce = lse.get_site_info_for_specie_ce(specie=Species( "Si", 4), ce_symbol="T:4") np.testing.assert_array_almost_equal(site_info_ce["fractions"], [1.0, 1.0, 1.0]) np.testing.assert_array_almost_equal( site_info_ce["csms"], [ 0.009887784240541068, 0.009887786546730826, 0.009887787384385317 ], ) self.assertEqual(site_info_ce["isites"], [6, 7, 8]) site_info_allces = lse.get_site_info_for_specie_allces( specie=Species("Si", 4)) self.assertEqual(site_info_allces["T:4"], site_info_ce) self.assertFalse(lse.contains_only_one_anion("I-")) self.assertFalse(lse.contains_only_one_anion_atom("I")) self.assertTrue( lse.site_contains_environment(isite=isite, ce_symbol="T:4")) self.assertFalse( lse.site_contains_environment(isite=isite, ce_symbol="S:4")) self.assertFalse( lse.structure_contains_atom_environment(atom_symbol="Si", ce_symbol="S:4")) self.assertTrue( lse.structure_contains_atom_environment(atom_symbol="Si", ce_symbol="T:4")) self.assertFalse( lse.structure_contains_atom_environment(atom_symbol="O", ce_symbol="T:4")) self.assertTrue(lse.uniquely_determines_coordination_environments) self.assertFalse(lse.__ne__(lse)) envs = lse.strategy.get_site_coordination_environments( lse.structure[6]) self.assertEqual(len(envs), 1) self.assertEqual(envs[0][0], "T:4") multi_strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters( ) lse_multi = LightStructureEnvironments.from_structure_environments( strategy=multi_strategy, structure_environments=se, valences="undefined") self.assertAlmostEqual( lse_multi.coordination_environments[isite][0]["csm"], 0.009887784240541068, ) self.assertAlmostEqual( lse_multi.coordination_environments[isite][0]["ce_fraction"], 1.0) self.assertEqual( lse_multi.coordination_environments[isite][0]["ce_symbol"], "T:4")
def get_chemenv_analysis(struct, distance_cutoff, angle_cutoff): if not struct: raise PreventUpdate struct = self.from_data(struct) distance_cutoff = float(distance_cutoff) angle_cutoff = float(angle_cutoff) # decide which indices to present to user sga = SpacegroupAnalyzer(struct) symm_struct = sga.get_symmetrized_structure() inequivalent_indices = [ indices[0] for indices in symm_struct.equivalent_indices ] wyckoffs = symm_struct.wyckoff_symbols lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments( maximum_distance_factor=distance_cutoff + 0.01, only_indices=inequivalent_indices, ) strategy = SimplestChemenvStrategy(distance_cutoff=distance_cutoff, angle_cutoff=angle_cutoff) lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) all_ce = AllCoordinationGeometries() envs = [] unknown_sites = [] for index, wyckoff in zip(inequivalent_indices, wyckoffs): datalist = { "Site": struct[index].species_string, "Wyckoff Label": wyckoff, } if not lse.neighbors_sets[index]: unknown_sites.append( f"{struct[index].species_string} ({wyckoff})") continue # represent the local environment as a molecule mol = Molecule.from_sites( [struct[index]] + lse.neighbors_sets[index][0].neighb_sites) mol = mol.get_centered_molecule() mg = MoleculeGraph.with_empty_graph(molecule=mol) for i in range(1, len(mol)): mg.add_edge(0, i) view = html.Div( [ StructureMoleculeComponent( struct_or_mol=mg, static=True, id= f"{struct.composition.reduced_formula}_site_{index}", scene_settings={ "enableZoom": False, "defaultZoom": 0.6 }, ).all_layouts["struct"] ], style={ "width": "300px", "height": "300px" }, ) env = lse.coordination_environments[index] co = all_ce.get_geometry_from_mp_symbol(env[0]["ce_symbol"]) name = co.name if co.alternative_names: name += f" (also known as {', '.join(co.alternative_names)})" datalist.update({ "Environment": name, "IUPAC Symbol": co.IUPAC_symbol_str, get_tooltip( "CSM", '"Continuous Symmetry Measure," a measure of how symmetrical a ' "local environment is from most symmetrical at 0% to least " "symmetrical at 100%", ): f"{env[0]['csm']:.2f}%", "Interactive View": view, }) envs.append(get_data_list(datalist)) # TODO: switch to tiles? envs_grouped = [envs[i:i + 2] for i in range(0, len(envs), 2)] analysis_contents = [] for env_group in envs_grouped: analysis_contents.append( Columns([Column(e, size=6) for e in env_group])) if unknown_sites: unknown_sites = html.Strong( f"The following sites were not identified: {', '.join(unknown_sites)}. " f"Please try changing the distance or angle cut-offs to identify these sites." ) else: unknown_sites = html.Span() return html.Div( [html.Div(analysis_contents), html.Br(), unknown_sites])
def test_light_structure_environments(self): f = open("{}/{}".format(se_files_dir, 'se_mp-7000.json'), 'r') dd = json.load(f) f.close() se = StructureEnvironments.from_dict(dd) strategy = SimplestChemenvStrategy() lse = LightStructureEnvironments.from_structure_environments(structure_environments=se, strategy=strategy, valences='undefined') isite = 6 nb_set = lse.neighbors_sets[isite][0] neighb_coords = [np.array([0.2443798, 1.80409653, -1.13218359]), np.array([1.44020353, 1.11368738, 1.13218359]), np.array([2.75513098, 2.54465207, -0.70467298]), np.array([0.82616785, 3.65833945, 0.70467298])] neighb_indices = [0, 3, 5, 1] neighb_images = [[0, 0, -1], [0, 0, 0], [0, 0, -1], [0, 0, 0]] np.testing.assert_array_almost_equal(neighb_coords, nb_set.neighb_coords) np.testing.assert_array_almost_equal(neighb_coords, [s.coords for s in nb_set.neighb_sites]) nb_sai = nb_set.neighb_sites_and_indices np.testing.assert_array_almost_equal(neighb_coords, [sai['site'].coords for sai in nb_sai]) np.testing.assert_array_almost_equal(neighb_indices, [sai['index'] for sai in nb_sai]) nb_iai = nb_set.neighb_indices_and_images np.testing.assert_array_almost_equal(neighb_indices, [iai['index'] for iai in nb_iai]) np.testing.assert_array_equal(neighb_images, [iai['image_cell'] for iai in nb_iai]) self.assertEqual(nb_set.__len__(), 4) self.assertEqual(nb_set.__hash__(), 4) self.assertFalse(nb_set.__ne__(nb_set)) self.assertEqual(nb_set.__str__(), 'Neighbors Set for site #6 :\n' ' - Coordination number : 4\n' ' - Neighbors sites indices : 0, 1, 2, 3\n') stats = lse.get_statistics() neighbors = lse.strategy.get_site_neighbors(site=lse.structure[isite]) self.assertArrayAlmostEqual(neighbors[0].coords, np.array([ 0.2443798, 1.80409653, -1.13218359])) self.assertArrayAlmostEqual(neighbors[1].coords, np.array([ 1.44020353, 1.11368738, 1.13218359])) self.assertArrayAlmostEqual(neighbors[2].coords, np.array([ 2.75513098, 2.54465207, -0.70467298])) self.assertArrayAlmostEqual(neighbors[3].coords, np.array([ 0.82616785, 3.65833945, 0.70467298])) equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform(neighbors[0]) self.assertEqual(equiv_site_index_and_transform[0], 0) self.assertArrayAlmostEqual(equiv_site_index_and_transform[1], [0.0, 0.0, 0.0]) self.assertArrayAlmostEqual(equiv_site_index_and_transform[2], [0.0, 0.0, -1.0]) equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform(neighbors[1]) self.assertEqual(equiv_site_index_and_transform[0], 3) self.assertArrayAlmostEqual(equiv_site_index_and_transform[1], [0.0, 0.0, 0.0]) self.assertArrayAlmostEqual(equiv_site_index_and_transform[2], [0.0, 0.0, 0.0]) self.assertEqual(stats['atom_coordination_environments_present'], {'Si': {'T:4': 3.0}}) self.assertEqual(stats['coordination_environments_atom_present'], {'T:4': {'Si': 3.0}}) self.assertEqual(stats['fraction_atom_coordination_environments_present'], {'Si': {'T:4': 1.0}}) site_info_ce = lse.get_site_info_for_specie_ce(specie=Specie('Si', 4), ce_symbol='T:4') np.testing.assert_array_almost_equal(site_info_ce['fractions'], [1.0, 1.0, 1.0]) np.testing.assert_array_almost_equal(site_info_ce['csms'], [0.009887784240541068, 0.009887786546730826, 0.009887787384385317]) self.assertEqual(site_info_ce['isites'], [6, 7, 8]) site_info_allces = lse.get_site_info_for_specie_allces(specie=Specie('Si', 4)) self.assertEqual(site_info_allces['T:4'], site_info_ce) self.assertFalse(lse.contains_only_one_anion('I-')) self.assertFalse(lse.contains_only_one_anion_atom('I')) self.assertTrue(lse.site_contains_environment(isite=isite, ce_symbol='T:4')) self.assertFalse(lse.site_contains_environment(isite=isite, ce_symbol='S:4')) self.assertFalse(lse.structure_contains_atom_environment(atom_symbol='Si', ce_symbol='S:4')) self.assertTrue(lse.structure_contains_atom_environment(atom_symbol='Si', ce_symbol='T:4')) self.assertFalse(lse.structure_contains_atom_environment(atom_symbol='O', ce_symbol='T:4')) self.assertTrue(lse.uniquely_determines_coordination_environments) self.assertFalse(lse.__ne__(lse)) envs = lse.strategy.get_site_coordination_environments(lse.structure[6]) self.assertEqual(len(envs), 1) self.assertEqual(envs[0][0], 'T:4') multi_strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters() lse_multi = LightStructureEnvironments.from_structure_environments(strategy=multi_strategy, structure_environments=se, valences='undefined') self.assertAlmostEqual(lse_multi.coordination_environments[isite][0]['csm'], 0.009887784240541068) self.assertAlmostEqual(lse_multi.coordination_environments[isite][0]['ce_fraction'], 1.0) self.assertEqual(lse_multi.coordination_environments[isite][0]['ce_symbol'], 'T:4')
Ir_indices = [ i for i, s in enumerate(atoms.get_chemical_symbols()) if s == 'Ir' ] struct = AseAtomsAdaptor.get_structure(atoms) lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments(maximum_distance_factor=1.41, only_cations=False) strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters( ) lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) isite = 0 cor_env = [] for isite in Ir_indices: c_env = lse.coordination_environments[isite] if len(c_env) == 0: continue cor_env += [c_env[0]['ce_symbol']] if len(cor_env) == 0: continue uniques, counts = np.unique(cor_env, return_counts=True) if len(uniques) > 1:
def run_task(self, fw_spec): logging.basicConfig( filename='chemenv_light_structure_environments.log', format='%(levelname)s:%(module)s:%(funcName)s:%(message)s', level=logging.DEBUG) identifier = fw_spec['identifier'] criteria = {'identifier': identifier} # Where to get the full structure environments object se_database = fw_spec['structure_environments_database'] entry = se_database.collection.find_one(criteria) if fw_spec['structure_environments_setup'] == 'from_gridfs': gfs_fileobject = se_database.gridfs.get( entry['structure_environments']) dd = json.load(gfs_fileobject) se = StructureEnvironments.from_dict(dd) elif fw_spec['structure_environments_setup'] == 'from_storedfile': se_storage_server = fw_spec['se_storage_server'] se_filepath = entry['structure_environments_file'] se_storage_server.get(se_filepath, 'se.json') f = open('se.json', 'r') dd = json.load(f) f.close() se = StructureEnvironments.from_dict(dd) else: raise RuntimeError('Wrong structure_environments_setup : ' '"{}" is not allowed'.format( fw_spec['structure_environments_setup'])) # Compute the light structure environments chemenv_strategy = fw_spec['chemenv_strategy'] if 'valences' in fw_spec: valences = fw_spec['valences'] valences_origin = fw_spec['valences_origin'] else: valences = 'undefined' valences_origin = 'None' lse = LightStructureEnvironments.from_structure_environments( strategy=chemenv_strategy, structure_environments=se, valences=valences, valences_origin=valences_origin) # Write to json file if 'json_file' in fw_spec: json_file = fw_spec['json_file'] else: json_file = 'light_structure_environments.json' f = open(json_file, 'w') json.dump(lse.as_dict(), f) f.close() # Save to database if 'mongo_database' in fw_spec: database = fw_spec['mongo_database'] entry = { 'identifier': identifier, 'elements': [elmt.symbol for elmt in lse.structure.composition.elements], 'nelements': len(lse.structure.composition.elements), 'pretty_formula': lse.structure.composition.reduced_formula, 'nsites': len(lse.structure), 'chemenv_statistics': lse.get_statistics(statistics_fields='ALL', bson_compatible=True) } saving_option = fw_spec['saving_option'] if saving_option == 'gridfs': gridfs_msonables = { 'structure': lse.structure, 'light_structure_environments': lse } elif saving_option == 'storefile': gridfs_msonables = None if 'lse_prefix' in fw_spec: lse_prefix = fw_spec['lse_prefix'] if not lse_prefix.isalpha(): raise ValueError( 'Prefix for light_structure_environments file is "{}" ' 'while it should be alphabetic'.format(lse_prefix)) else: lse_prefix = '' if lse_prefix: lse_rfilename = '{}_{}.json'.format( lse_prefix, fw_spec['storefile_basename']) else: lse_rfilename = '{}.json'.format( fw_spec['storefile_basename']) lse_rfilepath = '{}/{}'.format(fw_spec['storefile_dirpath'], lse_rfilename) storage_server = fw_spec['storage_server'] storage_server.put(localpath=json_file, remotepath=lse_rfilepath, overwrite=True, makedirs=False) entry['light_structure_environments_file'] = lse_rfilepath else: raise ValueError( 'Saving option is "{}" while it should be ' '"gridfs" or "storefile"'.format(saving_option)) criteria = {'identifier': identifier} if database.collection.find(criteria).count() == 1: database.update_entry(query=criteria, entry_update=entry, gridfs_msonables=gridfs_msonables) else: database.insert_entry(entry=entry, gridfs_msonables=gridfs_msonables)
def test_real_systems(self): # Initialize geometry and connectivity finders strat = SimplestChemenvStrategy() lgf = LocalGeometryFinder() cf = ConnectivityFinder() # Connectivity of LiFePO4 struct = self.get_structure("LiFePO4") lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments(only_atoms=["Li", "Fe", "P"], maximum_distance_factor=1.2) lse = LightStructureEnvironments.from_structure_environments( strategy=strat, structure_environments=se) # Make sure the initial structure and environments are correct for isite in range(0, 4): assert lse.structure[isite].specie.symbol == "Li" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "O:6" for isite in range(4, 8): assert lse.structure[isite].specie.symbol == "Fe" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "O:6" for isite in range(8, 12): assert lse.structure[isite].specie.symbol == "P" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "T:4" # Get the connectivity including all environments and check results sc = cf.get_structure_connectivity(lse) assert len(sc.environment_subgraphs ) == 0 # Connected component not computed by default ccs = sc.get_connected_components( ) # by default, will use all the environments (O:6 and T:4 here) assert list(sc.environment_subgraphs.keys()) == [ "O:6-T:4" ] # Now the default components are there assert len(sc.environment_subgraphs) == 1 assert len(ccs) == 1 cc = ccs[0] assert len(cc) == 12 assert cc.periodicity == "3D" assert ( cc.description() == """Connected component with environment nodes : Node #0 Li (O:6) Node #1 Li (O:6) Node #2 Li (O:6) Node #3 Li (O:6) Node #4 Fe (O:6) Node #5 Fe (O:6) Node #6 Fe (O:6) Node #7 Fe (O:6) Node #8 P (T:4) Node #9 P (T:4) Node #10 P (T:4) Node #11 P (T:4)""") assert (cc.description( full=True) == """Connected component with environment nodes : Node #0 Li (O:6), connected to : - Node #1 Li (O:6) with delta image cells (-1 0 1) (-1 1 1) - Node #4 Fe (O:6) with delta image cells (-1 1 1) (0 1 1) - Node #5 Fe (O:6) with delta image cells (0 0 1) - Node #6 Fe (O:6) with delta image cells (-1 1 0) - Node #7 Fe (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #8 P (T:4) with delta image cells (-1 0 1) (0 0 1) - Node #11 P (T:4) with delta image cells (-1 1 0) (0 1 0) Node #1 Li (O:6), connected to : - Node #0 Li (O:6) with delta image cells (1 -1 -1) (1 0 -1) - Node #4 Fe (O:6) with delta image cells (0 0 0) (1 0 0) - Node #5 Fe (O:6) with delta image cells (1 0 0) - Node #6 Fe (O:6) with delta image cells (0 0 -1) - Node #7 Fe (O:6) with delta image cells (0 0 -1) (1 0 -1) - Node #8 P (T:4) with delta image cells (0 0 0) (1 0 0) - Node #11 P (T:4) with delta image cells (0 0 -1) (1 0 -1) Node #2 Li (O:6), connected to : - Node #3 Li (O:6) with delta image cells (0 0 0) (0 1 0) - Node #4 Fe (O:6) with delta image cells (0 1 0) - Node #5 Fe (O:6) with delta image cells (0 0 0) (1 0 0) - Node #6 Fe (O:6) with delta image cells (-1 1 0) (0 1 0) - Node #7 Fe (O:6) with delta image cells (0 0 0) - Node #9 P (T:4) with delta image cells (0 1 0) (1 1 0) - Node #10 P (T:4) with delta image cells (-1 0 0) (0 0 0) Node #3 Li (O:6), connected to : - Node #2 Li (O:6) with delta image cells (0 -1 0) (0 0 0) - Node #4 Fe (O:6) with delta image cells (0 0 0) - Node #5 Fe (O:6) with delta image cells (0 0 0) (1 0 0) - Node #6 Fe (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #7 Fe (O:6) with delta image cells (0 0 0) - Node #9 P (T:4) with delta image cells (0 0 0) (1 0 0) - Node #10 P (T:4) with delta image cells (-1 0 0) (0 0 0) Node #4 Fe (O:6), connected to : - Node #0 Li (O:6) with delta image cells (0 -1 -1) (1 -1 -1) - Node #1 Li (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #2 Li (O:6) with delta image cells (0 -1 0) - Node #3 Li (O:6) with delta image cells (0 0 0) - Node #5 Fe (O:6) with delta image cells (0 -1 0) (0 0 0) (1 -1 0) (1 0 0) - Node #8 P (T:4) with delta image cells (0 -1 0) (0 0 0) - Node #9 P (T:4) with delta image cells (0 0 0) (1 0 0) - Node #11 P (T:4) with delta image cells (0 0 -1) Node #5 Fe (O:6), connected to : - Node #0 Li (O:6) with delta image cells (0 0 -1) - Node #1 Li (O:6) with delta image cells (-1 0 0) - Node #2 Li (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #3 Li (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #4 Fe (O:6) with delta image cells (-1 0 0) (-1 1 0) (0 0 0) (0 1 0) - Node #8 P (T:4) with delta image cells (-1 0 0) (0 0 0) - Node #9 P (T:4) with delta image cells (0 0 0) (0 1 0) - Node #10 P (T:4) with delta image cells (-1 0 0) Node #6 Fe (O:6), connected to : - Node #0 Li (O:6) with delta image cells (1 -1 0) - Node #1 Li (O:6) with delta image cells (0 0 1) - Node #2 Li (O:6) with delta image cells (0 -1 0) (1 -1 0) - Node #3 Li (O:6) with delta image cells (0 0 0) (1 0 0) - Node #7 Fe (O:6) with delta image cells (0 -1 0) (0 0 0) (1 -1 0) (1 0 0) - Node #9 P (T:4) with delta image cells (1 0 0) - Node #10 P (T:4) with delta image cells (0 -1 0) (0 0 0) - Node #11 P (T:4) with delta image cells (0 0 0) (1 0 0) Node #7 Fe (O:6), connected to : - Node #0 Li (O:6) with delta image cells (0 0 0) (1 0 0) - Node #1 Li (O:6) with delta image cells (-1 0 1) (0 0 1) - Node #2 Li (O:6) with delta image cells (0 0 0) - Node #3 Li (O:6) with delta image cells (0 0 0) - Node #6 Fe (O:6) with delta image cells (-1 0 0) (-1 1 0) (0 0 0) (0 1 0) - Node #8 P (T:4) with delta image cells (0 0 1) - Node #10 P (T:4) with delta image cells (-1 0 0) (0 0 0) - Node #11 P (T:4) with delta image cells (0 0 0) (0 1 0) Node #8 P (T:4), connected to : - Node #0 Li (O:6) with delta image cells (0 0 -1) (1 0 -1) - Node #1 Li (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #4 Fe (O:6) with delta image cells (0 0 0) (0 1 0) - Node #5 Fe (O:6) with delta image cells (0 0 0) (1 0 0) - Node #7 Fe (O:6) with delta image cells (0 0 -1) Node #9 P (T:4), connected to : - Node #2 Li (O:6) with delta image cells (-1 -1 0) (0 -1 0) - Node #3 Li (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #4 Fe (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #5 Fe (O:6) with delta image cells (0 -1 0) (0 0 0) - Node #6 Fe (O:6) with delta image cells (-1 0 0) Node #10 P (T:4), connected to : - Node #2 Li (O:6) with delta image cells (0 0 0) (1 0 0) - Node #3 Li (O:6) with delta image cells (0 0 0) (1 0 0) - Node #5 Fe (O:6) with delta image cells (1 0 0) - Node #6 Fe (O:6) with delta image cells (0 0 0) (0 1 0) - Node #7 Fe (O:6) with delta image cells (0 0 0) (1 0 0) Node #11 P (T:4), connected to : - Node #0 Li (O:6) with delta image cells (0 -1 0) (1 -1 0) - Node #1 Li (O:6) with delta image cells (-1 0 1) (0 0 1) - Node #4 Fe (O:6) with delta image cells (0 0 1) - Node #6 Fe (O:6) with delta image cells (-1 0 0) (0 0 0) - Node #7 Fe (O:6) with delta image cells (0 -1 0) (0 0 0)""") # Get the connectivity for T:4 and O:6 separately and check results # Only tetrahedral sc.setup_environment_subgraph(environments_symbols=["T:4"]) assert list(sc.environment_subgraphs.keys()) == ["O:6-T:4", "T:4"] ccs = sc.get_connected_components() assert len(ccs) == 4 for cc in ccs: assert cc.periodicity == "0D" # Only octahedral sc.setup_environment_subgraph(environments_symbols=["O:6"]) assert list( sc.environment_subgraphs.keys()) == ["O:6-T:4", "T:4", "O:6"] ccs = sc.get_connected_components() assert len(ccs) == 1 cc = ccs[0] assert cc.periodicity == "3D" # Only Manganese octahedral sc.setup_environment_subgraph(environments_symbols=["O:6"], only_atoms=["Fe"]) assert list(sc.environment_subgraphs.keys()) == [ "O:6-T:4", "T:4", "O:6", "O:6#Fe", ] ccs = sc.get_connected_components() assert len(ccs) == 2 for cc in ccs: assert cc.periodicity == "2D" # Connectivity of Li4Fe3Mn1(PO4)4 struct = Structure.from_file( os.path.join(self.TEST_FILES_DIR, "Li4Fe3Mn1(PO4)4.cif")) lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments( only_atoms=["Li", "Fe", "Mn", "P"], maximum_distance_factor=1.2) lse = LightStructureEnvironments.from_structure_environments( strategy=strat, structure_environments=se) # Make sure the initial structure and environments are correct for isite in range(0, 4): assert lse.structure[isite].specie.symbol == "Li" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "O:6" for isite in range(4, 5): assert lse.structure[isite].specie.symbol == "Mn" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "O:6" for isite in range(5, 8): assert lse.structure[isite].specie.symbol == "Fe" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "O:6" for isite in range(8, 12): assert lse.structure[isite].specie.symbol == "P" assert lse.coordination_environments[isite][0][ "ce_symbol"] == "T:4" # Get the connectivity including all environments and check results sc = cf.get_structure_connectivity(lse) assert len(sc.environment_subgraphs ) == 0 # Connected component not computed by default ccs = sc.get_connected_components( ) # by default, will use all the environments (O:6 and T:4 here) # Now connected components for the defaults are there : assert list(sc.environment_subgraphs.keys()) == ["O:6-T:4"] assert len(sc.environment_subgraphs) == 1 assert len(ccs) == 1 cc = ccs[0] assert cc.periodicity == "3D" # Get the connectivity for Li octahedral only ccs = sc.get_connected_components(environments_symbols=["O:6"], only_atoms=["Li"]) assert list(sc.environment_subgraphs.keys()) == ["O:6-T:4", "O:6#Li"] assert len(ccs) == 2 for cc in ccs: assert cc.periodicity == "1D" assert len(cc) == 2 # Sort connected components as they might # come in a different order depending on # the algorithm used to get them. sorted_ccs = sorted(ccs, key=lambda x: sorted(x.graph.nodes())[0]) assert (sorted_ccs[0].description( full=True) == """Connected component with environment nodes : Node #0 Li (O:6), connected to : - Node #1 Li (O:6) with delta image cells (1 -1 1) (1 0 1) Node #1 Li (O:6), connected to : - Node #0 Li (O:6) with delta image cells (-1 0 -1) (-1 1 -1)""") assert (sorted_ccs[1].description( full=True) == """Connected component with environment nodes : Node #2 Li (O:6), connected to : - Node #3 Li (O:6) with delta image cells (0 -1 0) (0 0 0) Node #3 Li (O:6), connected to : - Node #2 Li (O:6) with delta image cells (0 0 0) (0 1 0)""") # Get the connectivity for Mn octahedral only ccs = sc.get_connected_components(environments_symbols=["O:6"], only_atoms=["Mn"]) assert list(sc.environment_subgraphs.keys()) == [ "O:6-T:4", "O:6#Li", "O:6#Mn" ] assert len(ccs) == 1 assert ccs[0].periodicity == "0D" # Get the connectivity for Fe octahedral only ccs = sc.get_connected_components(environments_symbols=["O:6"], only_atoms=["Fe"]) assert list(sc.environment_subgraphs.keys()) == [ "O:6-T:4", "O:6#Li", "O:6#Mn", "O:6#Fe", ] assert len(ccs) == 2 ccs_periodicities = set(cc.periodicity for cc in ccs) assert ccs_periodicities == {"0D", "2D"}
def test_light_structure_environments(self): with ScratchDir("."): f = open("{}/{}".format(se_files_dir, 'se_mp-7000.json'), 'r') dd = json.load(f) f.close() se = StructureEnvironments.from_dict(dd) strategy = SimplestChemenvStrategy() lse = LightStructureEnvironments.from_structure_environments( structure_environments=se, strategy=strategy, valences='undefined') isite = 6 nb_set = lse.neighbors_sets[isite][0] neighb_coords = [ np.array([0.2443798, 1.80409653, -1.13218359]), np.array([1.44020353, 1.11368738, 1.13218359]), np.array([2.75513098, 2.54465207, -0.70467298]), np.array([0.82616785, 3.65833945, 0.70467298]) ] neighb_indices = [0, 3, 5, 1] neighb_images = [[0, 0, -1], [0, 0, 0], [0, 0, -1], [0, 0, 0]] np.testing.assert_array_almost_equal(neighb_coords, nb_set.neighb_coords) np.testing.assert_array_almost_equal( neighb_coords, [s.coords for s in nb_set.neighb_sites]) nb_sai = nb_set.neighb_sites_and_indices np.testing.assert_array_almost_equal( neighb_coords, [sai['site'].coords for sai in nb_sai]) np.testing.assert_array_almost_equal( neighb_indices, [sai['index'] for sai in nb_sai]) nb_iai = nb_set.neighb_indices_and_images np.testing.assert_array_almost_equal( neighb_indices, [iai['index'] for iai in nb_iai]) np.testing.assert_array_equal( neighb_images, [iai['image_cell'] for iai in nb_iai]) self.assertEqual(nb_set.__len__(), 4) self.assertEqual(nb_set.__hash__(), 4) self.assertFalse(nb_set.__ne__(nb_set)) self.assertEqual( nb_set.__str__(), 'Neighbors Set for site #6 :\n' ' - Coordination number : 4\n' ' - Neighbors sites indices : 0, 1, 2, 3\n') stats = lse.get_statistics() neighbors = lse.strategy.get_site_neighbors( site=lse.structure[isite]) self.assertArrayAlmostEqual( neighbors[0].coords, np.array([0.2443798, 1.80409653, -1.13218359])) self.assertArrayAlmostEqual( neighbors[1].coords, np.array([1.44020353, 1.11368738, 1.13218359])) self.assertArrayAlmostEqual( neighbors[2].coords, np.array([2.75513098, 2.54465207, -0.70467298])) self.assertArrayAlmostEqual( neighbors[3].coords, np.array([0.82616785, 3.65833945, 0.70467298])) equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform( neighbors[0]) self.assertEqual(equiv_site_index_and_transform[0], 0) self.assertArrayAlmostEqual(equiv_site_index_and_transform[1], [0.0, 0.0, 0.0]) self.assertArrayAlmostEqual(equiv_site_index_and_transform[2], [0.0, 0.0, -1.0]) equiv_site_index_and_transform = lse.strategy.equivalent_site_index_and_transform( neighbors[1]) self.assertEqual(equiv_site_index_and_transform[0], 3) self.assertArrayAlmostEqual(equiv_site_index_and_transform[1], [0.0, 0.0, 0.0]) self.assertArrayAlmostEqual(equiv_site_index_and_transform[2], [0.0, 0.0, 0.0]) self.assertEqual(stats['atom_coordination_environments_present'], {'Si': { 'T:4': 3.0 }}) self.assertEqual(stats['coordination_environments_atom_present'], {'T:4': { 'Si': 3.0 }}) self.assertEqual( stats['fraction_atom_coordination_environments_present'], {'Si': { 'T:4': 1.0 }}) site_info_ce = lse.get_site_info_for_specie_ce(specie=Species( 'Si', 4), ce_symbol='T:4') np.testing.assert_array_almost_equal(site_info_ce['fractions'], [1.0, 1.0, 1.0]) np.testing.assert_array_almost_equal(site_info_ce['csms'], [ 0.009887784240541068, 0.009887786546730826, 0.009887787384385317 ]) self.assertEqual(site_info_ce['isites'], [6, 7, 8]) site_info_allces = lse.get_site_info_for_specie_allces( specie=Species('Si', 4)) self.assertEqual(site_info_allces['T:4'], site_info_ce) self.assertFalse(lse.contains_only_one_anion('I-')) self.assertFalse(lse.contains_only_one_anion_atom('I')) self.assertTrue( lse.site_contains_environment(isite=isite, ce_symbol='T:4')) self.assertFalse( lse.site_contains_environment(isite=isite, ce_symbol='S:4')) self.assertFalse( lse.structure_contains_atom_environment(atom_symbol='Si', ce_symbol='S:4')) self.assertTrue( lse.structure_contains_atom_environment(atom_symbol='Si', ce_symbol='T:4')) self.assertFalse( lse.structure_contains_atom_environment(atom_symbol='O', ce_symbol='T:4')) self.assertTrue(lse.uniquely_determines_coordination_environments) self.assertFalse(lse.__ne__(lse)) envs = lse.strategy.get_site_coordination_environments( lse.structure[6]) self.assertEqual(len(envs), 1) self.assertEqual(envs[0][0], 'T:4') multi_strategy = MultiWeightsChemenvStrategy.stats_article_weights_parameters( ) lse_multi = LightStructureEnvironments.from_structure_environments( strategy=multi_strategy, structure_environments=se, valences='undefined') self.assertAlmostEqual( lse_multi.coordination_environments[isite][0]['csm'], 0.009887784240541068) self.assertAlmostEqual( lse_multi.coordination_environments[isite][0]['ce_fraction'], 1.0) self.assertEqual( lse_multi.coordination_environments[isite][0]['ce_symbol'], 'T:4')
def test_coordination_sequences(self): BaTiO3_se_fpath = os.path.join( self.TEST_FILES_DIR, "chemenv", "structure_environments_files", "se_mp-5020.json", ) with open(BaTiO3_se_fpath, "r") as f: dd = json.load(f) se = StructureEnvironments.from_dict(dd) lse = LightStructureEnvironments.from_structure_environments( strategy=SimplestChemenvStrategy(), structure_environments=se) cf = ConnectivityFinder() sc = cf.get_structure_connectivity(light_structure_environments=lse) ccs_oct = sc.get_connected_components(environments_symbols=["O:6"]) ccs_all = sc.get_connected_components( environments_symbols=["O:6", "C:12"]) assert len(ccs_oct) == 1 assert len(ccs_all) == 1 cc_oct = ccs_oct[0] cc_all = ccs_all[0] cc_oct_node = list(cc_oct.graph.nodes())[0] cseq = cc_oct.coordination_sequence(source_node=cc_oct_node, path_size=6) assert cseq == {1: 6, 2: 18, 3: 38, 4: 66, 5: 102, 6: 146} cc_all_oct_node = next(n for n in cc_all.graph.nodes() if n.coordination_environment == "O:6") cc_all_cuboct_node = next(n for n in cc_all.graph.nodes() if n.coordination_environment == "C:12") cseq = cc_all.coordination_sequence(source_node=cc_all_oct_node, path_size=6) assert cseq == {1: 14, 2: 74, 3: 218, 4: 442, 5: 746, 6: 1130} cseq = cc_all.coordination_sequence(source_node=cc_all_cuboct_node, path_size=6) assert cseq == {1: 26, 2: 122, 3: 298, 4: 554, 5: 890, 6: 1306} cseq = cc_all.coordination_sequence(source_node=cc_all_cuboct_node, path_size=6, include_source=True) assert cseq == {0: 1, 1: 26, 2: 122, 3: 298, 4: 554, 5: 890, 6: 1306} cseq = cc_all.coordination_sequence(source_node=cc_all_oct_node, path_size=4, coordination="env:number") assert cseq == { 1: { "O:6": 6, "C:12": 8 }, 2: { "O:6": 26, "C:12": 48 }, 3: { "O:6": 90, "C:12": 128 }, 4: { "O:6": 194, "C:12": 248 }, } cseq = cc_all.coordination_sequence(source_node=cc_all_cuboct_node, path_size=4, coordination="env:number") assert cseq == { 1: { "O:6": 8, "C:12": 18 }, 2: { "O:6": 48, "C:12": 74 }, 3: { "O:6": 128, "C:12": 170 }, 4: { "O:6": 248, "C:12": 306 }, } cseq = cc_all.coordination_sequence( source_node=cc_all_cuboct_node, path_size=4, coordination="env:number", include_source=True, ) assert cseq == { 0: { "C:12": 1 }, 1: { "O:6": 8, "C:12": 18 }, 2: { "O:6": 48, "C:12": 74 }, 3: { "O:6": 128, "C:12": 170 }, 4: { "O:6": 248, "C:12": 306 }, }
def get_chemenv_analysis(struct, distance_cutoff, angle_cutoff): if not struct: raise PreventUpdate struct = self.from_data(struct) kwargs = self.reconstruct_kwargs_from_state( callback_context.inputs) distance_cutoff = kwargs["distance_cutoff"] angle_cutoff = kwargs["angle_cutoff"] # TODO: remove these brittle guard statements, figure out more robust way to handle multiple input types if isinstance(struct, StructureGraph): struct = struct.structure def get_valences(struct): valences = [ getattr(site.specie, "oxi_state", None) for site in struct ] valences = [v for v in valences if v is not None] if len(valences) == len(struct): return valences else: return "undefined" # decide which indices to present to user sga = SpacegroupAnalyzer(struct) symm_struct = sga.get_symmetrized_structure() inequivalent_indices = [ indices[0] for indices in symm_struct.equivalent_indices ] wyckoffs = symm_struct.wyckoff_symbols lgf = LocalGeometryFinder() lgf.setup_structure(structure=struct) se = lgf.compute_structure_environments( maximum_distance_factor=distance_cutoff + 0.01, only_indices=inequivalent_indices, valences=get_valences(struct), ) strategy = SimplestChemenvStrategy(distance_cutoff=distance_cutoff, angle_cutoff=angle_cutoff) lse = LightStructureEnvironments.from_structure_environments( strategy=strategy, structure_environments=se) all_ce = AllCoordinationGeometries() envs = [] unknown_sites = [] for index, wyckoff in zip(inequivalent_indices, wyckoffs): datalist = { "Site": unicodeify_species(struct[index].species_string), "Wyckoff Label": wyckoff, } if not lse.neighbors_sets[index]: unknown_sites.append( f"{struct[index].species_string} ({wyckoff})") continue # represent the local environment as a molecule mol = Molecule.from_sites( [struct[index]] + lse.neighbors_sets[index][0].neighb_sites) mol = mol.get_centered_molecule() mg = MoleculeGraph.with_empty_graph(molecule=mol) for i in range(1, len(mol)): mg.add_edge(0, i) view = html.Div( [ StructureMoleculeComponent( struct_or_mol=mg, disable_callbacks=True, id= f"{struct.composition.reduced_formula}_site_{index}", scene_settings={ "enableZoom": False, "defaultZoom": 0.6 }, )._sub_layouts["struct"] ], style={ "width": "300px", "height": "300px" }, ) env = lse.coordination_environments[index] co = all_ce.get_geometry_from_mp_symbol(env[0]["ce_symbol"]) name = co.name if co.alternative_names: name += f" (also known as {', '.join(co.alternative_names)})" datalist.update({ "Environment": name, "IUPAC Symbol": co.IUPAC_symbol_str, get_tooltip( "CSM", "The continuous symmetry measure (CSM) describes the similarity to an " "ideal coordination environment. It can be understood as a 'distance' to " "a shape and ranges from 0 to 100 in which 0 corresponds to a " "coordination environment that is exactly identical to the ideal one. A " "CSM larger than 5.0 already indicates a relatively strong distortion of " "the investigated coordination environment.", ): f"{env[0]['csm']:.2f}", "Interactive View": view, }) envs.append(get_data_list(datalist)) # TODO: switch to tiles? envs_grouped = [envs[i:i + 2] for i in range(0, len(envs), 2)] analysis_contents = [] for env_group in envs_grouped: analysis_contents.append( Columns([Column(e, size=6) for e in env_group])) if unknown_sites: unknown_sites = html.Strong( f"The following sites were not identified: {', '.join(unknown_sites)}. " f"Please try changing the distance or angle cut-offs to identify these sites, " f"or try an alternative algorithm such as LocalEnv.") else: unknown_sites = html.Span() return html.Div( [html.Div(analysis_contents), html.Br(), unknown_sites])
def run(self, sn): """ Args: sn (SiteNetwork) Returns: ``sn``, with type information. """ # -- Determine local environments # Get an ASE structure with a single mobile site that we'll move around if sn.n_sites == 0: logger.warning("Site network had no sites.") return sn site_struct, site_species = sn[0:1].get_structure_with_sites() pymat_struct = AseAtomsAdaptor.get_structure(site_struct) lgf = cgf.LocalGeometryFinder() site_atom_index = len(site_struct) - 1 coord_envs = [] vertices = [] valences = 'undefined' if self._guess_ionic_bonds: sim_struct = AseAtomsAdaptor.get_structure(sn.structure) valences = np.zeros(shape = len(site_struct), dtype = np.int) bv = BVAnalyzer() try: struct_valences = np.asarray(bv.get_valences(sim_struct)) except ValueError as ve: logger.warning("Failed to compute bond valences: %s" % ve) else: valences = np.zeros(shape = len(site_struct), dtype = np.int) valences[:site_atom_index] = struct_valences[sn.static_mask] mob_val = struct_valences[sn.mobile_mask] if np.any(mob_val != mob_val[0]): logger.warning("Mobile atom estimated valences (%s) not uniform; arbitrarily taking first." % mob_val) valences[site_atom_index] = mob_val[0] finally: valences = list(valences) logger.info("Running site coordination environment analysis...") # Do this once. # __init__ here defaults to disabling structure refinement, so all this # method is doing is making a copy of the structure and setting some # variables to None. lgf.setup_structure(structure = pymat_struct) for site in tqdm(range(sn.n_sites), desc = 'Site'): # Update the position of the site lgf.structure[site_atom_index].coords = sn.centers[site] # Compute structure environments for the site struct_envs = lgf.compute_structure_environments( only_indices = [site_atom_index], valences = valences, additional_conditions = [AdditionalConditions.ONLY_ANION_CATION_BONDS], **self._kwargs ) struct_envs = LightStructureEnvironments.from_structure_environments( strategy = cgf.LocalGeometryFinder.DEFAULT_STRATEGY, structure_environments = struct_envs ) # Store the results # We take the first environment for each site since it's the most likely this_site_envs = struct_envs.coordination_environments[site_atom_index] if len(this_site_envs) > 0: coord_envs.append(this_site_envs[0]) vertices.append( [n['index'] for n in struct_envs.neighbors_sets[site_atom_index][0].neighb_indices_and_images] ) else: coord_envs.append({'ce_symbol' : 'BAD:0', 'ce_fraction' : 0.}) vertices.append([]) del lgf del struct_envs # -- Postprocess # TODO: allow user to ask for full fractional breakdown str_coord_environments = [env['ce_symbol'] for env in coord_envs] # The closer to 1 this is, the better site_type_confidences = np.array([env['ce_fraction'] for env in coord_envs]) coordination_numbers = np.array([int(env['ce_symbol'].split(':')[1]) for env in coord_envs]) assert np.all(coordination_numbers == [len(v) for v in vertices]) typearr = str_coord_environments if self._full_chemenv_site_types else coordination_numbers unique_envs = list(set(typearr)) site_types = np.array([unique_envs.index(t) for t in typearr]) n_types = len(unique_envs) logger.info(("Type " + "{:<8}" * n_types).format(*unique_envs)) logger.info(("# of sites " + "{:<8}" * n_types).format(*np.bincount(site_types))) sn.site_types = site_types sn.vertices = vertices sn.add_site_attribute("coordination_environments", str_coord_environments) sn.add_site_attribute("site_type_confidences", site_type_confidences) sn.add_site_attribute("coordination_numbers", coordination_numbers) return sn