Source code for libcasm.enumerate._ConfigEnumAllOccupations

from typing import Optional

import numpy as np

import libcasm.clusterography as casmclust
import libcasm.configuration as casmconfig
import libcasm.xtal as xtal

from ._enumerate import (
    ConfigEnumAllOccupationsBase,
    make_distinct_cluster_sites,
)
from ._ScelEnum import ScelEnum
from ._SuperConfigEnum import SuperConfigEnum


def _make_sublat_sites(supercell: casmconfig.Supercell, sublats: set[int]):
    converter = supercell.site_index_converter
    sites = set()
    for i in range(supercell.n_sites):
        integral_site_coordinate = converter.integral_site_coordinate(i)
        if integral_site_coordinate.sublattice() in sublats:
            sites.add(i)
    return sites


[docs] class ConfigEnumAllOccupations: """Enumerate configuration occupations""" def __init__( self, prim: casmconfig.Prim, supercell_set: Optional[casmconfig.SupercellSet] = None, ): """ .. rubric:: Constructor Parameters ---------- prim: casmconfig.Prim The Prim supercell_set: Optional[casmconfig.SupercellSet] = None If not None, generated :class:`~casmconfig.Supercell` are constructed by adding in the :class:`~casmconfig.SupercellSet`. """ self._prim = prim self._supercell_set = supercell_set # Set and updated during an enumeration self._background = None self._sites = None self._enum_index = None @property def prim(self) -> casmconfig.Prim: """The Prim""" return self._prim @property def supercell_set(self) -> Optional[casmconfig.SupercellSet]: """If not None, generated :class:`~casmconfig.Supercell` are constructed by adding in the :class:`~casmconfig.SupercellSet`.""" return self._supercell_set @property def background(self) -> Optional[casmconfig.Configuration]: """During enumeration, `background` is set to the current background configuration in which occupations are enumerated.""" return self._background @property def sites(self) -> Optional[set[int]]: """During enumeration, `sites` is set to the linear site indices on which occupations are enumerated in the current background configuration.""" return self._sites @property def enum_index(self) -> Optional[int]: """During enumeration, `enum_index` is incremented to count over the combinations of background configuration and sites on which enumeration is performed, starting from 0.""" return self._enum_index def _begin(self): """Initialize values""" self._background = None self._sites = None self._enum_index = None def _set_motif(self, motif: Optional[casmconfig.Configuration] = None): """Check motif and use volume 1 default configuration if not provided""" if motif is None: T = np.eye(3, dtype=int) if self.supercell_set is not None: record = self.supercell_set.add_by_transformation_matrix_to_super( transformation_matrix_to_super=T, ) supercell = record.supercell else: supercell = casmconfig.Supercell( prim=self.prim, transformation_matrix_to_super=T, ) motif = casmconfig.Configuration(supercell) return motif def _make_supercell_list( self, background: casmconfig.Configuration, supercells: Optional[dict] = None, ): """Make supercell list from `supercells` or just background supercell""" if supercells is None: if self.supercell_set is not None: self.supercell_set.add_supercell(background.supercell) supercell_list = [background.supercell] else: scel_enum = ScelEnum( prim=self.prim, supercell_set=self.supercell_set, ) supercell_list = [ supercell for supercell in scel_enum.by_volume(**supercells) ] return supercell_list def _make_super_backgrounds( self, background: casmconfig.Configuration, supercells: Optional[dict] = None, ): """Fill the background configuration into enumerated supercells, maintaining the orientation of the background configuration. Parameters ---------- background: casmconfig.Configuration The background configuration to fill into enumerated supercells. supercells: Optional[dict] = None Parameters to forward to :func:`ScelEnum.by_volume <libcasm.enumerate.ScelEnum.by_volume>` to specify the supercells that the background configuration will be filled into. If None, only the exact background configuration is returned. Returns ------- super_backgrounds: list[casmconfig.Configuration] A list of :class:`~casmconfig.Configuration` objects, each a distinct super configuration of the background. """ if supercells is None: return [background.copy()] super_config_enum = SuperConfigEnum( prim=self.prim, supercell_set=self.supercell_set, ) super_backgrounds = [] for config in super_config_enum.by_supercell( motif=background, **supercells, ): super_backgrounds.append(config.copy()) return super_backgrounds def _by_site( self, background: casmconfig.Configuration, sites: set[int], skip_non_primitive: bool, skip_equivalents: bool, use_background_invariant_group: bool, which_dofs: Optional[set[str]] = None, ): """Run the inner loop of enumerating occupations on sites in a background This method will set and update self.background, self.sites, and self.enum_index as it yields configurations. Parameters ---------- background: casmconfig.Configuration During enumeration, `background` is set to the current background configuration in which occupations are enumerated. sites: set[int] During enumeration, `sites` is set to the linear site indices on which occupations are enumerated in the current background configuration. skip_non_primitive: bool If True, enumeration skips non-primitive configurations. skip_equivalents: bool If True, enumeration skips non-unique configurations with respect to either (i) the complete set of symmetry operations that leave the supercell lattice vectors invariant (these are canonical configurations), or (ii) the subgroup that leaves the background configuration invariant and does not mix the given sites and other sites. use_background_invariant_group: bool If True, check for uniqueness using the subgroup that leaves the background configuration invariant and does not mix the given sites and other sites; otherwise use the complete set of symmetry operations that leave the supercell lattice vectors invariant. which_dofs: Optional[set[str]] = None When ``skip_equivalents is True`` and ``use_background_invariant_group is True``, the names of the degrees of freedom (DoF) in the background that must be invariant. The default is that all DoF must be invariant. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ if which_dofs is None: which_dofs = set(["all"]) self._background = background self._sites = sites if self._enum_index is None: self._enum_index = 0 else: self._enum_index += 1 config_enum = ConfigEnumAllOccupationsBase( background=background, sites=sites, ) if skip_equivalents: if use_background_invariant_group: canonicalization_group = casmconfig.make_invariant_subgroup( configuration=background, site_indices=sites, which_dofs=which_dofs, ) else: canonicalization_group = None while config_enum.is_valid(): if skip_non_primitive and not casmconfig.is_primitive_configuration( configuration=config_enum.value() ): config_enum.advance() continue if skip_equivalents and not casmconfig.is_canonical_configuration( configuration=config_enum.value(), subgroup=canonicalization_group, ): config_enum.advance() continue yield config_enum.value() config_enum.advance()
[docs] def by_supercell( self, max: Optional[int] = None, min: int = 1, unit_cell: Optional[np.ndarray] = None, dirs: str = "abc", diagonal_only: bool = False, fixed_shape: bool = False, supercells: Optional[dict] = None, motif: Optional[casmconfig.Configuration] = None, skip_non_primitive: bool = True, skip_non_canonical: bool = True, ): """Enumerate all occupations in a series of enumerated supercells Parameters ---------- max: Optional[int] = None The maximum volume superlattice to enumerate. The volume is measured relative the unit cell being used to generate supercells (i.e. the determinant of the `unit_cell` parameter). Is required if `supercells` is None. min: int = 1 The minimum volume superlattice to enumerate. The volume is measured relative the unit cell being used to generate supercells. dirs: str = "abc" A string indicating which lattice vectors to enumerate over. Some combination of 'a', 'b', and 'c', where 'a' indicates the first lattice vector of the unit cell, 'b' the second, and 'c' the third. unit_cell: Optional[np.ndarray] = None, An integer shape=(3,3) transformation matrix `U` allows specifying an alternative unit cell that can be used to generate superlattices of the form `S = (L @ U) @ T`. If None, `U` is set to the identity matrix. diagonal_only: bool = False If true, restrict :math:`T` to diagonal matrices. fixed_shape: bool = False If true, restrict :math:`T` to diagonal matrices with diagonal coefficients :math:`[m, 1, 1]` (1d), :math:`[m, m, 1]` (2d), or :math:`[m, m, m]` (3d), where the dimension is determined from `len(dirs)`. supercells: Optional[dict] = None Parameters to forward to :func:`ScelEnum.by_volume <libcasm.enumerate.ScelEnum.by_volume>` to specify the supercells that the motif configuration will be filled into. .. deprecated:: 2.0a5 Give the :func:`ScelEnum.by_volume <libcasm.enumerate.ScelEnum.by_volume>` parameters directly, or by using `**supercells`, instead of using this parameter. motif: Optional[casmconfig.Configuration] = None The background configuration on which enumeration takes place. The motif is filled into each supercell using :func:`~libcasm.configuration.make_distinct_super_configurations`. If the motif does not tile exactly into a supercell that supercell is skipped. Providing a motif allows enumerating all occupations with other degrees of freedom (DoF) fixed. If None, the default configuration in the volume 1 supercell is used. .. deprecated:: 2.0a5 Use the method :func:`~libcasm.enumerate.ConfigEnumAllOccupations.by_supercell_with_continuous_dof` instead. skip_non_primitive: bool = True If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_non_canonical: bool = True If True, enumeration skips non-canonical configurations with respect to the symmetry operations that leave the supercell lattice vectors invariant. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. All configurations are in the canonical supercell. """ import warnings if max is None: if supercells is None or "max" not in supercells: raise ValueError( "Error in ConfigEnumAllOccupations.by_supercell: " "`max` is required. Could not be obtained from supercells." ) else: max = supercells["max"] warnings.warn( "The `max` parameter is required. " "Obtaining max from supercells, but the 'supercells' parameter is " "deprecated. Give the ScelEnum.by_volume parameters directly, or " "by using `**supercells`, instead of using 'supercells'.", DeprecationWarning, stacklevel=2, ) _supercells = dict( max=max, min=min, unit_cell=unit_cell, dirs=dirs, diagonal_only=diagonal_only, fixed_shape=fixed_shape, ) if supercells is not None: warnings.warn( "The 'supercells' parameter is deprecated. Give the " "ScelEnum.by_volume parameters directly, or by using " "`**supercells`, instead of using 'supercells'.", DeprecationWarning, stacklevel=2, ) _supercells.update(supercells) if motif is not None: import warnings warnings.warn( "The 'motif' parameter is deprecated. Use the method " "'ConfigEnumAllOccupations.by_supercell_with_continuous_dof' instead.", DeprecationWarning, stacklevel=2, ) for config in self.by_supercell_with_continuous_dof( source=motif, **_supercells, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_non_canonical, ): yield config return self._begin() scel_enum = ScelEnum( prim=self.prim, supercell_set=self.supercell_set, ) for supercell in scel_enum.by_volume( **_supercells, ): sites = set(range(supercell.n_sites)) default_config = casmconfig.Configuration(supercell) for config in self._by_site( background=default_config, sites=sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_non_canonical, use_background_invariant_group=False, ): yield config
[docs] def by_supercell_list( self, supercells: list[casmconfig.Supercell], skip_non_primitive: bool = True, skip_non_canonical: bool = True, ): """Enumerate all occupations in a list of supercells explicitly provided Parameters ---------- supercells: list[casmconfig.Supercell] An explicit list of supercells in which to perform enumeration. skip_non_primitive: bool = True If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_non_canonical: bool = True If True, enumeration skips non-canonical configurations with respect to the symmetry operations that leave the supercell lattice vectors invariant. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() for supercell in supercells: if self.supercell_set is not None: self.supercell_set.add_supercell(supercell) sites = set(range(supercell.n_sites)) default_config = casmconfig.Configuration(supercell) for config in self._by_site( background=default_config, sites=sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_non_canonical, use_background_invariant_group=False, ): yield config
[docs] def by_supercell_with_continuous_dof( self, source: casmconfig.Configuration, max: int, min: int = 1, unit_cell: Optional[np.ndarray] = None, dirs: str = "abc", diagonal_only: bool = False, fixed_shape: bool = False, skip_non_primitive: bool = True, skip_equivalents: bool = True, ): """Enumerate all occupations in a series of enumerated supercells, with non-default continuous DoF Parameters ---------- source: casmconfig.Configuration Any global continuous DoF from `source` are set to the same value in all generated supercells. Any local continuous DoF of `source` are filled into each supercell using :func:`~libcasm.configuration.copy_configuration`. If `source` does not tile exactly into a supercell in its canonical form, or any equivalent supercell with respect to the prim factor group, then that supercell is skipped. max: Optional[int] = None The maximum volume superlattice to enumerate. The volume is measured relative the unit cell being used to generate supercells (i.e. the determinant of the `unit_cell` parameter). Is required if `supercells` is None. min: int = 1 The minimum volume superlattice to enumerate. The volume is measured relative the unit cell being used to generate supercells. dirs: str = "abc" A string indicating which lattice vectors to enumerate over. Some combination of 'a', 'b', and 'c', where 'a' indicates the first lattice vector of the unit cell, 'b' the second, and 'c' the third. unit_cell: Optional[np.ndarray] = None, An integer shape=(3,3) transformation matrix `U` allows specifying an alternative unit cell that can be used to generate superlattices of the form `S = (L @ U) @ T`. If None, `U` is set to the identity matrix. diagonal_only: bool = False If true, restrict :math:`T` to diagonal matrices. fixed_shape: bool = False If true, restrict :math:`T` to diagonal matrices with diagonal coefficients :math:`[m, 1, 1]` (1d), :math:`[m, m, 1]` (2d), or :math:`[m, m, m]` (3d), where the dimension is determined from `len(dirs)`. skip_non_primitive: bool = True If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_equivalents: bool = True If True, enumeration skips non-unique configurations with respect to the subgroup that leaves the supercell lattice vectors and continuous DoF of the background configuration invariant. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() supercells = dict( max=max, min=min, unit_cell=unit_cell, dirs=dirs, diagonal_only=diagonal_only, fixed_shape=fixed_shape, ) super_backgrounds = self._make_super_backgrounds( background=source, supercells=supercells, ) # Get continuous DoF which_dofs = set() for _global_dof in self.prim.xtal_prim.global_dof(): which_dofs.add(_global_dof.dofname()) for _sublattice_dof in self.prim.xtal_prim.local_dof(): for _local_dof in _sublattice_dof: which_dofs.add(_local_dof.dofname()) for super_background in super_backgrounds: sites = set(range(super_background.supercell.n_sites)) for config in self._by_site( background=super_background, sites=sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_equivalents, use_background_invariant_group=True, which_dofs=which_dofs, ): yield config
[docs] def by_linear_site_indices( self, background: casmconfig.Configuration, sites: set[int], skip_non_primitive: bool = False, skip_equivalents: bool = True, ): """Enumerate occupation perturbations of a background configuration on specified sites Parameters ---------- background: casmconfig.Configuration The background configuration on which enumeration takes place. sites: set[int] The sites, as linear site indices, on which occupations are enumerated, while all other degrees of freedom (DoF) are fixed. skip_non_primitive: bool = False If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_equivalents: bool = True If True, enumeration skips equivalent configurations with respect to the subgroup that leaves the background configuration invariant and does not mix the given sites and other sites. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() for config in self._by_site( background=background, sites=sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_equivalents, use_background_invariant_group=True, ): yield config
[docs] def by_integral_site_coordinates( self, background: casmconfig.Configuration, sites: list[xtal.IntegralSiteCoordinate], skip_non_primitive: bool = False, skip_equivalents: bool = True, ): """Enumerate occupation perturbations of a background configuration on specified sites Parameters ---------- background: casmconfig.Configuration The background configuration on which enumeration takes place. sites: list[xtal.IntegralSiteCoordinate] The sites on which occupations are enumerated, while all other degrees of freedom (DoF) are fixed. skip_non_primitive: bool = False If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_equivalents: bool = True If True, enumeration skips equivalent configurations with respect to the subgroup that leaves the background configuration invariant and does not mix the given sites and other sites. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() converter = background.supercell.site_index_converter site_indices = set([converter.linear_site_index(site) for site in sites]) for config in self._by_site( background=background, sites=site_indices, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_equivalents, use_background_invariant_group=True, ): yield config
[docs] def by_sublattice( self, background: casmconfig.Configuration, sublats: set[int], supercells: Optional[dict] = None, skip_non_primitive: bool = True, skip_equivalents: bool = True, ): """Enumerate occupation perturbations of a background configuration on specified sublattices Parameters ---------- background: casmconfig.Configuration, The background configuration on which enumeration takes place. This allows fixing other DoF and enumerating all occupations and/or specifying the starting occupation which is perturbed. sublats: set[int] If not None, occupation enumeration only occurs on the specified sublattices. supercells: Optional[dict] = None Parameters to forward to :func:`ScelEnum.by_volume <libcasm.enumerate.ScelEnum.by_volume>` to specify the supercells that the background configuration will be filled into. If None, only the exact background configuration is perturbed. skip_non_primitive: bool = False If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_equivalents: bool = True If True, enumeration skips equivalent configurations with respect to the subgroup that leaves the background configuration invariant and does not mix the selected sublattice sites and other sites. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() super_backgrounds = self._make_super_backgrounds( background=background, supercells=supercells, ) for super_background in super_backgrounds: sublat_sites = _make_sublat_sites(super_background.supercell, sublats) for config in self._by_site( background=super_background, sites=sublat_sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_equivalents, use_background_invariant_group=True, ): yield config
[docs] def by_cluster( self, background: casmconfig.Configuration, cluster_specs: dict, supercells: Optional[dict] = None, skip_non_primitive: bool = False, skip_equivalents: bool = True, ): """Enumerate occupation perturbations of a background configuration on specified clusters Parameters ---------- background: casmconfig.Configuration, The background configuration on which enumeration takes place. This allows fixing other DoF and enumerating all occupations and/or specifying the starting occupation which is perturbed. cluster_specs: dict Parameters used to construct a :class:`~libcasm.clusterography.ClusterSpecs` instance using :func:`~libcasm.clusterography.ClusterSpecs.from_dict`. Specifies orbits of clusters in the infinite crystal. Enumeration is done on all symmetrically distinct clusters in those orbits within the background configurations generated by filling `background` into the supercells specified by the `supercells` parameter. supercells: Optional[dict] = None Parameters to forward to :func:`ScelEnum.by_volume <libcasm.enumerate.ScelEnum.by_volume>` to specify the supercells that the background configuration will be filled into. If None, only the exact background configuration is perturbed. skip_non_primitive: bool = False If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_equivalents: bool = True If True, enumeration skips equivalent configurations with respect to the subgroup that leaves the background configuration invariant and does not mix the cluster sites and other sites. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() super_backgrounds = self._make_super_backgrounds( background=background, supercells=supercells, ) cspecs = casmclust.ClusterSpecs.from_dict( data=cluster_specs, xtal_prim=self.prim.xtal_prim, prim_factor_group=self.prim.factor_group, integral_site_coordinate_symgroup_rep=self.prim.integral_site_coordinate_symgroup_rep, ) orbits = cspecs.make_orbits() for super_background in super_backgrounds: distinct_cluster_sites = make_distinct_cluster_sites( configuration=super_background, orbits=orbits, ) for cluster_sites in distinct_cluster_sites: for config in self._by_site( background=super_background, sites=cluster_sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_equivalents, use_background_invariant_group=True, ): yield config
[docs] def by_cluster_list( self, background: casmconfig.Configuration, clusters: list[casmclust.Cluster], supercells: Optional[dict] = None, skip_non_primitive: bool = False, skip_equivalents: bool = True, ): """Enumerate occupation perturbations of a background configuration on an explicitly provided list of clusters Parameters ---------- background: casmconfig.Configuration, The background configuration on which enumeration takes place. This allows fixing other DoF and enumerating all occupations and/or specifying the starting occupation which is perturbed. clusters: list[Clusters] Each cluster is used to generate an orbit of clusters in the infinite crystal. Enumeration is done on all symmetrically distinct clusters in those orbits within the background configurations generated by filling the supercells specified by the `supercells` parameter. supercells: Optional[dict] = None Parameters to forward to :func:`ScelEnum.by_volume <libcasm.enumerate.ScelEnum.by_volume>` to specify the supercells that the background configuration will be filled into. If None, only the exact background configuration is perturbed. skip_non_primitive: bool = False If True, enumeration skips non-primitive configurations. All DoF are included in the check for primitive configurations. skip_equivalents: bool = True If True, enumeration skips equivalent configurations with respect to the subgroup that leaves the background configuration invariant and does not mix the cluster sites and other sites. Yields ------ config: casmconfig.Configuration A :class:`~casmconfig.Configuration`. """ self._begin() super_backgrounds = self._make_super_backgrounds( background=background, supercells=supercells, ) orbits = [] orbit_prototypes = [] for cluster in clusters: orbit = casmclust.make_periodic_orbit( orbit_element=cluster, integral_site_coordinate_symgroup_rep=self.prim.integral_site_coordinate_symgroup_rep, ) if orbit[0] not in orbit_prototypes: orbit_prototypes.append(orbit[0]) orbits.append(orbit) for super_background in super_backgrounds: distinct_cluster_sites = make_distinct_cluster_sites( configuration=super_background, orbits=orbits, ) for cluster_sites in distinct_cluster_sites: for config in self._by_site( background=super_background, sites=cluster_sites, skip_non_primitive=skip_non_primitive, skip_equivalents=skip_equivalents, use_background_invariant_group=True, ): yield config