예제 #1
0
    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())
예제 #2
0
 def setUp(self):
     self.lgf = LocalGeometryFinder()
     self.lgf.setup_parameters(centering_type='standard')
     self.strategies = [
         SimplestChemenvStrategy(),
         SimpleAbundanceChemenvStrategy()
     ]
예제 #3
0
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
예제 #4
0
    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)
예제 #5
0
    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)
예제 #6
0
    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)
예제 #7
0
 def from_preset(preset):
     """
     Use a standard collection of CE types and
     choose your ChemEnv neighbor-finding strategy.
     Args:
         preset (str): preset types ("simple" or
                       "multi_weights").
     Returns:
         ChemEnvSiteFingerprint object from a preset.
     """
     cetypes = [
         'S:1', 'L:2', 'A:2', 'TL:3', 'TY:3', 'TS:3', 'T:4', 'S:4', 'SY:4',
         'SS:4', 'PP:5', 'S:5', 'T:5', 'O:6', 'T:6', 'PP:6', 'PB:7', 'ST:7',
         'ET:7', 'FO:7', 'C:8', 'SA:8', 'SBT:8', 'TBT:8', 'DD:8', 'DDPN:8',
         'HB:8', 'BO_1:8', 'BO_2:8', 'BO_3:8', 'TC:9', 'TT_1:9', 'TT_2:9',
         'TT_3:9', 'HD:9', 'TI:9', 'SMA:9', 'SS:9', 'TO_1:9', 'TO_2:9',
         'TO_3:9', 'PP:10', 'PA:10', 'SBSA:10', 'MI:10', 'S:10', 'H:10',
         'BS_1:10', 'BS_2:10', 'TBSA:10', 'PCPA:11', 'H:11', 'SH:11',
         'CO:11', 'DI:11', 'I:12', 'PBP:12', 'TT:12', 'C:12', 'AC:12',
         'SC:12', 'S:12', 'HP:12', 'HA:12', 'SH:13', 'DD:20'
     ]
     lgf = LocalGeometryFinder()
     lgf.setup_parameters(
         centering_type='centroid',
         include_central_site_in_centroid=True,
         structure_refinement=lgf.STRUCTURE_REFINEMENT_NONE)
     if preset == "simple":
         return ChemEnvSiteFingerprint(
             cetypes,
             SimplestChemenvStrategy(distance_cutoff=1.4, angle_cutoff=0.3),
             lgf)
     elif preset == "multi_weights":
         return ChemEnvSiteFingerprint(
             cetypes,
             MultiWeightsChemenvStrategy.stats_article_weights_parameters(),
             lgf)
     else:
         raise RuntimeError('unknown neighbor-finding strategy preset.')
