Source code for sqsgenerator.core

import logging
import operator
import functools
import frozendict
import typing as T
from sqsgenerator.core.core import SQSResult, Atom
from sqsgenerator.core.core import __version__, __features__, ATOL, RTOL
from sqsgenerator.core.core import set_log_level, pair_sqs_iteration, pair_analysis
from sqsgenerator.core.core import IterationMode, IterationSettings, BoostLogLevel
from sqsgenerator.core.structure import Structure, structure_to_dict, make_supercell
from sqsgenerator.core.core import default_shell_distances, atoms_from_numbers, atoms_from_symbols, available_species, \
    symbols_from_z, z_from_symbols, build_configuration, ALL_SITES, make_rank as make_rank_, \
    rank_structure as rank_structure_, total_permutations as total_permutations_, \
    compute_prefactors as compute_prefactors_
from sqsgenerator.core.utils import merge, transpose, chunks_of

__all__ = [
    '__version__',
    '__features__',
    'set_log_level',
    'pair_sqs_iteration',
    'IterationMode',
    'IterationSettings',
    'BoostLogLevel',
    'pair_analysis',
    'Structure',
    'structure_to_dict',
    'get_function_logger',
    'Atom',
    'make_supercell',
    'SQSResult',
    'default_shell_distances',
    'total_permutations',
    'rank_structure',
    'atoms_from_numbers',
    'atoms_from_symbols',
    'available_species',
    'symbols_from_z',
    'z_from_symbols',
    'build_configuration',
    'ALL_SITES',
    'make_rank',
    'rank_structure',
    'merge',
    'transpose',
    'chunks_of',
    'ATOL',
    'RTOL'
]

attr = operator.attrgetter
item = operator.itemgetter
method = operator.methodcaller
partial = functools.partial

SQSCoreCallback = T.Callable[[int, SQSResult, int, int], T.Optional[bool]]
SQSCallback = T.Callable[[int, Structure, SQSResult, int, int], T.Optional[bool]]

TRACE = 5
logging.addLevelName(TRACE, 'TRACE')

log_levels = frozendict.frozendict(
    trace=TRACE,
    debug=logging.DEBUG,
    info=logging.INFO,
    warning=logging.WARNING,
    error=logging.ERROR
)


def set_core_log_level(level: int = logging.WARNING) -> T.NoReturn:
    level_map = {
        TRACE: BoostLogLevel.trace,
        logging.DEBUG: BoostLogLevel.debug,
        logging.INFO: BoostLogLevel.info,
        logging.WARNING: BoostLogLevel.warning,
        logging.ERROR: BoostLogLevel.error
    }
    set_log_level(level_map.get(level))


def get_function_logger(f: T.Callable) -> logging.Logger:
    return logging.getLogger(f.__module__ + '.' + f.__name__)


set_core_log_level(logging.WARNING)


[docs]def total_permutations(struct: Structure, which: T.Optional[T.Iterable[int]] = None) -> int: """ Compute the total number of total permutations, by looking at the configuration of {struct}. The number of of permutations are given by the multinomial coefficient. Let :math:`N_1, N_2, \ldots, N_m` the number of the atoms of the :math:`m^{\\text{th}}` species. Therefore, one can generate .. math:: N_{\\text{perms}} = \dfrac{N!}{\prod_m^M N_m} \quad \\text{where} \quad \sum_m^M N_m = N permutations in total :param struct: the structure with the configuration to calculate the number of total permutations :type struct: Structure :param which: the indices of the lattice positions to choose (default is ``None``) :type which: Optional[Iterable[int]] :return: the rank of the structure of the sub-lattice selected by {which} :rtype: int """ which = tuple(range(len(struct))) if which is None else which return total_permutations_(struct[which])
[docs]def make_rank(struct: Structure, rank: int, configuration: T.Optional[T.Iterable[str]] = None, which: T.Optional[T.Iterable[int]] = None) -> Structure: """ Uses the lattice and positions from {struct} and generates the {rank}-th permutation (in lexicographical order) of {configuration} and creates a new structure with it. If {which} is given, the selected lattices positions are used, therefore the length of {configuration} must match the length of {which} :param struct: the basis structure from which the new structure will be created :type struct: Structure :param rank: the index of the permutation sequence :type rank: int :param configuration: the species sequence from which the {rank}-th permutation sequence. Use this parameter when you want to create a rank and simultaneously want to distribute new atomic species (default is ``None``) :type configuration: Optional[Iterable[str]] :param which: the indices of the lattice positions to choose (default is ``None``) :type which: Optional[Iterable[int]] :raises IndexError: if the length of {which} does not match the length of {configuration} :raises ValueError: if the rank is less than 1 or greater than the number of :py:func:`total_permutations` :return: the structure with {rank}-th permutation sequence as configuration :rtype: Structure """ configuration = struct.symbols.tolist() if configuration is None else configuration msg = f"the rank of a configuration must me 1 <= rank <= total_permutations() = " \ f"{total_permutations(struct.slice_with_species(configuration, which))}" if rank <= 0: raise ValueError(msg) which = tuple(range(len(struct))) if which is None else which try: unranked_configuration = make_rank_(configuration, rank) except IndexError: # the C++ extension raises an IndexError, we want to propagate it as a Value error raise ValueError(msg) return struct.with_species(unranked_configuration, which=which)
[docs]def rank_structure(struct: Structure, which: T.Optional[T.Iterable[int]] = None) -> int: """ Computes the index (in lexicographical order) of the configuration sequence. If {which} is specified only those sites are selected to compute the permutation rank :param struct: the structure which carries the configuration sequence :type struct: Structure :param which: the indices of the lattice positions to choose (default is ``None``) :type which: Optional[Iterable[int]] :return: the index of the configuration sequence :rtype: int """ which = tuple(range(len(struct))) if which is None else which return rank_structure_(struct[which])
def compute_prefactors(structure: Structure, composition: T.Dict[str, T.Any], shell_weights: T.Dict[int, float], shell_distances: T.List[float], atol: float = 1e-3, rtol: float = 1e-5): return compute_prefactors_(structure, composition, shell_weights, shell_distances, atol, rtol)