Coverage for icet/core/orbit_list.py: 83%
57 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-26 04:12 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-26 04:12 +0000
1"""
2This module provides the OrbitList class.
3"""
5from typing import List, Dict
6from collections import Counter
8import numpy as np
10from _icet import _OrbitList
11from icet.core.orbit import Orbit # noqa
12from ase import Atoms
13from icet.core.local_orbit_list_generator import LocalOrbitListGenerator
14from icet.core.neighbor_list import get_neighbor_lists
15from icet.core.matrix_of_equivalent_positions import \
16 _get_lattice_site_matrix_of_equivalent_positions, \
17 matrix_of_equivalent_positions_from_structure
18from icet.core.structure import Structure
19from icet.tools.geometry import (chemical_symbols_to_numbers,
20 atomic_number_to_chemical_symbol)
21from icet.input_output.logging_tools import logger
23logger = logger.getChild('orbit_list')
26class OrbitList(_OrbitList):
27 """
28 The orbit list object handles an internal list of orbits.
30 An orbit has a list of equivalent sites with the restriction
31 that at least one site is in the cell of the primitive structure.
33 Note
34 ----
35 As a user you will usually not interact directly with objects of this type.
37 Parameters
38 ----------
39 structure
40 This structure will be used to construct a primitive
41 structure on which all the lattice sites in the orbits
42 are based.
43 cutoffs
44 The `i`-th element of this list is the cutoff for orbits with
45 order `i+2`.
46 chemical_symbols
47 List of chemical symbols, each of which must map to an element
48 of the periodic table.
50 The outer list must be the same length as the `structure` object
51 and :attr:`chemical_symbols[i]` will correspond to the allowed species
52 on lattice site ``i``.
53 symprec
54 Tolerance imposed when analyzing the symmetry using spglib.
55 position_tolerance
56 Tolerance applied when comparing positions in Cartesian coordinates.
57 fractional_position_tolerance
58 Tolerance applied when comparing positions in fractional coordinates.
59 """
61 def __init__(self,
62 structure: Atoms,
63 cutoffs: List[float],
64 chemical_symbols: List[List[str]],
65 symprec: float,
66 position_tolerance: float,
67 fractional_position_tolerance: float) -> None:
68 max_cutoff = np.max(cutoffs)
69 # Set up a permutation matrix
70 matrix_of_equivalent_positions, prim_structure, _ \
71 = matrix_of_equivalent_positions_from_structure(structure=structure,
72 cutoff=max_cutoff,
73 position_tolerance=position_tolerance,
74 find_primitive=False,
75 symprec=symprec)
76 prim_structure.allowed_atomic_numbers = [chemical_symbols_to_numbers(syms)
77 for syms in chemical_symbols]
79 logger.info('Done getting matrix_of_equivalent_positions.')
81 # Get a list of neighbor-lists
82 neighbor_lists = get_neighbor_lists(structure=prim_structure, cutoffs=cutoffs,
83 position_tolerance=position_tolerance)
85 logger.info('Done getting neighbor lists.')
87 # Transform matrix_of_equivalent_positions to be in lattice site format
88 pm_lattice_sites = _get_lattice_site_matrix_of_equivalent_positions(
89 structure=prim_structure,
90 matrix_of_equivalent_positions=matrix_of_equivalent_positions,
91 fractional_position_tolerance=fractional_position_tolerance,
92 prune=True)
94 logger.info('Transformation of matrix of equivalent positions'
95 ' to lattice neighbor format completed.')
97 _OrbitList.__init__(self,
98 structure=prim_structure,
99 matrix_of_equivalent_sites=pm_lattice_sites,
100 neighbor_lists=neighbor_lists,
101 position_tolerance=position_tolerance)
102 logger.info('Finished construction of orbit list.')
104 @property
105 def primitive_structure(self):
106 """
107 A copy of the primitive structure to which the lattice sites in
108 the orbits are referenced to.
109 """
110 return self.get_structure().to_atoms()
112 def __str__(self):
113 """ String representation. """
114 s = []
115 s += ['Number of orbits: {}'.format(len(self))]
116 for k, orbit in enumerate(self.orbits):
117 s += [f'Orbit {k}:']
118 s += [f'\t{s_tmp}' for s_tmp in orbit.__str__().split('\n')][:-1]
119 return '\n'.join(s)
121 def __getitem__(self, ind: int):
122 return self.get_orbit(ind)
124 def get_supercell_orbit_list(self,
125 structure: Atoms,
126 fractional_position_tolerance: float):
127 """
128 Returns the orbit list for a supercell structure.
130 Parameters
131 ----------
132 structure
133 Atomic structure.
134 fractional_position_tolerance
135 Tolerance applied when comparing positions in fractional coordinates.
136 """
137 lolg = LocalOrbitListGenerator(
138 self,
139 structure=Structure.from_atoms(structure),
140 fractional_position_tolerance=fractional_position_tolerance)
141 supercell_orbit_list = lolg.generate_full_orbit_list()
142 return supercell_orbit_list
144 def get_cluster_counts(self,
145 structure: Atoms,
146 fractional_position_tolerance: float,
147 orbit_indices: List[int] = None) -> Dict[int, Counter]:
148 """
149 Counts all clusters in a structure by finding their local orbit list.
151 Parameters
152 ----------
153 structure
154 Structure for which to count clusters. This structure needs to
155 be commensurate with the structure this orbit list is based on.
156 fractional_position_tolerance
157 Tolerance applied when comparing positions in fractional coordinates.
158 orbit_indices
159 Indices of orbits, for which counts are requested; if ``None`` all
160 orbits will be counted.
162 Returns
163 -------
164 Dictionary, the keys of which are orbit indices and the values
165 cluster counts. The latter are themselves dicts, with tuples
166 of chemical symbols as keys and the number of such clusters
167 as values.
168 """
169 supercell_orbit_list = self.get_supercell_orbit_list(
170 structure=structure,
171 fractional_position_tolerance=fractional_position_tolerance)
173 # Collect counts for all orbit_indices
174 if orbit_indices is None:
175 orbit_indices = range(len(self))
176 structure_icet = Structure.from_atoms(structure)
177 cluster_counts_full = {}
178 for i in orbit_indices:
179 orbit = supercell_orbit_list.get_orbit(i)
180 counts = orbit.get_cluster_counts(structure_icet)
181 sorted_counts = Counter()
182 for atomic_numbers, count in counts.items():
183 symbols = atomic_number_to_chemical_symbol(atomic_numbers)
184 sorted_symbols = tuple(sorted(symbols))
185 sorted_counts[sorted_symbols] += count
186 cluster_counts_full[i] = sorted_counts
187 return cluster_counts_full