예제 #8
0
    def test_strategies(self):
        simplest_strategy_1 = SimplestChemenvStrategy()
        simplest_strategy_2 = SimplestChemenvStrategy(distance_cutoff=1.5,
                                                      angle_cutoff=0.5)
        self.assertFalse(simplest_strategy_1 == simplest_strategy_2)
        simplest_strategy_1_from_dict = SimplestChemenvStrategy.from_dict(
            simplest_strategy_1.as_dict())
        self.assertTrue(simplest_strategy_1, simplest_strategy_1_from_dict)

        effective_csm_estimator = {
            "function": "power2_inverse_decreasing",
            "options": {
                "max_csm": 8.0
            },
        }
        self_csm_weight = SelfCSMNbSetWeight()
        surface_definition = {
            "type": "standard_elliptic",
            "distance_bounds": {
                "lower": 1.1,
                "upper": 1.9
            },
            "angle_bounds": {
                "lower": 0.1,
                "upper": 0.9
            },
        }
        surface_definition_2 = {
            "type": "standard_elliptic",
            "distance_bounds": {
                "lower": 1.1,
                "upper": 1.9
            },
            "angle_bounds": {
                "lower": 0.1,
                "upper": 0.95
            },
        }
        da_area_weight = DistanceAngleAreaNbSetWeight(
            weight_type="has_intersection",
            surface_definition=surface_definition,
            nb_sets_from_hints="fallback_to_source",
            other_nb_sets="0_weight",
            additional_condition=DistanceAngleAreaNbSetWeight.AC.ONLY_ACB,
        )
        da_area_weight_2 = DistanceAngleAreaNbSetWeight(
            weight_type="has_intersection",
            surface_definition=surface_definition_2,
            nb_sets_from_hints="fallback_to_source",
            other_nb_sets="0_weight",
            additional_condition=DistanceAngleAreaNbSetWeight.AC.ONLY_ACB,
        )
        weight_estimator = {
            "function": "smootherstep",
            "options": {
                "delta_csm_min": 0.5,
                "delta_csm_max": 3.0
            },
        }
        symmetry_measure_type = "csm_wcs_ctwcc"
        delta_weight = DeltaCSMNbSetWeight(
            effective_csm_estimator=effective_csm_estimator,
            weight_estimator=weight_estimator,
            symmetry_measure_type=symmetry_measure_type,
        )
        bias_weight = CNBiasNbSetWeight.linearly_equidistant(weight_cn1=1.0,
                                                             weight_cn13=4.0)
        bias_weight_2 = CNBiasNbSetWeight.linearly_equidistant(weight_cn1=1.0,
                                                               weight_cn13=5.0)
        angle_weight = AngleNbSetWeight()
        nad_weight = NormalizedAngleDistanceNbSetWeight(
            average_type="geometric", aa=1, bb=1)
        multi_weights_strategy_1 = MultiWeightsChemenvStrategy(
            dist_ang_area_weight=da_area_weight,
            self_csm_weight=self_csm_weight,
            delta_csm_weight=delta_weight,
            cn_bias_weight=bias_weight,
            angle_weight=angle_weight,
            normalized_angle_distance_weight=nad_weight,
            symmetry_measure_type=symmetry_measure_type,
        )
        multi_weights_strategy_2 = MultiWeightsChemenvStrategy(
            dist_ang_area_weight=da_area_weight,
            self_csm_weight=self_csm_weight,
            delta_csm_weight=delta_weight,
            cn_bias_weight=bias_weight_2,
            angle_weight=angle_weight,
            normalized_angle_distance_weight=nad_weight,
            symmetry_measure_type=symmetry_measure_type,
        )
        multi_weights_strategy_3 = MultiWeightsChemenvStrategy(
            dist_ang_area_weight=da_area_weight_2,
            self_csm_weight=self_csm_weight,
            delta_csm_weight=delta_weight,
            cn_bias_weight=bias_weight,
            angle_weight=angle_weight,
            normalized_angle_distance_weight=nad_weight,
            symmetry_measure_type=symmetry_measure_type,
        )
        multi_weights_strategy_1_from_dict = MultiWeightsChemenvStrategy.from_dict(
            multi_weights_strategy_1.as_dict())

        self.assertTrue(
            multi_weights_strategy_1 == multi_weights_strategy_1_from_dict)
        self.assertFalse(simplest_strategy_1 == multi_weights_strategy_1)
        self.assertFalse(multi_weights_strategy_1 == multi_weights_strategy_2)
        self.assertFalse(multi_weights_strategy_1 == multi_weights_strategy_3)
        self.assertFalse(multi_weights_strategy_2 == multi_weights_strategy_3)
예제 #9
0
    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')
예제 #10
0
 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 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")
