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