예제 #12
0
    def test_strategies(self):
        simplest_strategy_1 = SimplestChemenvStrategy()
        simplest_strategy_2 = SimplestChemenvStrategy(distance_cutoff=1.5, angle_cutoff=0.5)
        self.assertFalse(simplest_strategy_1 == simplest_strategy_2)
        simplest_strategy_1_from_dict = SimplestChemenvStrategy.from_dict(simplest_strategy_1.as_dict())
        self.assertTrue(simplest_strategy_1, simplest_strategy_1_from_dict)

        effective_csm_estimator = {'function': 'power2_inverse_decreasing',
                                   'options': {'max_csm': 8.0}}
        self_csm_weight = SelfCSMNbSetWeight()
        surface_definition = {'type': 'standard_elliptic',
                              'distance_bounds': {'lower': 1.1, 'upper': 1.9},
                              'angle_bounds': {'lower': 0.1, 'upper': 0.9}}
        surface_definition_2 = {'type': 'standard_elliptic',
                              'distance_bounds': {'lower': 1.1, 'upper': 1.9},
                              'angle_bounds': {'lower': 0.1, 'upper': 0.95}}
        da_area_weight = DistanceAngleAreaNbSetWeight(weight_type='has_intersection',
                                                      surface_definition=surface_definition,
                                                      nb_sets_from_hints='fallback_to_source',
                                                      other_nb_sets='0_weight',
                                                      additional_condition=DistanceAngleAreaNbSetWeight.AC.ONLY_ACB)
        da_area_weight_2 = DistanceAngleAreaNbSetWeight(weight_type='has_intersection',
                                                      surface_definition=surface_definition_2,
                                                      nb_sets_from_hints='fallback_to_source',
                                                      other_nb_sets='0_weight',
                                                      additional_condition=DistanceAngleAreaNbSetWeight.AC.ONLY_ACB)
        weight_estimator = {'function': 'smootherstep',
                            'options': {'delta_csm_min': 0.5,
                                        'delta_csm_max': 3.0}}
        symmetry_measure_type = 'csm_wcs_ctwcc'
        delta_weight = DeltaCSMNbSetWeight(effective_csm_estimator=effective_csm_estimator,
                                           weight_estimator=weight_estimator,
                                           symmetry_measure_type=symmetry_measure_type)
        bias_weight = CNBiasNbSetWeight.linearly_equidistant(weight_cn1=1.0, weight_cn13=4.0)
        bias_weight_2 = CNBiasNbSetWeight.linearly_equidistant(weight_cn1=1.0, weight_cn13=5.0)
        angle_weight = AngleNbSetWeight()
        nad_weight = NormalizedAngleDistanceNbSetWeight(average_type='geometric', aa=1, bb=1)
        multi_weights_strategy_1 = MultiWeightsChemenvStrategy(dist_ang_area_weight=da_area_weight,
                                                               self_csm_weight=self_csm_weight,
                                                               delta_csm_weight=delta_weight,
                                                               cn_bias_weight=bias_weight,
                                                               angle_weight=angle_weight,
                                                               normalized_angle_distance_weight=nad_weight,
                                                               symmetry_measure_type=symmetry_measure_type)
        multi_weights_strategy_2 = MultiWeightsChemenvStrategy(dist_ang_area_weight=da_area_weight,
                                                               self_csm_weight=self_csm_weight,
                                                               delta_csm_weight=delta_weight,
                                                               cn_bias_weight=bias_weight_2,
                                                               angle_weight=angle_weight,
                                                               normalized_angle_distance_weight=nad_weight,
                                                               symmetry_measure_type=symmetry_measure_type)
        multi_weights_strategy_3 = MultiWeightsChemenvStrategy(dist_ang_area_weight=da_area_weight_2,
                                                               self_csm_weight=self_csm_weight,
                                                               delta_csm_weight=delta_weight,
                                                               cn_bias_weight=bias_weight,
                                                               angle_weight=angle_weight,
                                                               normalized_angle_distance_weight=nad_weight,
                                                               symmetry_measure_type=symmetry_measure_type)
        multi_weights_strategy_1_from_dict = MultiWeightsChemenvStrategy.from_dict(multi_weights_strategy_1.as_dict())

        self.assertTrue(multi_weights_strategy_1 == multi_weights_strategy_1_from_dict)
        self.assertFalse(simplest_strategy_1 == multi_weights_strategy_1)
        self.assertFalse(multi_weights_strategy_1 == multi_weights_strategy_2)
        self.assertFalse(multi_weights_strategy_1 == multi_weights_strategy_3)
        self.assertFalse(multi_weights_strategy_2 == multi_weights_strategy_3)
예제 #13
0
    def test_strategies(self):
        simplest_strategy_1 = SimplestChemenvStrategy()
        simplest_strategy_2 = SimplestChemenvStrategy(distance_cutoff=1.5,
                                                      angle_cutoff=0.5)
        self.assertFalse(simplest_strategy_1 == simplest_strategy_2)
        simplest_strategy_1_from_dict = SimplestChemenvStrategy.from_dict(
            simplest_strategy_1.as_dict())
        self.assertTrue(simplest_strategy_1, simplest_strategy_1_from_dict)

        effective_csm_estimator = {
            'function': 'power2_inverse_decreasing',
            'options': {
                'max_csm': 8.0
            }
        }
        self_csm_weight = SelfCSMNbSetWeight()
        surface_definition = {
            'type': 'standard_elliptic',
            'distance_bounds': {
                'lower': 1.1,
                'upper': 1.9
            },
            'angle_bounds': {
                'lower': 0.1,
                'upper': 0.9
            }
        }
        surface_definition_2 = {
            'type': 'standard_elliptic',
            'distance_bounds': {
                'lower': 1.1,
                'upper': 1.9
            },
            'angle_bounds': {
                'lower': 0.1,
                'upper': 0.95
            }
        }
        da_area_weight = DistanceAngleAreaNbSetWeight(
            weight_type='has_intersection',
            surface_definition=surface_definition,
            nb_sets_from_hints='fallback_to_source',
            other_nb_sets='0_weight',
            additional_condition=DistanceAngleAreaNbSetWeight.AC.ONLY_ACB)
        da_area_weight_2 = DistanceAngleAreaNbSetWeight(
            weight_type='has_intersection',
            surface_definition=surface_definition_2,
            nb_sets_from_hints='fallback_to_source',
            other_nb_sets='0_weight',
            additional_condition=DistanceAngleAreaNbSetWeight.AC.ONLY_ACB)
        weight_estimator = {
            'function': 'smootherstep',
            'options': {
                'delta_csm_min': 0.5,
                'delta_csm_max': 3.0
            }
        }
        symmetry_measure_type = 'csm_wcs_ctwcc'
        delta_weight = DeltaCSMNbSetWeight(
            effective_csm_estimator=effective_csm_estimator,
            weight_estimator=weight_estimator,
            symmetry_measure_type=symmetry_measure_type)
        bias_weight = CNBiasNbSetWeight.linearly_equidistant(weight_cn1=1.0,
                                                             weight_cn13=4.0)
        bias_weight_2 = CNBiasNbSetWeight.linearly_equidistant(weight_cn1=1.0,
                                                               weight_cn13=5.0)
        angle_weight = AngleNbSetWeight()
        nad_weight = NormalizedAngleDistanceNbSetWeight(
            average_type='geometric', aa=1, bb=1)
        multi_weights_strategy_1 = MultiWeightsChemenvStrategy(
            dist_ang_area_weight=da_area_weight,
            self_csm_weight=self_csm_weight,
            delta_csm_weight=delta_weight,
            cn_bias_weight=bias_weight,
            angle_weight=angle_weight,
            normalized_angle_distance_weight=nad_weight,
            symmetry_measure_type=symmetry_measure_type)
        multi_weights_strategy_2 = MultiWeightsChemenvStrategy(
            dist_ang_area_weight=da_area_weight,
            self_csm_weight=self_csm_weight,
            delta_csm_weight=delta_weight,
            cn_bias_weight=bias_weight_2,
            angle_weight=angle_weight,
            normalized_angle_distance_weight=nad_weight,
            symmetry_measure_type=symmetry_measure_type)
        multi_weights_strategy_3 = MultiWeightsChemenvStrategy(
            dist_ang_area_weight=da_area_weight_2,
            self_csm_weight=self_csm_weight,
            delta_csm_weight=delta_weight,
            cn_bias_weight=bias_weight,
            angle_weight=angle_weight,
            normalized_angle_distance_weight=nad_weight,
            symmetry_measure_type=symmetry_measure_type)
        multi_weights_strategy_1_from_dict = MultiWeightsChemenvStrategy.from_dict(
            multi_weights_strategy_1.as_dict())

        self.assertTrue(
            multi_weights_strategy_1 == multi_weights_strategy_1_from_dict)
        self.assertFalse(simplest_strategy_1 == multi_weights_strategy_1)
        self.assertFalse(multi_weights_strategy_1 == multi_weights_strategy_2)
        self.assertFalse(multi_weights_strategy_1 == multi_weights_strategy_3)
        self.assertFalse(multi_weights_strategy_2 == multi_weights_strategy_3)
예제 #14
0
        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])
예제 #15
0
        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])
예제 #16
0
    def test_strategies(self):
        simplest_strategy = SimplestChemenvStrategy()
        self.assertTrue(simplest_strategy.uniquely_determines_coordination_environments)
        self.assertAlmostEqual(
            simplest_strategy.continuous_symmetry_measure_cutoff, 10.0
        )
        self.assertAlmostEqual(simplest_strategy.distance_cutoff, 1.4)
        self.assertAlmostEqual(simplest_strategy.angle_cutoff, 0.3)

        simplest_strategy = SimplestChemenvStrategy(
            distance_cutoff=1.3,
            angle_cutoff=0.45,
            continuous_symmetry_measure_cutoff=8.26,
        )
        self.assertAlmostEqual(
            simplest_strategy.continuous_symmetry_measure_cutoff, 8.26
        )
        self.assertAlmostEqual(simplest_strategy.distance_cutoff, 1.3)
        self.assertAlmostEqual(simplest_strategy.angle_cutoff, 0.45)

        simplest_strategy.set_option("distance_cutoff", 1.5)
        self.assertAlmostEqual(simplest_strategy.distance_cutoff, 1.5)

        with self.assertRaises(ValueError) as cm:
            simplest_strategy.set_option("distance_cutoff", 0.5)
        self.assertEqual(
            str(cm.exception), "Distance cutoff should be between 1.0 and +infinity"
        )

        simplest_strategy.set_option("angle_cutoff", 0.2)
        self.assertAlmostEqual(simplest_strategy.angle_cutoff, 0.2)

        with self.assertRaises(ValueError) as cm:
            simplest_strategy.set_option("angle_cutoff", 1.5)
        self.assertEqual(
            str(cm.exception), "Angle cutoff should be between 0.0 and 1.0"
        )

        simplest_strategy.setup_options(
            {
                "distance_cutoff": 1.4,
                "additional_condition": 3,
                "continuous_symmetry_measure_cutoff": 8.5,
            }
        )
        self.assertAlmostEqual(simplest_strategy.distance_cutoff, 1.4)
        self.assertAlmostEqual(
            simplest_strategy.continuous_symmetry_measure_cutoff, 8.5
        )
        self.assertEqual(simplest_strategy.additional_condition, 3)

        with self.assertRaises(ValueError) as cm:
            simplest_strategy.setup_options(
                {"continuous_symmetry_measure_cutoff": -0.1}
            )
        self.assertEqual(
            str(cm.exception),
            "Continuous symmetry measure limits should be between 0.0 and 100.0",
        )

        with self.assertRaises(ValueError) as cm:
            simplest_strategy.setup_options(
                {"continuous_symmetry_measure_cutoff": 100.1}
            )
        self.assertEqual(
            str(cm.exception),
            "Continuous symmetry measure limits should be between 0.0 and 100.0",
        )
예제 #17
0
    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"}