diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index ce9b17b..0000000 --- a/.coveragerc +++ /dev/null @@ -1,10 +0,0 @@ -[run] -source = bsym -omit = - */python?.?/* - */lib-python/?.?/*.py - */lib_pypy/_*.py - */site-packages/ordereddict.py - */site-packages/nose/* - */unittest2/* - docs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 09d3cf6..27f6f1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,45 +1,55 @@ -name: Build - +name: build +# Controls when the action will run on: [push, pull_request] +# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: tests: runs-on: ubuntu-latest timeout-minutes: 30 strategy: matrix: - python-version: ['3.9','3.10','3.11','3.12'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Install python dependencies - run: | - pip install --upgrade pip - pip install numpy - pip install -r requirements.txt - pip install mypy - pip install -r tests/notebook_tests/requirements.txt - pip install pytest - pip install pytest-cov - pip install . - pip list - - name: Run tests - run: | - pytest tests/integration_tests - pytest tests/notebook_tests - pytest tests/unit_tests --cov-config=.coveragerc --cov=bsym --cov-report lcov - - name: Coveralls GitHub Action - uses: coverallsapp/github-action@v2.3.0 - with: - file: ./coverage.lcov - github-token: ${{ secrets.COVERALLS_REPO_TOKEN }} - - name: Install type stubs - run: | - python -m pip install types-tqdm - - name: Static type checking - run: | - mypy bsym + - uses: actions/checkout@v4 + + - name: Set up Python ${{matrix.python-version}} + uses: actions/setup-python@v5 + with: + python-version: ${{matrix.python-version}} + cache: 'pip' + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + # Install package with dev dependencies + pip install -e ".[dev]" + pip list + + - name: Run tests + run: | + pytest --cov-config=.coveragerc --cov=bsym --cov-report lcov + + - name: Coveralls GitHub Action + uses: coverallsapp/github-action@v2.3.6 + with: + path-to-lcov: ./coverage.lcov + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel: true + flag-name: python-${{ matrix.python-version }} + + - name: Static type checking + run: | + mypy bsym + # This job finalizes the parallel coverage reports + finish: + needs: tests + if: ${{ always() }} + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@v2.3.6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel-finished: true diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c51c5a3..7b6fadb 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,7 +8,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.9" + python: "3.10" apt_packages: - pandoc # Add pandoc as a system dependency @@ -17,12 +17,9 @@ sphinx: configuration: docs/source/conf.py fail_on_warning: false -# Optionally build your docs in additional formats such as PDF -formats: - - pdf - python: install: - method: pip path: . - - requirements: docs/requirements.txt + extra_requirements: + - docs diff --git a/CHANGELOG,md b/CHANGELOG,md new file mode 100644 index 0000000..701347d --- /dev/null +++ b/CHANGELOG,md @@ -0,0 +1,48 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2.0.0] - 2025-11-02 + +### Added +- Varying composition enumeration via `unique_configurations_by_composition` method +- `generate_partitions` utility for integer partitioning +- `compute_mapping_vectors` for species permutation mapping +- `satisfies_bounds` for occupancy constraint validation +- Comprehensive progress tracking with nested progress bars +- Verbose output options for composition enumeration +- `unique_structure_substitutions_by_composition` in pymatgen interface +- Multi-level disorder enumeration documentation and examples +- Complete documentation restructure with separate Theory and User Guide sections +- Type hints throughout the codebase with mypy static type checking in CI + +### Changed +- **BREAKING**: Minimum Python version raised to 3.10 +- **BREAKING**: Removed `ColourOperation` class (moved to `feature/colour-operations` branch) +- Optimised Configuration storage using `np.int8` arrays +- Implemented batched symmetry operations with cached index mappings +- Migrated from individual loops to vectorised NumPy operations +- Updated CI to GitHub Actions with Python 3.10-3.14 support +- CI now uses pytest as test runner (unittest test suite maintained) +- Converted narrative documentation from reStructuredText to Markdown +- Species exchange symmetry optimisation reduces analyses by 40-50% + +### Performance +- Substantially improved configuration enumeration performance +- Example: 2×2×2 supercell of TiOF₂ shows 4.7× speedup (35.6s → 7.6s) with 13% memory overhead +- Vectorised operations and optimised data types throughout + +### Documentation +- Complete restructure: Getting Started, Theory, User Guide, API Reference +- New theory documents explaining mathematical foundations +- Practical Jupyter notebook tutorials with executable examples +- Expanded README with installation, testing, and usage examples +- Added multi-level disorder enumeration examples + +### Development +- Added mypy static type checking to CI pipeline +- Modernised GitHub Actions workflow with pip caching and parallel coverage +- Updated to pyproject.toml-based configuration diff --git a/README.md b/README.md index ac308a5..cbdae47 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,91 @@ +# bsym + [![PyPI version](https://badge.fury.io/py/bsym.svg)](https://badge.fury.io/py/bsym) [![DOI](https://zenodo.org/badge/19279643.svg)](https://zenodo.org/badge/latestdoi/19279643) [![status](http://joss.theoj.org/papers/6696543fc631bf66feb99a9cde808a39/status.svg)](http://joss.theoj.org/papers/6696543fc631bf66feb99a9cde808a39) [![Coverage Status](https://coveralls.io/repos/github/bjmorgan/bsym/badge.svg?branch=master)](https://coveralls.io/github/bjmorgan/bsym?branch=master) [![Documentation Status](https://readthedocs.org/projects/bsym/badge/?version=latest)](http://bsym.readthedocs.io/en/latest/?badge=latest) - `bsym` is a basic Python symmetry module. It consists of core classes that describe configuration vector spaces, their symmetry operations, and specific configurations of objects within these spaces. The module also contains an interface for working with [`pymatgen`](http://pymatgen.org) `Structure` objects, to allow simple generation of disordered symmetry-inequivalent structures from a symmetric parent crystal structure. -Usage examples are provided in the [documentation](http://bsym.readthedocs.io/en/latest) -API documentation is [here][API]. +Usage examples are provided in the [documentation](http://bsym.readthedocs.io/en/latest). +API documentation is [here][API]. Source code is available at [https://github.com/bjmorgan/bsym][github]. +## Requirements + +`bsym` requires Python 3.10 or later. ## Installation -``` +### Standard Installation + +Install from PyPI: +```bash pip install bsym ``` -Or download the latest release from [GitHub](httpsL//github.com/bjmorgan/bsym/releases), and install -``` +### Installation from Source + +Download the latest release from [GitHub](https://github.com/bjmorgan/bsym/releases), and install: +```bash cd bsym -python setup.py install +pip install . ``` -Or clone the latest development version -``` +Or clone the latest development version: +```bash git clone git@github.com:bjmorgan/bsym.git +cd bsym +pip install . ``` -and install the same way. -``` + +### Development Installation + +To install with development dependencies (for running tests, type checking, building docs, etc.): +```bash +git clone git@github.com:bjmorgan/bsym.git cd bsym -python setup.py install +pip install -e ".[dev]" ``` +This installs `bsym` in editable mode with additional tools for development. + ## Tests -Manual tests can be run using +Tests use pytest. After installing with development dependencies, run: +```bash +pytest ``` -python -m unittest discover + +For verbose output: +```bash +pytest -v +``` + +To run specific test files: +```bash +pytest tests/unit_tests/test_symmetry_group.py ``` -The code has been tested with Python versions 3.5 and above. +## Example Usage + +### Enumerating Symmetry-Inequivalent Structures + +`bsym` can enumerate symmetry-inequivalent structures for disordered materials. Here's an example using a `pymatgen` `Structure` as input: +```python +from bsym.interface.pymatgen import unique_structure_substitutions +unique_structures = unique_structure_substitutions( + parent_structure, + 'X', # Sites to substitute + {'O': 8, 'F': 16} # 8 oxygen, 16 fluorine +) +print(f"Found {len(unique_structures)} unique structures") +``` ## Documentation -An overview of the capabilities of `bsym` along with example code is provided in the [codumentation](http://bsym.readthedocs.io/en/latest/). -## I want to generate a set of symmetry-inequivalent crystal structures -`bsym` can enumerate symmetry-inequivalent crystal structures generated by partial substitutions, starting from a parent structure described by a `pymatgen` `Structure` object. -Example code is in the [documentation](http://bsym.readthedocs.io/en/latest/). -Also refer to the [`bsym.interface.pymatgen.unique_structure_substitutions`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.unique_structure_substitutions) documentation. +An overview of the capabilities of `bsym` along with example code is provided in the [documentation](http://bsym.readthedocs.io/en/latest/). ## Citing `bsym` @@ -58,8 +94,7 @@ This code can be cited as: Morgan, Benjamin J. (2017). *bsym - a Basic Symmetry Module*. The Journal of Open Source Software. http://doi.org/10.21105/joss.00370 ### BibTeX - -``` +```bibtex @article{Morgan_JOSS2017b, doi = {10.21105/joss.00370}, url = {https://doi.org/10.21105/joss.00370}, diff --git a/bsym/__init__.py b/bsym/__init__.py index 8ec2d41..4a93b0a 100644 --- a/bsym/__init__.py +++ b/bsym/__init__.py @@ -5,7 +5,6 @@ from bsym.configuration import Configuration from bsym.configuration_space import ConfigurationSpace from bsym.coordinate_config_space import CoordinateConfigSpace -from bsym.colour_operation import ColourOperation from bsym.version import __version__ diff --git a/bsym/bsym.py b/bsym/bsym.py index 38773eb..18663db 100644 --- a/bsym/bsym.py +++ b/bsym/bsym.py @@ -1,6 +1,6 @@ import warnings import sys -warnings.warn( "\nYou are trying to import bsym.bsym, which is not compatible with this version of bsym.\nPlease check the updated API information at https://github.com/bjmorgan/bsym,\n or download a compatible 0.1.* release at https://github.com/bjmorgan/bsym/releases.\n" ) +warnings.warn( "\nYou are trying to import bsym.bsym, which is not compatible with this version of bsym.\nPlease check the updated API information at https://github.com/bjmorgan/bsym,\n or download a compatible release at https://github.com/bjmorgan/bsym/releases.\n" ) sys.exit() diff --git a/bsym/colour_operation.py b/bsym/colour_operation.py deleted file mode 100644 index 13422d7..0000000 --- a/bsym/colour_operation.py +++ /dev/null @@ -1,113 +0,0 @@ -from bsym import Configuration, SymmetryOperation -import numpy as np - -class ColourOperation( SymmetryOperation ): - """ - This class subclasses `SymmetryOperation`. - It defines a class of object for performing a compound operation on a - configuration. - First, a matrix transform applied to the configuration - vector space, equivlant to a `SymmetryOperation`. - Second, a colour mapping, that allows objects to be replaced in the - configuration vector space. - """ - - def __init__( self, matrix, colour_mapping, label=None ): - """ - Initialise a `ColourOperation` object. - A `ColourOperation` object behaves similarly to a `SymmetryOperation`, but has - an additional `colour_mapping` attribute, which is a `list` of ``dict`s, - describing a per-site mapping between objects. - - Args: - matrix (numpy.matrix|numpy.ndarray|list): 1D vector as either a - `numpy.matrix`, `numpy.ndarray`, or `list` containing the site mappings - for this symmetry operation. - colour_mapping (list[dict]): A `list` of `dict`s, that describe per-site - object mappings. - label (default=None) (str): optional string label for this `SymmetryOperation` object. - - Returns: - None - - Example: - - >>> matrix = np.array( [[1, 0], [0, 1]] ) - >>> colour_mapping = [ { 0: 1, 1: 0 }, { 0: 0, 1: 1 } ] - >>> ColourOperation( matrix, colour_mapping ) - - """ - super().__init__( matrix, label ) - self.colour_mapping = colour_mapping - - @classmethod - def from_vector( cls, vector, colour_mapping, count_from_zero=False, label=None ): - """ - Initialise a ColourOperation from a vector of site mappings. - - Args: - vector (list): vector of integers defining a symmetry operation mapping. - colour_mapping (list[dict]): A `list` of `dict`s, that describe per-site object mappings. - count_from_zero (default = False) (bool): set to True if the site index counts from zero. - label (default=None) (str): optional string label for this `SymmetryOperation` object. - - Returns: - a new SymmetryOperation object - """ - if not count_from_zero: - vector = [ x - 1 for x in vector ] - dim = len( vector ) - matrix = np.zeros( ( dim, dim ) ) - for index, element in enumerate( vector ): - matrix[ element, index ] = 1 - new_colour_operation = cls( matrix, colour_mapping=colour_mapping, label=label ) - return new_colour_operation - - def operate_on( self, configuration ): - """ - Return the Configuration generated by appliying this colour operation - - Args: - configuration (Configuration): the configuration / occupation vector to operate on. - - Returns: - (Configuration): the new configuration. - """ - if not isinstance( configuration, Configuration ): - raise TypeError - new_configuration = Configuration( self.matrix.dot( configuration.vector ) ) - return Configuration( [ d[value] for value, d in zip( new_configuration.vector, self.colour_mapping ) ] ) - - def __mul__( self, other ): - """ - Operate on another object with this `ColourOperation`. - - Args: - other (ColourOperation, SymmetryOperation, Configuration): the other object (colour operation, symmetry operation, configuration, or matrix). - - Returns: - (ColourOperation): a new `ColourOperation` instance with the resultant matrix and colour_mapping.. - (Configuration): if `other` is a `Configuration`. - """ - if isinstance( other, ColourOperation ): - new_matrix = self.matrix.dot( other.matrix ) - new_mapping = [ {} for d in self.colour_mapping ] - for i, (this_mapping, other_mapping) in enumerate( zip( self.colour_mapping, other.colour_mapping ) ): - for key in this_mapping.keys(): - new_mapping[i][key] = this_mapping[ other_mapping[ key ] ] - return ColourOperation( new_matrix, colour_mapping=new_mapping ) - elif isinstance( other, SymmetryOperation ): - return ColourOperation( self.matrix.dot( other.matrix ), colour_mapping=self.colour_mapping ) - elif isinstance( other, Configuration ): - return self.operate_on( other ) - else: - print( self.__class__, other.__class__ ) - raise TypeError - - def invert( self, label=None ): - # TODO - raise NotImplementedError - - def __repr__( self ): - label = self.label if self.label else '---' - return 'ColourOperation\nlabel(' + label + ")\n" + "\n".join( [ row.__str__() + ' ' + mapping.__repr__() for row, mapping in zip( self.matrix, self.colour_mapping ) ] ) diff --git a/bsym/configuration.py b/bsym/configuration.py index 78c5eb6..c4479f6 100644 --- a/bsym/configuration.py +++ b/bsym/configuration.py @@ -30,9 +30,9 @@ class Configuration: """ def __init__(self, vector: list[int] | NDArray[np.int_]) -> None: - self.count = None - self.lowest_numeric_representation = None - self.vector = np.array(vector) + self.count: int | None = None + self.lowest_numeric_representation: int | None = None + self.vector: np.ndarray = np.asarray(vector, dtype=np.int8) def __eq__(self, other: object) -> bool: if not isinstance(other, Configuration): @@ -161,20 +161,19 @@ def as_number(self) -> int: return as_number(self.vector) @classmethod - def from_tuple(cls, this_tuple) -> Configuration: + def from_tuple(cls, configuration_tuple): """ - Construct a :any:`Configuration` from a `tuple`, - e.g.:: - - Configuration.from_tuple( ( 1, 1, 0 ) ) - + Create a Configuration from a tuple. + + Configurations are stored as int8 arrays, supporting species labels 0-255. + Args: - this_tuple (tuple): The tuple used to construct this :any:`Configuration`. - + configuration_tuple: Tuple of configuration values (0-255). + Returns: - (:any:`Configuration`): The new :any:`Configuration`. + Configuration: New configuration object. """ - return cls(this_tuple) + return cls(np.array(configuration_tuple, dtype=np.int8)) def tolist(self) -> list[int]: """ @@ -223,7 +222,48 @@ def map_objects(self, objects: list) -> dict[int, Any]: for key in set(self.vector): sorted_objects[key] = [o for k, o in zip(self.vector, objects) if k == key] return sorted_objects - + + @staticmethod + def tuple_to_bytes(tup: tuple) -> bytes: + """ + Convert configuration tuple to bytes. + + Used for initial permutation checking in enumerate_configurations. + + Args: + tup: Configuration as tuple. + + Returns: + bytes: Byte representation for hashing. + """ + return np.array(tup, dtype=np.int8).tobytes() + + @staticmethod + def array_to_bytes(arr: np.ndarray) -> bytes: + """ + Convert configuration array to bytes. + + Assumes array is already int8. Used for transformations. + + Args: + arr: Configuration as int8 numpy array. + + Returns: + bytes: Byte representation for hashing. + """ + return arr.tobytes() + + def as_bytes(self) -> bytes: + """Get byte representation of this configuration.""" + return Configuration.array_to_bytes(self.vector) + + def get_byte_equivalents(self, symmetry_group) -> set[bytes]: + """Get byte representations of all symmetry-equivalent configurations.""" + transformed_vectors = self.vector[symmetry_group.unique_index_mappings] + byte_equivalents = set( + Configuration.array_to_bytes(vec) for vec in transformed_vectors + ) + return byte_equivalents def as_number(a: list[int] | NDArray[np.int_]) -> int: tot = 0 diff --git a/bsym/configuration_space.py b/bsym/configuration_space.py index 83f42a3..3a34cc4 100644 --- a/bsym/configuration_space.py +++ b/bsym/configuration_space.py @@ -1,13 +1,19 @@ from bsym.permutations import flatten_list, unique_permutations, number_of_unique_permutations +from bsym.partitions import compute_mapping_vector from bsym import Configuration, SymmetryGroup, SymmetryOperation +from bsym.partitions import generate_partitions, satisfies_bounds import numpy as np from itertools import combinations_with_replacement from collections import Counter from tqdm import tqdm, tqdm_notebook +from typing import Iterator + class ConfigurationSpace: - def __init__( self, objects, symmetry_group=None ): + def __init__(self, + objects: list, + symmetry_group: SymmetryGroup | None = None) -> None: """ Create a :any:`ConfigurationSpace` object. @@ -19,7 +25,7 @@ def __init__( self, objects, symmetry_group=None ): None """ # Check that all properties have compatible dimensions - self.dim = len( objects ) + self.dim = len(objects) self.objects = objects if symmetry_group: for so in symmetry_group.symmetry_operations: @@ -27,46 +33,57 @@ def __init__( self, objects, symmetry_group=None ): raise ValueError self.symmetry_group = symmetry_group else: - self.symmetry_group = SymmetryGroup( symmetry_operations=[ SymmetryOperation( np.identity( self.dim, dtype=int ), label='E' ) ] ) + self.symmetry_group = SymmetryGroup( + symmetry_operations=[ + SymmetryOperation(np.identity(self.dim, dtype=int), label='E') + ] + ) - def __repr__( self ): + def __repr__(self) -> str: + to_return: str to_return = "ConfigurationSpace\n" to_return += self.objects.__repr__() + "\n" - to_return += "\n".join( self.symmetry_group.__repr__().split("\n")[1:] ) + to_return += "\n".join(self.symmetry_group.__repr__().split("\n")[1:]) return to_return - def enumerate_configurations( self, generator, verbose=False ): + def enumerate_configurations(self, generator, verbose=False): """ Find all symmetry inequivalent configurations within the set produced by `generator`. - + Args: generator (:obj:`generator`): Generator object, that yields the configurations to search through. verbose (opt:default=False): Print verbose output. - + Returns: unique_configurations (list): A list of :any:`Configuration` objects, for each symmetry - inequivalent configuration. + inequivalent configuration. """ - working = True seen = set() unique_configurations = [] - using_tqdm = hasattr( generator, 'postfix' ) + using_tqdm = hasattr(generator, 'postfix') + for new_permutation in generator: - if permutation_as_config_number( new_permutation ) not in seen: - config = Configuration.from_tuple( new_permutation ) - numeric_equivalents = set( config.numeric_equivalents( self.symmetry_group.symmetry_operations ) ) - config.count = len( numeric_equivalents ) - [ seen.add( i ) for i in numeric_equivalents ] - unique_configurations.append( config ) + perm_as_bytes = Configuration.tuple_to_bytes(new_permutation) + if perm_as_bytes not in seen: + config = Configuration.from_tuple(new_permutation) + byte_equivalents = config.get_byte_equivalents(self.symmetry_group) + config.count = len(byte_equivalents) + seen.update(byte_equivalents) + unique_configurations.append(config) if using_tqdm: - generator.set_postfix( found=len(unique_configurations) ) + generator.set_postfix(found=len(unique_configurations)) + if verbose: - print( 'unique configurations: {} / {}'.format( len( unique_configurations ), len( seen ) ) ) - return( unique_configurations ) + print('unique configurations: {} / {}'.format(len(unique_configurations), len(seen))) + + return unique_configurations - def unique_configurations( self, site_distribution, verbose=False, show_progress=False ): + def unique_configurations(self, + site_distribution, + verbose=False, + show_progress=False): """ Find the symmetry inequivalent configurations for a given population of objects. @@ -87,21 +104,24 @@ def unique_configurations( self, site_distribution, verbose=False, show_progress unique_configurations (list): A list of :any:`Configuration` objects, for each symmetry inequivalent configuration. """ - s = flatten_list( [ [ key ] * site_distribution[ key ] for key in site_distribution ] ) - total_permutations = number_of_unique_permutations( s ) + s = flatten_list([[key] * site_distribution[key] for key in site_distribution]) + total_permutations = number_of_unique_permutations(s) if verbose: - print( 'total number of sites: ' + str( sum( site_distribution.values() ) ) ) - print( 'using {:d} symmetry operations.'.format( len( self.symmetry_group.symmetry_operations ) ) ) - print( 'evaluating {:d} unique permutations.'.format( total_permutations ) ) - generator = unique_permutations( s ) + print('total number of sites: ' + str( sum( site_distribution.values()))) + print('using {:d} symmetry operations.'.format( len( self.symmetry_group.symmetry_operations))) + print('evaluating {:d} unique permutations.'.format( total_permutations)) + generator: Iterator[tuple[int, ...]] = unique_permutations(s) if show_progress: - if show_progress=='notebook': - generator = tqdm_notebook( generator, total=total_permutations, unit=' permutations' ) - else: - generator = tqdm( generator, total=total_permutations, unit=' permutations' ) - return self.enumerate_configurations( generator, verbose=verbose ) + TqdmClass = tqdm_notebook if show_progress == 'notebook' else tqdm + generator = TqdmClass( # type: ignore[assignment] + generator, + total=total_permutations, + unit=' permutations', + mininterval=0.1 + ) + return self.enumerate_configurations(generator, verbose=verbose) - def unique_colourings( self, colours, verbose=False ): + def unique_colourings(self, colours, verbose=False): """ Find the symmetry inequivalent colourings for a given number of 'colours'. @@ -115,13 +135,132 @@ def unique_colourings( self, colours, verbose=False ): """ generator = colourings_generator( colours, self.dim ) return self.enumerate_configurations( generator, verbose=verbose ) + + def unique_configurations_by_composition(self, + n_species: int, + bounds: dict[int, tuple[int|None, int|None]] | None = None, + verbose: bool = False, + show_progress: bool | str = False + ) -> dict[tuple[int, ...], list[Configuration]]: + """[docstring unchanged]""" + from bsym.partitions import compute_mapping_vector + + n_sites = self.dim + all_partitions = generate_partitions(n_sites, n_species) + + # Initialize progress bar without pre-counting (avoids iterator exhaustion) + if show_progress: + TqdmClass = tqdm_notebook if show_progress == 'notebook' else tqdm + progress_bar = TqdmClass( + desc="Compositions", + unit=" compositions", + mininterval=0.1 + ) + + results = {} + partitions_analyzed = 0 + + for partition in all_partitions: + canonical = partition + + # Get all permutations for this partition + all_perms = list(unique_permutations(partition)) + + # Filter by bounds + valid_perms = [] + for perm in all_perms: + composition_dict = {i: count for i, count in enumerate(perm)} + if bounds is None or satisfies_bounds(composition_dict, bounds): + valid_perms.append(perm) + + if not valid_perms: + continue + + partitions_analyzed += 1 + + if verbose and not show_progress: + print(f"Processing partition {partition}...") + + # Build site_distribution for canonical + site_distribution = { + species: count + for species, count in enumerate(canonical) + if count > 0 + } + + # Analyze canonical - pass through show_progress + canonical_configs = self.unique_configurations( + site_distribution=site_distribution, + verbose=False, + show_progress=show_progress # Changed: pass through + ) + + if verbose and not show_progress: + print(f" Found {len(canonical_configs)} unique configurations") + + # Add results for each valid permutation + for perm in valid_perms: + if perm == canonical: + results[canonical] = canonical_configs + else: + mapping = compute_mapping_vector(canonical, perm) + relabeled = [apply_species_mapping(config, mapping) + for config in canonical_configs] + results[perm] = relabeled + + if show_progress: + progress_bar.update(1) + + if show_progress: + progress_bar.close() + + if verbose: + print(f"\nSummary:") + print(f" Analyzed {partitions_analyzed} partitions") + print(f" Generated {len(results)} compositions") + print(f" Total unique configurations: {sum(len(configs) for configs in results.values())}") + + return results +def apply_species_mapping(config, mapping_vector): + """ + Apply species permutation to a Configuration. + + Creates a new Configuration where each species index is remapped according + to the mapping vector. This is used to generate configurations for non-canonical + compositions by relabeling species from canonical composition results. + + Args: + config (Configuration): Configuration object with species indices. + mapping_vector (list[int]): 0-indexed list where mapping_vector[i] gives + the new species index for current species i. + e.g., [1, 0] swaps species 0 and 1. + + Returns: + Configuration: New Configuration with relabeled species. The count + (degeneracy) is preserved from the original configuration. + + Example: + >>> config = Configuration([0, 0, 1]) + >>> config.count = 2 + >>> mapping = [1, 0] # Swap species 0 and 1 + >>> result = apply_species_mapping(config, mapping) + >>> list(result) + [1, 1, 0] + >>> result.count + 2 + """ + new_config_list = [mapping_vector[species_idx] for species_idx in config.vector] + new_config = Configuration(new_config_list) + new_config.count = config.count + return new_config + def colourings_generator( colours, dim ): for s in combinations_with_replacement( colours, dim ): for new_permutation in unique_permutations( s ): yield new_permutation -def permutation_as_config_number( p ): +def permutation_as_config_number(p): """ A numeric representation of a numeric list. diff --git a/bsym/coordinate_config_space.py b/bsym/coordinate_config_space.py index 893c194..2344259 100644 --- a/bsym/coordinate_config_space.py +++ b/bsym/coordinate_config_space.py @@ -1,46 +1,63 @@ -from bsym import ConfigurationSpace +from __future__ import annotations + import numpy as np +from bsym.symmetry_group import SymmetryGroup +from bsym.configuration_space import ConfigurationSpace + -class CoordinateConfigSpace( ConfigurationSpace ): +class CoordinateConfigSpace(ConfigurationSpace): """ - A :any:`CoordinateConfigSpace` object is a :any`ConfigurationSpace` that has an associated + A :any:`CoordinateConfigSpace` object is a :any:`ConfigurationSpace` that has an associated set of coordinates. Each vector in the configuration vector space has a corresponding coordinate. """ - - def __init__( self, coordinates, symmetry_group=None, objects=None ): + + def __init__( + self, + coordinates: np.ndarray, + symmetry_group: SymmetryGroup | None = None, + objects: np.ndarray | list[int] | None = None + ) -> None: """ Create a :any:`CoordinateConfigSpace` object. - + Args: - coordinates (np.array): The set of coordinates that describe the vector space of this configuration space. - symmetry_group (:any:`SymmetryGroup`): The set of symmetry operations describing the symmetries of this configuration space. - + coordinates: The set of coordinates that describe the vector space of this + configuration space. + symmetry_group: The set of symmetry operations describing the symmetries of + this configuration space. + objects: Optional array or list of objects to represent the coordinates. If None, + creates objects as np.arange(len(coordinates)) + 1. + Returns: None """ if objects is None: # Create a set of objects to represent the coordinates. - objects = np.arange( len( coordinates ) ) + 1 - super().__init__( objects, symmetry_group ) + objects = np.arange(len(coordinates)) + 1 + # Convert to list for parent class + objects_list = objects.tolist() if isinstance(objects, np.ndarray) else list(objects) + super().__init__(objects_list, symmetry_group) self.coordinates = coordinates - - def unique_coordinates( self, site_distribution, verbose=False ): + + def unique_coordinates( + self, + site_distribution: dict[int, int], + verbose: bool = False + ) -> list[dict[int, np.ndarray]]: """ Find the symmetry inequivalent coordinates for a given site occupation. - + Args: - site_distribution (dict): A dictionary that defines the number of each object - to be arranged in this system. - - e.g. for a structure with four sites, with two occupied (denoted `1`) - and two unoccupied (denoted `0`):: - - { 1: 2, 0: 2 } - verbose (opt:default=False): Print verbose output. - + site_distribution: A dictionary that defines the number of each object + to be arranged in this system. + e.g. for a structure with four sites, with two occupied (denoted `1`) + and two unoccupied (denoted `0`):: + {1: 2, 0: 2} + verbose: Print verbose output. + Returns: - unique_coordinates (list[dict]): A list of dicts. Each dict describes the set of coordinates for each site type. + A list of dicts. Each dict describes the set of coordinates for each site type. """ - unique_configs = self.unique_configurations( site_distribution, verbose=verbose ) - unique_coordinates = [ u.map_objects( self.coordinates ) for u in unique_configs ] - return unique_coordinates + unique_configs = self.unique_configurations(site_distribution, verbose=verbose) + unique_coordinates = [u.map_objects(self.coordinates) for u in unique_configs] + return unique_coordinates \ No newline at end of file diff --git a/bsym/interface/pymatgen.py b/bsym/interface/pymatgen.py index 1e2f805..2047791 100644 --- a/bsym/interface/pymatgen.py +++ b/bsym/interface/pymatgen.py @@ -1,237 +1,314 @@ -from pymatgen.symmetry.analyzer import SpacegroupAnalyzer, SpacegroupOperations, PointGroupAnalyzer # type: ignore -from pymatgen.util.coord import coord_list_mapping_pbc, coord_list_mapping # type: ignore -from pymatgen.core.lattice import Lattice # type: ignore -from pymatgen.core.structure import Molecule, Structure # type: ignore +from __future__ import annotations + +from typing import Callable, Any +from pymatgen.symmetry.analyzer import SpacegroupAnalyzer, SpacegroupOperations, PointGroupAnalyzer +from pymatgen.util.coord import coord_list_mapping_pbc, coord_list_mapping +from pymatgen.core.lattice import Lattice +from pymatgen.core.structure import Molecule, Structure +from pymatgen.core.operations import SymmOp from bsym import SpaceGroup, SymmetryOperation, ConfigurationSpace, PointGroup from copy import copy from functools import partial import numpy as np -def structure_cartesian_coordinates_mapping( structure, symmop ): + +def structure_cartesian_coordinates_mapping( + structure: Structure, + symmop: SymmOp +) -> np.ndarray: """ Maps the coordinates of pymatgen ``Structure`` according to a ``SymmOp`` symmetry operation. Args: - structure (``Structure``): The pymatgen ``Structure``. - symmop (``SymmOp``): The pymatgen symmetry operation object. + structure: The pymatgen ``Structure``. + symmop: The pymatgen symmetry operation object. - Returns - (np.array): The mapped Cartesian coordinates. + Returns: + The mapped Cartesian coordinates. """ - return structure.lattice.get_cartesian_coords( symmop.operate_multi( structure.frac_coords ) ) + return structure.lattice.get_cartesian_coords(symmop.operate_multi(structure.frac_coords)) + -def molecule_cartesian_coordinates_mapping( molecule, symmop ): +def molecule_cartesian_coordinates_mapping( + molecule: Molecule, + symmop: SymmOp +) -> np.ndarray: """ Maps the coordinates of pymatgen ``Molecule`` according to a ``SymmOp`` symmetry operation. Args: - molecule (``Structure``): The pymatgen ``Molecule``. - symmop (``SymmOp``): The pymatgen symmetry operation object. + molecule: The pymatgen ``Molecule``. + symmop: The pymatgen symmetry operation object. - Returns - (np.array): The mapped Cartesian coordinates. + Returns: + The mapped Cartesian coordinates. """ - return symmop.operate_multi( molecule.cart_coords ) + return symmop.operate_multi(molecule.cart_coords) + -def structure_mapping_list( new_structure, mapping_structure, atol ): +def structure_mapping_list( + new_structure: Structure, + mapping_structure: Structure, + atol: float +) -> list[int]: """ Gives the index mapping between two pymatgen ``Structure`` objects. Args: - new_structure (``Structure``): - mapping_structure (``Structure``): + new_structure: The new structure. + mapping_structure: The structure to map to. + atol: Absolute tolerance for coordinate matching. Returns: - list of indices such that mapping_structure.sites[indices] == new_structure.sites + List of indices such that mapping_structure.sites[indices] == new_structure.sites """ - return coord_list_mapping_pbc( new_structure.frac_coords, mapping_structure.frac_coords, atol=atol ) - -def molecule_mapping_list( new_molecule, mapping_molecule, atol ): + return coord_list_mapping_pbc( # type: ignore[no-any-return] + new_structure.frac_coords, + mapping_structure.frac_coords, + atol=atol + ) + + +def molecule_mapping_list( + new_molecule: Molecule, + mapping_molecule: Molecule, + atol: float +) -> list[int]: """ Gives the index mapping between two pymatgen ``Molecule`` objects. Args: - new_structure (``Molecule``): - mapping_structure (``Molecule``): + new_molecule: The new molecule. + mapping_molecule: The molecule to map to. + atol: Absolute tolerance for coordinate matching. Returns: - list of indices such that mapping_molecule.sites[indices] == new_molecule.sites + List of indices such that mapping_molecule.sites[indices] == new_molecule.sites """ - return coord_list_mapping( new_molecule.cart_coords, mapping_molecule.cart_coords, atol=atol ) - -def unique_symmetry_operations_as_vectors_from_structure( structure, verbose=False, subset=None, atol=1e-5 ): + return coord_list_mapping( # type: ignore[no-any-return] + new_molecule.cart_coords, + mapping_molecule.cart_coords, + atol=atol + ) + + +def unique_symmetry_operations_as_vectors_from_structure( + structure: Structure | Molecule, + verbose: bool = False, + subset: list[int] | None = None, + atol: float = 1e-5 +) -> list[list[int]]: """ - Uses `pymatgen`_ symmetry analysis to find the minimum complete set of symmetry operations for the space group of a structure. + Uses `pymatgen`_ symmetry analysis to find the minimum complete set of symmetry operations + for the space group of a structure. Args: - structure (pymatgen ``Structure``): structure to be analysed. - subset (Optional [list]): list of atom indices to be used for generating the symmetry operations. - atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. + structure: Structure or Molecule to be analysed. + verbose: Print verbose output including space/point group information. + subset: List of atom indices to be used for generating the symmetry operations. + atol: Tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each + symmetry operation. Returns: - (list[list]): a list of lists, containing the symmetry operations as vector mappings. + A list of lists, containing the symmetry operations as vector mappings. .. _pymatgen: http://pymatgen.org .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc - """ - if isinstance( structure, Structure ): - instantiate_structure = partial( Structure, lattice=structure.lattice, coords_are_cartesian=True ) - coord_mapping = structure_cartesian_coordinates_mapping - mapping_list = structure_mapping_list - symmetry_analyzer = SpacegroupAnalyzer( structure ) + if isinstance(structure, Structure): + instantiate_structure: Callable[..., Structure | Molecule] = partial( + Structure, lattice=structure.lattice, coords_are_cartesian=True + ) + coord_mapping: Callable[[Structure | Molecule, SymmOp], np.ndarray] = structure_cartesian_coordinates_mapping # type: ignore[assignment] + mapping_list: Callable[[Structure | Molecule, Structure | Molecule, float], list[int]] = structure_mapping_list # type: ignore[assignment] + symmetry_analyzer: SpacegroupAnalyzer | PointGroupAnalyzer = SpacegroupAnalyzer(structure) if verbose: - print( "The space group for this structure is {}".format( symmetry_analyzer.get_space_group_symbol()) ) - elif isinstance( structure, Molecule ): + if isinstance(symmetry_analyzer, SpacegroupAnalyzer): + print(f"The space group for this structure is {symmetry_analyzer.get_space_group_symbol()}") + elif isinstance(structure, Molecule): instantiate_structure = Molecule - coord_mapping = molecule_cartesian_coordinates_mapping - mapping_list = molecule_mapping_list - symmetry_analyzer = PointGroupAnalyzer( structure, tolerance=atol ) + coord_mapping = molecule_cartesian_coordinates_mapping # type: ignore[assignment] + mapping_list = molecule_mapping_list # type: ignore[assignment] + symmetry_analyzer = PointGroupAnalyzer(structure, tolerance=atol) if verbose: - print( "The point group for this structure is {}".format( symmetry_analyzer.get_pointgroup()) ) + print(f"The point group for this structure is {symmetry_analyzer.sch_symbol}") else: - raise ValueError( 'structure argument should be a Structure or Molecule object' ) + raise ValueError('structure argument should be a Structure or Molecule object') + symmetry_operations = symmetry_analyzer.get_symmetry_operations() - mappings = [] + mappings: list[list[int]] = [] + + mapping_structure: Structure | Molecule if subset: - species_subset = [ spec for i,spec in enumerate( structure.species ) if i in subset ] - cart_coords_subset = [ coord for i, coord in enumerate( structure.cart_coords ) if i in subset ] - mapping_structure = instantiate_structure( species=species_subset, coords=cart_coords_subset ) + species_subset = [spec for i, spec in enumerate(structure.species) if i in subset] + cart_coords_subset = [coord for i, coord in enumerate(structure.cart_coords) if i in subset] + mapping_structure = instantiate_structure(species=species_subset, coords=cart_coords_subset) else: mapping_structure = structure + for symmop in symmetry_operations: - cart_coords = coord_mapping( mapping_structure, symmop ) - new_structure = instantiate_structure( species=mapping_structure.species, coords=cart_coords ) - new_mapping = [ x+1 for x in list( mapping_list( new_structure, mapping_structure, atol ) ) ] + cart_coords = coord_mapping(mapping_structure, symmop) + new_structure = instantiate_structure(species=mapping_structure.species, coords=cart_coords) + new_mapping = [x + 1 for x in list(mapping_list(new_structure, mapping_structure, atol))] if new_mapping not in mappings: - mappings.append( new_mapping ) + mappings.append(new_mapping) + return mappings -def space_group_symbol_from_structure( structure ): + +def space_group_symbol_from_structure(structure: Structure) -> str: """ - Returns the symbol for the space group defined by this structure. + Returns the symbol for the space group defined by this structure. Args: - structure (pymatgen ``Structure``): The input structure. - + structure: The input structure. + Returns: - (str): The space group symbol. + The space group symbol. """ - symmetry_analyzer = SpacegroupAnalyzer( structure ) + symmetry_analyzer = SpacegroupAnalyzer(structure) symbol = symmetry_analyzer.get_space_group_symbol() return symbol -def space_group_from_structure( structure, subset=None, atol=1e-5 ): + +def space_group_from_structure( + structure: Structure, + subset: list[int] | None = None, + atol: float = 1e-5 +) -> SpaceGroup: """ - Generates a ``SpaceGroup`` object from a `pymatgen` ``Structure``. + Generates a ``SpaceGroup`` object from a `pymatgen` ``Structure``. Args: - structure (pymatgen ``Structure``): structure to be used to define the :any:`SpaceGroup`. - subset (Optional [list]): list of atom indices to be used for generating the symmetry operations. - atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. + structure: Structure to be used to define the :any:`SpaceGroup`. + subset: List of atom indices to be used for generating the symmetry operations. + atol: Tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each + symmetry operation. Returns: - a new :any:`SpaceGroup` instance + A new :any:`SpaceGroup` instance .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc - """ - mappings = unique_symmetry_operations_as_vectors_from_structure( structure, subset=subset, atol=atol ) - symmetry_operations = [ SymmetryOperation.from_vector( m ) for m in mappings ] - return SpaceGroup( symmetry_operations=symmetry_operations ) + mappings = unique_symmetry_operations_as_vectors_from_structure(structure, subset=subset, atol=atol) + symmetry_operations = [SymmetryOperation.from_vector(m) for m in mappings] + return SpaceGroup(symmetry_operations=symmetry_operations) -def point_group_from_molecule( molecule, subset=None, atol=1e-5 ): + +def point_group_from_molecule( + molecule: Molecule, + subset: list[int] | None = None, + atol: float = 1e-5 +) -> PointGroup: """ - Generates a ``PointGroup`` object from a `pymatgen` ``Molecule``. + Generates a ``PointGroup`` object from a `pymatgen` ``Molecule``. Args: - molecule (pymatgen ``Molecule``): molecule to be used to define the :any:`PointGroup`. - subset (Optional [list]): list of atom indices to be used for generating the symmetry operations. - atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. + molecule: Molecule to be used to define the :any:`PointGroup`. + subset: List of atom indices to be used for generating the symmetry operations. + atol: Tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each + symmetry operation. Returns: - a new :any:`PointGroup` instance + A new :any:`PointGroup` instance .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping - """ - molecule = Molecule( molecule.species, molecule.cart_coords - molecule.center_of_mass ) - mappings = unique_symmetry_operations_as_vectors_from_structure( molecule, subset=subset, atol=atol ) - symmetry_operations = [ SymmetryOperation.from_vector( m ) for m in mappings ] - return PointGroup( symmetry_operations=symmetry_operations ) + molecule = Molecule(molecule.species, molecule.cart_coords - molecule.center_of_mass) + mappings = unique_symmetry_operations_as_vectors_from_structure(molecule, subset=subset, atol=atol) + symmetry_operations = [SymmetryOperation.from_vector(m) for m in mappings] + return PointGroup(symmetry_operations=symmetry_operations) + -def configuration_space_from_structure( structure, subset=None, atol=1e-5 ): +def configuration_space_from_structure( + structure: Structure, + subset: list[int] | None = None, + atol: float = 1e-5 +) -> ConfigurationSpace: """ Generate a ``ConfigurationSpace`` object from a `pymatgen` ``Structure``. Args: - structure (pymatgen ``Structure``): structure to be used to define the :any:`ConfigurationSpace`. - subset (Optional [list]): list of atom indices to be used for generating the configuration space. - atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. - + structure: Structure to be used to define the :any:`ConfigurationSpace`. + subset: List of atom indices to be used for generating the configuration space. + atol: Tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each + symmetry operation. + Returns: - a new :any:`ConfigurationSpace` instance. + A new :any:`ConfigurationSpace` instance. .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc - """ - space_group = space_group_from_structure( structure, subset=subset, atol=atol ) + space_group = space_group_from_structure(structure, subset=subset, atol=atol) if subset is None: - subset = list( range( 1, len( structure )+1 ) ) - config_space = ConfigurationSpace( objects=subset, symmetry_group=space_group ) + subset = list(range(1, len(structure) + 1)) + config_space = ConfigurationSpace(objects=subset, symmetry_group=space_group) return config_space -def configuration_space_from_molecule( molecule, subset=None, atol=1e-5 ): + +def configuration_space_from_molecule( + molecule: Molecule, + subset: list[int] | None = None, + atol: float = 1e-5 +) -> ConfigurationSpace: """ Generate a ``ConfigurationSpace`` object from a `pymatgen` ``Molecule``. Args: - molecule (pymatgen ``Molecule``): molecule to be used to define the :any:`ConfigurationSpace`. - subset (Optional [list]): list of atom indices to be used for generating the configuration space. - atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. - + molecule: Molecule to be used to define the :any:`ConfigurationSpace`. + subset: List of atom indices to be used for generating the configuration space. + atol: Tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each + symmetry operation. + Returns: - a new :any:`ConfigurationSpace` instance. + A new :any:`ConfigurationSpace` instance. .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping - """ - molecule = Molecule( molecule.species, molecule.cart_coords - molecule.center_of_mass ) - point_group = point_group_from_molecule( molecule, subset=subset, atol=atol ) + molecule = Molecule(molecule.species, molecule.cart_coords - molecule.center_of_mass) + point_group = point_group_from_molecule(molecule, subset=subset, atol=atol) if subset is None: - subset = list( range( 1, len( molecule )+1 ) ) - config_space = ConfigurationSpace( objects=subset, symmetry_group=point_group ) + subset = list(range(1, len(molecule) + 1)) + config_space = ConfigurationSpace(objects=subset, symmetry_group=point_group) return config_space - -def unique_structure_substitutions( structure, to_substitute, site_distribution, verbose=False, atol=1e-5, show_progress=False ): + + +def unique_structure_substitutions( + structure: Structure | Molecule, + to_substitute: str, + site_distribution: dict[str, int], + verbose: bool = False, + atol: float = 1e-5, + show_progress: bool | str = False +) -> list[Any]: """ - Generate all symmetry-unique structures formed by substituting a set of sites in a `pymatgen` structure. + Generate all symmetry-unique structures formed by substituting a set of sites in a + `pymatgen` structure. Args: - structure (pymatgen.Structure): The parent structure. - to_substitute (str): atom label for the sites to be substituted. - site_distribution (dict): A dictionary that defines the number of each substituting element. - verbose (bool): verbose output. - atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. Default=1e-5. - show_progress (opt:default=False): Show a progress bar. - Setting to `True` gives a simple progress bar. - Setting to `"notebook"` gives a Jupyter notebook compatible progress bar. - + structure: The parent structure. + to_substitute: Atom label for the sites to be substituted. + site_distribution: A dictionary that defines the number of each substituting element. + verbose: Verbose output. + atol: Tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each + symmetry operation. Default=1e-5. + show_progress: Show a progress bar. Setting to `True` gives a simple progress bar. + Setting to `"notebook"` gives a Jupyter notebook compatible progress bar. Returns: - (list[Structure]): A list of Structure objects for each unique substitution. - + A list of Structure objects for each unique substitution. + Notes: The number of symmetry-equivalent configurations for each structure - is stored in the `number_of_equivalent_configurations` attribute. - + is stored in the `number_of_equivalent_configurations` attribute. + If the parent structure was previously generated using this function (as part of a sequence of substitutions) the full configuration degeneracy of each symmetry inequivalent configuration is stored in @@ -241,72 +318,182 @@ def unique_structure_substitutions( structure, to_substitute, site_distribution, .. _coordinate mapping: http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc - """ - site_substitution_index = list( structure.indices_from_symbol( to_substitute ) ) - if len( site_substitution_index ) != sum( site_distribution.values() ): - raise ValueError( "Number of sites from index does not match number from site distribution" ) - if isinstance( structure, Structure ): - config_space = configuration_space_from_structure( structure, subset=site_substitution_index, atol=atol ) - elif isinstance( structure, Molecule ): - structure = Molecule( structure.species, structure.cart_coords - structure.center_of_mass ) - config_space = configuration_space_from_molecule( structure, subset=site_substitution_index, atol=atol ) + site_substitution_index = list(structure.indices_from_symbol(to_substitute)) + if len(site_substitution_index) != sum(site_distribution.values()): + raise ValueError("Number of sites from index does not match number from site distribution") + + if isinstance(structure, Structure): + config_space = configuration_space_from_structure(structure, subset=site_substitution_index, atol=atol) + elif isinstance(structure, Molecule): + structure = Molecule(structure.species, structure.cart_coords - structure.center_of_mass) + config_space = configuration_space_from_molecule(structure, subset=site_substitution_index, atol=atol) else: - raise ValueError( "pymatgen Structure or Molecule object expected" ) - numeric_site_distribution, numeric_site_mapping = parse_site_distribution( site_distribution ) - unique_configurations = config_space.unique_configurations( numeric_site_distribution, verbose=verbose, show_progress=show_progress ) - new_structures = [ new_structure_from_substitution( structure, site_substitution_index, [ numeric_site_mapping[k] for k in c.tolist() ] ) for c in unique_configurations ] - if hasattr( structure, 'number_of_equivalent_configurations' ): - for s, c in zip( new_structures, unique_configurations ): - s.number_of_equivalent_configurations = c.count - s.full_configuration_degeneracy = c.count * structure.full_configuration_degeneracy + raise ValueError("pymatgen Structure or Molecule object expected") + + numeric_site_distribution, numeric_site_mapping = parse_site_distribution(site_distribution) + unique_configurations = config_space.unique_configurations( + numeric_site_distribution, verbose=verbose, show_progress=show_progress + ) + new_structures = [ + new_structure_from_substitution( + structure, site_substitution_index, [numeric_site_mapping[k] for k in c.tolist()] + ) + for c in unique_configurations + ] + + if hasattr(structure, 'number_of_equivalent_configurations'): + for s, c in zip(new_structures, unique_configurations): + s.number_of_equivalent_configurations = c.count + s.full_configuration_degeneracy = c.count * structure.full_configuration_degeneracy # type: ignore[union-attr] else: - for s, c in zip( new_structures, unique_configurations ): + for s, c in zip(new_structures, unique_configurations): s.number_of_equivalent_configurations = c.count s.full_configuration_degeneracy = c.count + return new_structures -def parse_site_distribution( site_distribution ): + +def parse_site_distribution( + site_distribution: dict[str, int] +) -> tuple[dict[int, int], dict[int, str]]: """ Converts a site distribution using species labels into one using integer labels. Args: - site_distribution (dict): e.g. `{ 'Mg': 1, 'Li': 3 }` + site_distribution: e.g. `{'Mg': 1, 'Li': 3}` Returns: - numeric_site_distribution ( dict): e.g. `{ 1:1, 0:3 }` - numeric_site_mapping (dict): e.g. `{ 0:'Mg', 1:'Li' }` + Tuple of (numeric_site_distribution, numeric_site_mapping) + e.g. ({0: 3, 1: 1}, {0: 'Li', 1: 'Mg'}) """ - numeric_site_distribution = {} - numeric_site_mapping = {} - for i,k in enumerate( site_distribution.keys() ): + numeric_site_distribution: dict[int, int] = {} + numeric_site_mapping: dict[int, str] = {} + for i, k in enumerate(site_distribution.keys()): numeric_site_distribution[i] = site_distribution[k] numeric_site_mapping[i] = k return numeric_site_distribution, numeric_site_mapping - -def new_structure_from_substitution( parent_structure, site_substitution_index, new_species_list ): + + +def new_structure_from_substitution( + parent_structure: Structure | Molecule, + site_substitution_index: list[int], + new_species_list: list[str] +) -> Any: """ Generate a new pymatgen ``Structure`` from site substitution parameters. Args: - parent_structure (Structure): The parent pymatgen ``Struture`` object. - site_substitution_index (list[int]): The list of site indices to be substituted. - new_species_list (list[str]): A list of the replacement atomic species. - + parent_structure: The parent pymatgen ``Structure`` or ``Molecule`` object. + site_substitution_index: The list of site indices to be substituted. + new_species_list: A list of the replacement atomic species. + Returns: - (``Structure``): The new pymatgen ``Structure``. + The new pymatgen ``Structure`` or ``Molecule``. Notes: pymatgen ``Structure`` and ``Molecule`` classes both subclass ``SiteCollection``. This function will also accept a parent ``Molecule`` object, and return a new ``Molecule``. """ - if len( site_substitution_index ) != len( new_species_list ): - raise ValueError - if any( i >= len( parent_structure ) for i in site_substitution_index ): - raise ValueError + if len(site_substitution_index) != len(new_species_list): + raise ValueError("site_substitution_index and new_species_list must have same length") + if any(i >= len(parent_structure) for i in site_substitution_index): + raise ValueError("site_substitution_index contains invalid site indices") + s = parent_structure.copy() - for i, spec in zip( site_substitution_index, new_species_list ): + for i, spec in zip(site_substitution_index, new_species_list): s[i] = spec return s + +def unique_structure_substitutions_by_composition( + structure: Structure | Molecule, + to_substitute: str, + species_list: list[str], + bounds: dict[str, tuple[int | None, int | None]] | None = None, + verbose: bool = False, + atol: float = 1e-5, + show_progress: bool | str = False +) -> dict[tuple[int, ...], list[Any]]: + """ + Generate all symmetry-unique structures for all compositions of substituting species. + + Args: + structure: The parent structure. + to_substitute: Atom label for the sites to be substituted. + species_list: List of species to substitute, e.g., ['Li', 'Na', 'Mg']. + Order determines composition tuple indices. + bounds: Occupancy bounds for each species. Keys are species names, values are + (min, max) tuples. e.g., {'Li': (1, 3), 'Na': (0, 2)} + verbose: Verbose output. + atol: Tolerance factor for coordinate mapping. Default=1e-5. + show_progress: Show progress bars. Can be True, False, or "notebook". + + Returns: + Mapping from composition tuples to lists of Structure objects. + Keys are tuples like (2, 1, 1) corresponding to species_list order. + Each Structure has a `number_of_equivalent_configurations` attribute. + + Example: + >>> results = unique_structure_substitutions_by_composition( + ... structure, 'X', ['Li', 'Na']) + >>> # results[(2, 2)] gives structures with 2 Li and 2 Na + """ + # Get sites to substitute + site_substitution_index = list(structure.indices_from_symbol(to_substitute)) + n_sites = len(site_substitution_index) + + # Create configuration space + if isinstance(structure, Structure): + config_space = configuration_space_from_structure(structure, subset=site_substitution_index, atol=atol) + elif isinstance(structure, Molecule): + structure = Molecule(structure.species, structure.cart_coords - structure.center_of_mass) + config_space = configuration_space_from_molecule(structure, subset=site_substitution_index, atol=atol) + else: + raise ValueError("pymatgen Structure or Molecule object expected") + + # Create species name to index mapping + species_to_index = {species: i for i, species in enumerate(species_list)} + + # Convert bounds from species names to indices if provided + bounds_numeric: dict[int, tuple[int | None, int | None]] | None = None + if bounds is not None: + bounds_numeric = {} + for species, (min_val, max_val) in bounds.items(): + if species not in species_to_index: + raise ValueError(f"Species '{species}' in bounds not found in species_list") + index = species_to_index[species] + bounds_numeric[index] = (min_val, max_val) + + # Get unique configurations by composition + configs_by_composition = config_space.unique_configurations_by_composition( + n_species=len(species_list), + bounds=bounds_numeric, + verbose=verbose, + show_progress=show_progress + ) + + # Convert configurations to structures + results: dict[tuple[int, ...], list[Any]] = {} + for composition_tuple, configurations in configs_by_composition.items(): + structures: list[Any] = [] + for config in configurations: + # Map configuration indices to species names + species_for_sites = [species_list[species_idx] for species_idx in config.tolist()] + + # Create new structure + new_structure = new_structure_from_substitution( + structure, + site_substitution_index, + species_for_sites + ) + + # Add metadata + new_structure.number_of_equivalent_configurations = config.count + + structures.append(new_structure) + + results[composition_tuple] = structures + + return results \ No newline at end of file diff --git a/bsym/partitions.py b/bsym/partitions.py new file mode 100644 index 0000000..2356ebc --- /dev/null +++ b/bsym/partitions.py @@ -0,0 +1,99 @@ +from typing import TypeVar + +K = TypeVar('K', int, str) + +def generate_partitions( + n: int, + k: int, + max_value: int | None = None +) -> list[tuple[int, ...]]: + """ + Generate all partitions of n into at most k parts. + + Args: + n: Number to partition + k: Maximum number of parts + max_value: Maximum value for any part (for recursion) + """ + if max_value is None: + max_value = n # First call: no restriction + + # Base cases + if k == 0: + return [()] if n == 0 else [] # can only partition 0 into 0 parts + if n == 0: + return [(0,) * k] # pad with zeros + + result = [] + + # Try each possible first part from max_value down to 0 + for first in range(min(n, max_value), -1, -1): + # Recursively partition remainder + for rest in generate_partitions(n - first, # remainder to partition + k - 1, # remaining number of parts, + first): # ensures parts in rest are ≤ first + result.append((first,) + rest) + + return result + +def compute_mapping_vector( + canonical_partition: tuple[int, ...], + permuted_partition: tuple[int, ...] +) -> list[int]: + """ + Compute 0-indexed mapping vector from canonical to permuted partition. + + Args: + canonical_partition: The canonical (first) permutation of a partition. + permuted_partition: A permutation of the canonical partition. + + Returns: + A 0-indexed list where mapping[i] indicates which species position + in the permuted partition corresponds to position i in canonical. + + Example: + >>> compute_mapping_vector((2, 1, 1), (1, 2, 1)) + [1, 0, 2] # Swap species 0↔1, keep species 2 + """ + mapping = [] + permuted_list = list(permuted_partition) + used = [False] * len(permuted_list) + + for canonical_val in canonical_partition: + # Find matching value in permuted that hasn't been used + for i, permuted_val in enumerate(permuted_list): + if permuted_val == canonical_val and not used[i]: + mapping.append(i) # Changed from i + 1 + used[i] = True + break + + return mapping + +def satisfies_bounds( + composition: dict[K, int], + occupancy_bounds: dict[K, tuple[int | None, int | None]] +) -> bool: + """ + Check if composition satisfies occupancy bounds. + + Args: + composition: e.g., {'A': 2, 'B': 1, 'C': 1} + occupancy_bounds: e.g., {'A': (1, 3), 'B': (0, 2)} + + Notes: + Species not in occupancy_bounds default to (0, n_sites). + Species in occupancy_bounds but not in composition are treated as count=0. + """ + n_sites = sum(composition.values()) + + # Check all species mentioned in either composition or bounds + all_species = set(composition.keys()) | set(occupancy_bounds.keys()) + + for species in all_species: + count = composition.get(species, 0) # 0 if absent from composition + lower, upper = occupancy_bounds.get(species, (0, n_sites)) + lower = lower if lower is not None else 0 + upper = upper if upper is not None else n_sites + if count < lower or count > upper: + return False + return True \ No newline at end of file diff --git a/bsym/permutations.py b/bsym/permutations.py index 763da49..53b88cf 100644 --- a/bsym/permutations.py +++ b/bsym/permutations.py @@ -2,11 +2,20 @@ from math import factorial from functools import reduce from operator import mul +from typing import Sequence, Generator, TypeVar, Protocol, Any -def flatten_list( this_list ): - return [ item for sublist in this_list for item in sublist ] +class SupportsRichComparison(Protocol): + def __lt__(self, other: Any) -> bool: ... + def __le__(self, other: Any) -> bool: ... + def __gt__(self, other: Any) -> bool: ... + def __ge__(self, other: Any) -> bool: ... -def number_of_unique_permutations( seq ): +T = TypeVar('T', bound=SupportsRichComparison) + +def flatten_list(this_list: list[list]) -> list: + return [item for sublist in this_list for item in sublist] + +def number_of_unique_permutations(seq: list) -> int: """Calculate the number of unique permutations of a sequence seq. Args: @@ -16,11 +25,11 @@ def number_of_unique_permutations( seq ): int: The number of unique permutations of seq """ - times_included = list( Counter( seq ).values() ) - factorials = list( map( factorial, times_included ) ) - return int( factorial( len( seq ) ) / reduce( mul, factorials ) ) + times_included = list(Counter(seq).values()) + factorials = list(map(factorial, times_included)) + return int(factorial(len(seq)) / reduce(mul, factorials)) -def unique_permutations( seq ): +def unique_permutations(seq: Sequence[T]) -> Generator[tuple[T, ...], None, None]: """ Yield only unique permutations of seq in an efficient way. @@ -37,7 +46,7 @@ def unique_permutations( seq ): seq = sorted(seq) while True: #yield list( seq ) - yield list( seq ) + yield tuple(seq) # Working backwards from the last-but-one index, k # we find the index of the first decrease in value. 0 0 1 0 1 1 1 0 for k in k_indices: diff --git a/bsym/point_group.py b/bsym/point_group.py index c60a917..8928397 100644 --- a/bsym/point_group.py +++ b/bsym/point_group.py @@ -1,5 +1,5 @@ from bsym import SymmetryGroup -class PointGroup( SymmetryGroup ): +class PointGroup(SymmetryGroup): class_str = 'PointGroup' diff --git a/bsym/space_group.py b/bsym/space_group.py index 35faabe..0029681 100644 --- a/bsym/space_group.py +++ b/bsym/space_group.py @@ -1,5 +1,5 @@ from bsym import SymmetryGroup -class SpaceGroup( SymmetryGroup ): +class SpaceGroup(SymmetryGroup): class_str = 'SymmetryGroup' diff --git a/bsym/symmetry_group.py b/bsym/symmetry_group.py index cf97d92..9fcb2cf 100644 --- a/bsym/symmetry_group.py +++ b/bsym/symmetry_group.py @@ -1,8 +1,10 @@ +from __future__ import annotations + import numpy as np from bsym import SymmetryOperation from itertools import product -from typing import TYPE_CHECKING from bsym.configuration import Configuration +from numpy.typing import NDArray class SymmetryGroup: """ @@ -27,17 +29,79 @@ class SymmetryGroup: class_str = 'SymmetryGroup' - def __init__( self, symmetry_operations=[] ): + def __init__(self, symmetry_operations=[]): """ Create a :any:`SymmetryGroup` object. - + Args: symmetry_operations (list): A list of :any:`SymmetryOperation` objects. - + Returns: None """ self.symmetry_operations = symmetry_operations + # Cache for batched operations + self._stacked_mappings = None + self._unique_mappings = None + + @property + def stacked_index_mappings(self) -> NDArray[np.int_]: + """ + Stack all index mappings from symmetry operations. + + Returns: + np.ndarray: Array of shape (n_operations, n_sites) containing all index mappings. + """ + if self._stacked_mappings is None: + self._stacked_mappings = np.array([ + op.index_mapping for op in self.symmetry_operations + ]) + return self._stacked_mappings + + @property + def unique_index_mappings(self) -> NDArray[np.int_]: + """ + Get unique index mappings, removing duplicates. + + Returns: + np.ndarray: Array of shape (n_unique, n_sites) containing only unique index mappings. + """ + if self._unique_mappings is None: + self._unique_mappings = np.unique( + self.stacked_index_mappings, + axis=0 + ) + return self._unique_mappings + + def operate_on(self, + configuration: Configuration, + minimal_set: bool=False) -> list[Configuration]: + """ + Returns a list of Configurations generated by applying every symmetry operation in this symmetry group. + + Args: + configuration: (Configuration): The configuration / occupation vector to operate on. + minimal_set: (:obj:`bool`, optional): Specifies whether to return the minimal set of resulting Configurations. Default is False. + + Returns: + list(Configuration) + + """ + # Choose which mappings to use + mappings = self.unique_index_mappings if minimal_set else self.stacked_index_mappings + + # Apply all operations at once using batched indexing + transformed_vectors = configuration.vector[mappings] + + # Wrap in Configuration objects + all_configs = [Configuration(vec) for vec in transformed_vectors] + + # Remove duplicates if minimal_set requested + # (unique_index_mappings removes duplicate operations, but can still produce duplicate results) + if minimal_set: + all_configs = list(set(all_configs)) + + return all_configs @classmethod def read_from_file( cls, filename ): @@ -63,32 +127,40 @@ def read_from_file( cls, filename ): return( cls( symmetry_operations = symmetry_operations ) ) @classmethod - def read_from_file_with_labels( cls, filename ): + def read_from_file_with_labels(cls, filename: str) -> SymmetryGroup: """ Create a :any:`SymmetryGroup` object from a file, with labelled symmetry operations. - - The file format should be a series of numerical mappings representing each symmetry operation, prepended with a string that will be used as a label. - + + The file format should be a series of numerical mappings representing each + symmetry operation, prepended with a string that will be used as a label. + e.g. for a pair of equivalent sites:: - + # example input file to define the spacegroup for a pair of equivalent sites E 1 2 C2 2 1 - + Args: filename (str): Name of the file to be read in. - + Returns: - spacegroup (SymmetryGroup) + SymmetryGroup: The symmetry group read from the file. """ - data = np.genfromtxt( filename, dtype=str ) - labels = [ row[0] for row in data ] - vectors = [ [ float(s) for s in row[1:] ] for row in data ] - symmetry_operations = [ SymmetryOperation.from_vector( v ) for v in vectors ] - [ so.set_label( l ) for (l, so) in zip( labels, symmetry_operations ) ] - return( cls( symmetry_operations=symmetry_operations ) ) - - def save_symmetry_operation_vectors_to( self, filename ): + data = np.genfromtxt(filename, dtype=str) + labels = [row[0] for row in data] + vectors = [[int(s) for s in row[1:]] for row in data] + symmetry_operations = [SymmetryOperation.from_vector(v) for v in vectors] + + # Set labels on each symmetry operation + for label, so in zip(labels, symmetry_operations): + so.set_label(label) + + return cls(symmetry_operations=symmetry_operations) + + def save_symmetry_operation_vectors_to( + self, + filename: str + ) -> None: """ Save the set of vectors describing each symmetry operation in this :any:`SymmetryGroup` to a file. @@ -100,10 +172,13 @@ def save_symmetry_operation_vectors_to( self, filename ): """ operation_list = [] for symmetry_operation in self.symmetry_operations: - operation_list.append( symmetry_operation.as_vector() ) - np.savetxt( filename, np.array( operation_list ), fmt='%i' ) + operation_list.append(symmetry_operation.as_vector()) + np.savetxt(filename, np.array( operation_list ), fmt='%i') - def extend( self, symmetry_operations_list ): + def extend( + self, + symmetry_operations_list: list[SymmetryOperation] + ) -> SymmetryGroup: """ Extend the list of symmetry operations in this :any:`SymmetryGroup`. @@ -116,7 +191,10 @@ def extend( self, symmetry_operations_list ): self.symmetry_operations.extend( symmetry_operations_list ) return self - def append( self, symmetry_operation ): + def append( + self, + symmetry_operation: SymmetryOperation + ) -> SymmetryGroup: """ Append a :any:`SymmetryOperation` to this :any:`SymmetryGroup`. @@ -126,10 +204,13 @@ def append( self, symmetry_operation ): Returns: self (:any:`SymmetryGroup`) """ - self.symmetry_operations.append( symmetry_operation ) + self.symmetry_operations.append(symmetry_operation) return self - def by_label( self, label ): + def by_label( + self, + label: str + ) -> SymmetryOperation | None: """ Returns the :any:`SymmetryOperation` with a matching label. @@ -142,7 +223,9 @@ def by_label( self, label ): return next((so for so in self.symmetry_operations if so.label == label), None) @property - def labels( self ): + def labels( + self + ) -> list[SymmetryOperation]: """ A list of labels for each :any:`SymmetryOperation` in this spacegroup. @@ -152,40 +235,23 @@ def labels( self ): Returns: (list): A list of label strings. """ - return [ so.label for so in self.symmetry_operations ] + return [so.label for so in self.symmetry_operations] - def __repr__( self ): - to_return = '{}\n'.format( self.__class__.class_str ) + def __repr__(self) -> str: + to_return: str + to_return = '{}\n'.format(self.__class__.class_str) for so in self.symmetry_operations: - to_return += "{}\t{}\n".format( so.label, so.as_vector() ) + to_return += "{}\t{}\n".format(so.label, so.as_vector()) return to_return @property - def size( self ): - return len( self.symmetry_operations ) + def size(self) -> int: + return len(self.symmetry_operations) - def __mul__( self, other ): + def __mul__(self, other): """ Direct product. """ - return SymmetryGroup( [ s1 * s2 for s1, s2 in product( self.symmetry_operations, other.symmetry_operations ) ] ) + return SymmetryGroup([s1 * s2 for s1, s2 in product(self.symmetry_operations, other.symmetry_operations)]) - def operate_on(self, - configuration: Configuration, - minimal_set: bool=False) -> list[Configuration]: - """ - Returns a list of Configurations generated by applying every symmetry operation in this symmetry group. - - Args: - configuration: (Configuration): The configuration / occupation vector to operate on. - minimal_set: (:obj:`bool`, optional): Specifies whether to return the minimal set of resulting Configurations. Default is False. - - Returns: - list(Configuration) - - """ - all_configs = [s.operate_on(configuration) for s in self.symmetry_operations] - if minimal_set: - all_configs = list(set(all_configs)) - return all_configs diff --git a/bsym/symmetry_operation.py b/bsym/symmetry_operation.py index a9375bf..c822c84 100644 --- a/bsym/symmetry_operation.py +++ b/bsym/symmetry_operation.py @@ -1,52 +1,63 @@ +from __future__ import annotations + +from typing import overload import numpy as np from bsym.configuration import Configuration -def is_square( m ): + +def is_square(m: np.ndarray) -> bool: """ Test whether a numpy matrix is square. Args: - m (np.matrix): The matrix. + m: The matrix. Returns: - (bool): True | False. + True if matrix is square, False otherwise. """ - return m.shape[0] == m.shape[1] + return bool(m.shape[0] == m.shape[1]) + -def is_permutation_matrix( m ): +def is_permutation_matrix(m: np.ndarray) -> bool: """ Test whether a numpy array is a `permutation matrix`_. .. _permutation_matrix: https://en.wikipedia.org/wiki/Permutation_matrix - + Args: - m (np.matrix): The matrix. + m: The matrix. Returns: - (bool): True | False. + True if matrix is a permutation matrix, False otherwise. """ m = np.asanyarray(m) - return (m.ndim == 2 and m.shape[0] == m.shape[1] and - (m.sum(axis=0) == 1).all() and - (m.sum(axis=1) == 1).all() and - ((m == 1) | (m == 0)).all()) - + return bool( + m.ndim == 2 and m.shape[0] == m.shape[1] and + (m.sum(axis=0) == 1).all() and + (m.sum(axis=1) == 1).all() and + ((m == 1) | (m == 0)).all() + ) + + class SymmetryOperation: """ `SymmetryOperation` class. """ - def __init__( self, matrix, label=None ): + def __init__( + self, + matrix: np.ndarray | list[list[int]], + label: str | None = None + ) -> None: """ Initialise a `SymmetryOperation` object Args: - matrix (numpy.matrix|numpy.ndarray|list): square 2D vector as either a - `numpy.matrix`, `numpy.ndarray`, or `list`. - for this symmetry operation. - label (default=None) (str): optional string label for this `SymmetryOperation` object. + matrix: Square 2D vector as either a `numpy.ndarray` or `list`. + label: Optional string label for this `SymmetryOperation` object. + Raises: - TypeError: if matrix is not `numpy.matrix`, `numpy.ndarray`, or `list`. + TypeError: if matrix is not `numpy.ndarray` or `list`. ValueError: if matrix is not square. ValueError: if matrix is not a `permutation matrix`_. @@ -59,153 +70,171 @@ def __init__( self, matrix, label=None ): Returns: None """ - if isinstance( matrix, np.matrix ): - self.matrix = np.array( matrix ) - elif isinstance( matrix, np.ndarray ): - self.matrix = np.array( matrix ) - elif isinstance( matrix, list): - self.matrix = np.array( matrix ) + if isinstance(matrix, np.matrix): + self.matrix = np.array(matrix) + elif isinstance(matrix, np.ndarray): + self.matrix = np.array(matrix) + elif isinstance(matrix, list): + self.matrix = np.array(matrix) else: raise TypeError - if not is_square( self.matrix ): + if not is_square(self.matrix): raise ValueError('Not a square matrix') - if not is_permutation_matrix( self.matrix ): + if not is_permutation_matrix(self.matrix): raise ValueError('Not a permutation matrix') self.label = label - self.index_mapping = np.array( [ np.array(row).tolist().index(1) for row in matrix ] ) + self.index_mapping: np.ndarray = np.array([np.array(row).tolist().index(1) for row in matrix]) + + @overload + def __mul__(self, other: SymmetryOperation) -> SymmetryOperation: ... + + @overload + def __mul__(self, other: Configuration) -> Configuration: ... - def __mul__( self, other ): + def __mul__(self, other: SymmetryOperation | Configuration) -> SymmetryOperation | Configuration: """ Multiply this `SymmetryOperation` matrix with another `SymmetryOperation`. Args: - other (SymmetryOperation, Configuration): the other symmetry operation or configuration or matrix - for the matrix multiplication self * other. + other: The other symmetry operation or configuration + for the matrix multiplication self * other. Returns: - (SymmetryOperation): a new `SymmetryOperation` instance with the resultant matrix. - (Configuration): if `other` is a `Configuration`. + A new `SymmetryOperation` instance with the resultant matrix, + or a `Configuration` if `other` is a `Configuration`. """ - if isinstance( other, SymmetryOperation ): - return SymmetryOperation( self.matrix.dot( other.matrix ) ) - elif isinstance( other, Configuration ): - return self.operate_on( other ) + if isinstance(other, SymmetryOperation): + return SymmetryOperation(self.matrix.dot(other.matrix)) + elif isinstance(other, Configuration): + return self.operate_on(other) else: raise TypeError - def invert( self, label=None ): + def invert(self, label: str | None = None) -> SymmetryOperation: """ Invert this `SymmetryOperation` object. Args: - None - + label: Optional label for the inverted symmetry operation. + Returns: A new `SymmetryOperation` object corresponding to the inverse matrix operation. """ - return SymmetryOperation( np.linalg.inv( self.matrix ).astype( int ), label=label ) + return SymmetryOperation(np.linalg.inv(self.matrix).astype(int), label=label) @classmethod - def from_vector( cls, vector, count_from_zero=False, label=None ): + def from_vector( + cls, + vector: list[int], + count_from_zero: bool = False, + label: str | None = None + ) -> SymmetryOperation: """ Initialise a SymmetryOperation object from a vector of site mappings. Args: - vector (list): vector of integers defining a symmetry operation mapping. - count_from_zero (default = False) (bool): set to True if the site index counts from zero. - label (default=None) (str): optional string label for this `SymmetryOperation` object. - + vector: Vector of integers defining a symmetry operation mapping. + count_from_zero: Set to True if the site index counts from zero. + label: Optional string label for this `SymmetryOperation` object. + Returns: - a new SymmetryOperation object + A new SymmetryOperation object """ if not count_from_zero: - vector = [ x - 1 for x in vector ] - dim = len( vector ) - matrix = np.zeros( ( dim, dim ) ) - for index, element in enumerate( vector ): - matrix[ element, index ] = 1 - new_symmetry_operation = cls( matrix, label=label ) + vector = [x - 1 for x in vector] + dim = len(vector) + matrix = np.zeros((dim, dim)) + for index, element in enumerate(vector): + matrix[element, index] = 1 + new_symmetry_operation = cls(matrix, label=label) return new_symmetry_operation - def similarity_transform( self, s, label=None ): + def similarity_transform( + self, + s: SymmetryOperation, + label: str | None = None + ) -> SymmetryOperation: """ Generate the SymmetryOperation produced by a similarity transform S^{-1}.M.S Args: - s: the symmetry operation or matrix S. - label (:obj:`str`, optional): the label to assign to the new SymmetryOperation. Defaults to None. + s: The symmetry operation or matrix S. + label: The label to assign to the new SymmetryOperation. Returns: - the SymmetryOperation produced by the similarity transform + The SymmetryOperation produced by the similarity transform """ - s_new = s.invert() * ( self * s ) + s_new = s.invert() * (self * s) if label: - s_new.set_label( label ) + s_new.set_label(label) return s_new - def operate_on(self, configuration): + def operate_on(self, configuration: Configuration) -> Configuration: """ Return the Configuration generated by applying this symmetry operation Args: - configuration (Configuration): the configuration / occupation vector to operate on + configuration: The configuration / occupation vector to operate on Returns: - (Configuration): the new configuration obtained by operating on configuration with this symmetry operation. + The new configuration obtained by operating on configuration with this + symmetry operation. """ if not isinstance(configuration, Configuration): raise TypeError return Configuration(configuration.vector[self.index_mapping]) - def character( self ): + def character(self) -> int: """ Return the character of this symmetry operation (the trace of `self.matrix`). Args: - none + None Returns: - np.trace( self.matrix ) + The trace of self.matrix """ - return np.trace( self.matrix ) + return int(np.trace(self.matrix)) - def as_vector( self, count_from_zero=False ): + def as_vector(self, count_from_zero: bool = False) -> list[int]: """ Return a vector representation of this symmetry operation Args: - count_from_zero (default = False) (bool): set to True if the vector representation counts from zero - + count_from_zero: Set to True if the vector representation counts from zero + Returns: - a vector representation of this symmetry operation (as a list) + A vector representation of this symmetry operation (as a list) """ offset = 0 if count_from_zero else 1 - return [ row.tolist().index( 1 ) + offset for row in self.matrix.T ] + return [row.tolist().index(1) + offset for row in self.matrix.T] - def set_label( self, label ): + def set_label(self, label: str) -> SymmetryOperation: """ Set the label for this symmetry operation. - + Args: - label: label to set for this symmetry operation + label: Label to set for this symmetry operation + Returns: - self + self """ self.label = label return self - def pprint( self ): + def pprint(self) -> None: """ Pretty print for this symmetry operation Args: None + Returns: None """ label = self.label if self.label else '---' - print( label + ' : ' + ' '.join( [ str(e) for e in self.as_vector() ] ) ) - - def __repr__( self ): + print(label + ' : ' + ' '.join([str(e) for e in self.as_vector()])) + + def __repr__(self) -> str: label = self.label if self.label else '---' - return 'SymmetryOperation\nlabel(' + label + ")\n" + self.matrix.__repr__() + return 'SymmetryOperation\nlabel(' + label + ")\n" + self.matrix.__repr__() \ No newline at end of file diff --git a/bsym/version.py b/bsym/version.py index c68196d..8c0d5d5 100644 --- a/bsym/version.py +++ b/bsym/version.py @@ -1 +1 @@ -__version__ = "1.2.0" +__version__ = "2.0.0" diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index f0029c2..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -sphinx>=7.0.0 -sphinx_rtd_theme>=1.0.0 -nbsphinx>=0.8.9 -ipykernel>=6.0.0 -matplotlib>=3.4.0 -pypandoc>=1.5 -myst-parser -myst-nb -numpy -pymatgen -tqdm diff --git a/docs/source/conf.py b/docs/source/conf.py index 27eb428..046f563 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,12 +11,28 @@ # Add any Sphinx extension module names here, as strings extensions = [ 'sphinx.ext.autodoc', - 'myst_parser', + 'myst_nb', # Replaces myst_parser for notebooks 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', - 'nbsphinx' +] + +# MyST-NB settings +nb_execution_mode = "cache" # or "auto" to always re-execute +nb_execution_timeout = 300 # 5 minutes max per cell +nb_execution_allow_errors = False +nb_merge_streams = True + +# Kernel to use for execution +nb_kernel_rgx = "python3" + +# Configure MyST parser +myst_enable_extensions = [ + "colon_fence", # ::: can be used instead of ``` + "deflist", + "dollarmath", # $ and $$ for math + "amsmath", ] # nbsphinx settings @@ -30,7 +46,10 @@ # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] -source_suffix = ['.rst', '.md'] +source_suffix = { + '.rst': 'restructuredtext', + '.md': 'myst-nb', +} # The master toctree document. master_doc = 'index' diff --git a/docs/source/development.rst b/docs/source/development.rst deleted file mode 100644 index 81125c0..0000000 --- a/docs/source/development.rst +++ /dev/null @@ -1,23 +0,0 @@ -Development -=========== - -Tests ------ - -Automated testing of the latest commit happens -`here `__. - -Manual tests can be run using:: - - python -m unittest discover - -Contributing ------------- - -Contributions are welcome. Please feel free to submit a Pull Request. - -Issue Tracking --------------- - -Issues and feature requests are tracked on GitHub: -https://github.com/bjmorgan/bsym/issues diff --git a/docs/source/getting_started/index.rst b/docs/source/getting_started/index.rst new file mode 100644 index 0000000..59df4ea --- /dev/null +++ b/docs/source/getting_started/index.rst @@ -0,0 +1,11 @@ +Getting Started +=============== + +This section provides an introduction to ``bsym`` and guides you through installation and basic usage. + +.. toctree:: + :maxdepth: 2 + + introduction + installation + quickstart diff --git a/docs/source/getting_started/installation.md b/docs/source/getting_started/installation.md new file mode 100644 index 0000000..4924bae --- /dev/null +++ b/docs/source/getting_started/installation.md @@ -0,0 +1,36 @@ +# Installation + +## Requirements + +bsym requires Python 3.10 or later. + +## Standard Installation + +Install from PyPI: +```bash +pip install bsym +``` + +## Installation from Source + +Clone the repository and install: +```bash +git clone https://github.com/bjmorgan/bsym.git +cd bsym +pip install . +``` + +## Development Installation + +For development work (running tests, building documentation): +```bash +git clone https://github.com/bjmorgan/bsym.git +cd bsym +pip install -e ".[dev]" +``` + +## Verifying Installation +```python +import bsym +print(bsym.__version__) +``` diff --git a/docs/source/getting_started/introduction.md b/docs/source/getting_started/introduction.md new file mode 100644 index 0000000..e24046d --- /dev/null +++ b/docs/source/getting_started/introduction.md @@ -0,0 +1,114 @@ +# Introduction to bsym + +## What is bsym? + +bsym is a Python package for working with symmetry operations and enumerating symmetry-inequivalent configurations. It provides tools for: + +- Defining abstract configuration spaces and their symmetry operations +- Finding all unique arrangements of objects that respect symmetry constraints +- Generating symmetry-inequivalent crystal structures with substitutional disorder + +## What Problems Does bsym Solve? + +### The Configuration Counting Problem + +Consider a crystal with 16 anion sites where you want to substitute 8 oxygen atoms for 8 fluorine atoms. Without considering symmetry, there are $\binom{16}{8} = 12,870$ possible arrangements. However, if the crystal has symmetry operations (rotations, reflections, translations), many of these arrangements are equivalent - they're just the same structure viewed from different angles or shifted in space. + +bsym identifies which arrangements are truly unique, dramatically reducing the number of structures you need to consider. For example, a high-symmetry crystal might have only a few dozen unique arrangements instead of thousands. + +### Why This Matters + +**For computational materials science:** +- Generate training sets for machine learning with only symmetry-inequivalent structures +- Calculate configuration-dependent properties efficiently +- Build phase diagrams by exploring composition space systematically +- Study defects and disorder without redundant calculations + +**For mathematical and theoretical work:** +- Explore combinatorial problems with symmetry constraints +- Study group theory applications +- Develop enumeration algorithms + +**For general research:** +- Avoid wasting computational resources on equivalent configurations +- Ensure systematic coverage of configuration space +- Calculate degeneracies for statistical mechanics + +## Key Features + +### Abstract Configuration Spaces + +Work with symmetry at a mathematical level, independent of any physical system: +```python +from bsym import ConfigurationSpace, SymmetryGroup + +config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) +unique_configs = config_space.unique_configurations({1: 2, 0: 2}) +``` + +### Crystallographic Interface + +Integration with pymatgen for crystal structure generation: +```python +from bsym.interface.pymatgen import unique_structure_substitutions + +unique_structures = unique_structure_substitutions( + parent_structure, 'F', {'O': 8, 'F': 8} +) +``` + +### Efficient Algorithms + +- Smart enumeration that only performs symmetry analysis when needed +- Species exchange optimization for composition enumeration +- Progress tracking for large systems + +### Degeneracy Tracking + +Each unique configuration includes its degeneracy - the number of symmetry-equivalent arrangements it represents. This is essential for statistical mechanics calculations. + +## Who is bsym For? + +**Computational materials scientists** studying: +- Solid solutions and substitutional disorder +- Defect configurations +- Surface adsorption patterns +- Magnetic ordering + +**Researchers in related fields** working on: +- Combinatorial problems with symmetry +- Group theory applications +- Enumeration algorithms + +**Students** learning about: +- Crystallographic symmetry +- Group theory +- Configuration spaces + +## The Approach + +bsym separates the mathematical logic of symmetry from the physical details of specific systems: + +1. **Abstract representation**: Configurations are vectors of integers, symmetry operations are permutations +2. **Efficient computation**: Symmetry operations implemented as numpy array indexing, with hash-based configuration lookup +3. **Physical interpretation**: Map results back to structures, coordinates, etc. when needed + +This separation makes the algorithms system-agnostic and computationally efficient. + +## What's Next? + +- **[Installation](installation.md)**: Get bsym installed +- **[Quickstart](quickstart.md)**: Try hands-on examples +- **[User Guide](../user_guide/index.rst)**: Detailed tutorials for specific tasks +- **[Theory](../theory/index.rst)**: Understand the concepts and algorithms + +## Further Reading + +For the theoretical background and detailed algorithm descriptions: +- [Configuration Spaces](../theory/configuration_spaces.md) +- [Symmetry Operations](../theory/symmetry_operations.md) +- [Unique Configuration Enumeration](../theory/unique_configurations.md) + +For practical applications: +- [Basic Substitutions](../user_guide/basic_substitutions.ipynb) +- [Varying Composition](../user_guide/varying_composition.ipynb) diff --git a/docs/source/getting_started/quickstart.md b/docs/source/getting_started/quickstart.md new file mode 100644 index 0000000..23e494e --- /dev/null +++ b/docs/source/getting_started/quickstart.md @@ -0,0 +1,164 @@ +# Quickstart Guide + +This guide provides quick examples to get you started with bsym, covering both abstract configuration spaces and practical crystallographic applications. + +## Abstract Example: Symmetry-Inequivalent Arrangements + +This example shows how to find unique arrangements of objects in a symmetric space without requiring any crystallographic knowledge. + +### Problem: Four Sites in a Square + +Consider four sites arranged in a square. How many unique ways can we place 2 occupied and 2 vacant sites, accounting for the square's rotational and reflection symmetry? + +![Square configuration space](../theory/figures/square_configuration_space.pdf) + +### Solution +```python +from bsym import ConfigurationSpace, SymmetryGroup, SymmetryOperation + +# Define C4v symmetry operations (square symmetry) +e = SymmetryOperation.from_vector([1, 2, 3, 4], label='E') +c4 = SymmetryOperation.from_vector([2, 3, 4, 1], label='C4') +c4_inv = SymmetryOperation.from_vector([4, 1, 2, 3], label='C4i') +c2 = SymmetryOperation.from_vector([3, 4, 1, 2], label='C2') +sigma_x = SymmetryOperation.from_vector([4, 3, 2, 1], label='s_x') +sigma_y = SymmetryOperation.from_vector([2, 1, 4, 3], label='s_y') +sigma_ac = SymmetryOperation.from_vector([1, 4, 3, 2], label='s_ac') +sigma_bd = SymmetryOperation.from_vector([3, 2, 1, 4], label='s_bd') + +# Create symmetry group +c4v = SymmetryGroup([e, c4, c4_inv, c2, sigma_x, sigma_y, sigma_ac, sigma_bd]) + +# Create configuration space +config_space = ConfigurationSpace( + objects=['a', 'b', 'c', 'd'], + symmetry_group=c4v +) + +# Find unique configurations (2 occupied, 2 vacant) +unique_configs = config_space.unique_configurations({1: 2, 0: 2}) + +print(f"Found {len(unique_configs)} unique configurations") +for config in unique_configs: + print(f"{config.tolist()}: degeneracy = {config.count}") +``` + +**Output:** +``` +Found 2 unique configurations +[0, 0, 1, 1]: degeneracy = 4 +[0, 1, 0, 1]: degeneracy = 2 +``` + +Without symmetry, there would be 6 distinct arrangements. With C4v symmetry, these reduce to just 2 unique patterns: +- Adjacent sites occupied (4 equivalent arrangements) +- Diagonal sites occupied (2 equivalent arrangements) + +### Next Steps + +- Learn about [configuration spaces](../theory/configuration_spaces.md) +- Understand [symmetry operations](../theory/symmetry_operations.md) +- Read about the [enumeration algorithm](../theory/unique_configurations.md) + +## Crystallographic Example: Disordered Structures + +This example shows how to generate symmetry-inequivalent crystal structures with substitutional disorder. + +### Problem: O/F Disorder in a Fluorite Structure + +Generate all unique structures for a 2×2×1 CaF2 supercell where we substitute 8 oxygen atoms for 8 of the 16 fluorine atoms. + +### Solution +```python +from pymatgen.core import Structure +from bsym.interface.pymatgen import unique_structure_substitutions + +# Load parent structure (CaF2 2x2x1 supercell) +parent_structure = Structure.from_file('CaF2_supercell.cif') +# Or create programmatically using pymatgen + +# Generate all unique O/F arrangements +unique_structures = unique_structure_substitutions( + structure=parent_structure, + to_substitute='F', # Substitute on F sites + site_distribution={'O': 8, 'F': 8} # 8 O, 8 F +) + +print(f"Found {len(unique_structures)} symmetry-inequivalent structures") + +# Check degeneracies +for i, structure in enumerate(unique_structures[:3]): + n_equiv = structure.number_of_equivalent_configurations + print(f"Structure {i}: represents {n_equiv} equivalent configurations") + +# Export structures +for i, structure in enumerate(unique_structures): + structure.to(filename=f'CaF2_O8F8_{i}.cif', fmt='cif') +``` + +**Output:** +``` +Found 47 symmetry-inequivalent structures +Structure 0: represents 192 equivalent configurations +Structure 1: represents 192 equivalent configurations +Structure 2: represents 96 equivalent configurations +``` + +The `unique_structure_substitutions` function: +1. Automatically detects the space group symmetry +2. Identifies all symmetry-equivalent F sites +3. Enumerates only the symmetry-inequivalent O/F arrangements +4. Returns pymatgen `Structure` objects with degeneracy information + +### Exploring Multiple Compositions + +To generate structures across different O:F ratios: +```python +from bsym.interface.pymatgen import unique_structure_substitutions_by_composition + +# Generate structures for all O:F compositions +all_structures = unique_structure_substitutions_by_composition( + structure=parent_structure, + to_substitute='F', + species=['O', 'F'] +) + +# Results organized by composition +for composition, structures in all_structures.items(): + n_O, n_F = composition + print(f"CaO{n_O}F{n_F}: {len(structures)} unique structures") +``` + +### Next Steps + +- See [Basic Substitutions](../user_guide/basic_substitutions.ipynb) for more examples +- Learn about [varying composition](../user_guide/varying_composition.ipynb) +- Understand [degeneracy tracking](../user_guide/fixed_composition.ipynb) + +## Key Concepts + +### Configuration Space +An abstract vector space where you arrange different types of objects across discrete positions. + +### Symmetry Operations +Transformations that map the configuration space onto itself (rotations, reflections, etc.). + +### Symmetry-Inequivalent Configurations +The minimal set of configurations where no two can be transformed into each other by symmetry operations. + +### Degeneracy +The number of symmetry-equivalent configurations represented by each unique configuration. + +## Where to Go Next + +**For abstract/mathematical applications:** +- [Theory: Configuration Spaces](../theory/configuration_spaces.md) +- [Theory: Symmetry Operations](../theory/symmetry_operations.md) + +**For crystallographic applications:** +- [User Guide: Basic Substitutions](../user_guide/basic_substitutions.ipynb) +- [User Guide: Varying Composition](../user_guide/varying_composition.ipynb) + +**For understanding the algorithms:** +- [Theory: Unique Configuration Enumeration](../theory/unique_configurations.md) +- [Theory: Composition Enumeration](../theory/composition_enumeration.md) diff --git a/docs/source/index.rst b/docs/source/index.rst index 1230724..a291daf 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,11 +9,10 @@ bsym - A basic symmetry module :maxdepth: 2 :caption: Contents: - introduction - installation - examples/bsym_examples + getting_started/index + user_guide/index + theory/index api/modules - development Indices and tables ================== @@ -24,8 +23,8 @@ Indices and tables .. |DOI| image:: https://zenodo.org/badge/19279643.svg :target: https://zenodo.org/badge/latestdoi/19279643 -.. |Build Status| image:: https://travis-ci.org/bjmorgan/bsym.svg?branch=master - :target: https://travis-ci.org/bjmorgan/bsym +.. |Build Status| image:: https://github.com/bjmorgan/bsym/actions/workflows/build.yml/badge.svg + :target: https://github.com/bjmorgan/bsym/actions/workflows/build.yml .. |Test Coverage| image:: https://codeclimate.com/github/bjmorgan/bsym/badges/coverage.svg :target: https://codeclimate.com/github/bjmorgan/bsym/coverage .. |Documentation Status| image:: https://readthedocs.org/projects/bsym/badge/?version=latest diff --git a/docs/source/installation.rst b/docs/source/installation.rst deleted file mode 100644 index 0ede1e6..0000000 --- a/docs/source/installation.rst +++ /dev/null @@ -1,26 +0,0 @@ -Installation -============ - -The simplest way to install bsym is using pip:: - - pip install bsym - -Alternatively, you can clone the repository and install from source:: - - git clone https://github.com/bjmorgan/bsym.git - cd bsym - pip install . - -Requirements ------------- - -bsym requires: - -- numpy -- pymatgen -- tqdm - -Python Compatibility --------------------- - -bsym has been tested with Python versions 3.9 and above. diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst deleted file mode 100644 index 2374139..0000000 --- a/docs/source/introduction.rst +++ /dev/null @@ -1,13 +0,0 @@ -Introduction -============ - -``bsym`` is a basic Python symmetry module. It consists of core -classes that describe configuration vector spaces, their symmetry -operations, and specific configurations of objects within these spaces. - -The module also contains an interface for working with -`pymatgen `__ ``Structure`` objects, to allow -simple generation of disordered symmetry-inequivalent structures from a -symmetric parent crystal structure. - -Source code is available as a git repository at https://github.com/bjmorgan/bsym. diff --git a/docs/source/theory/composition_enumeration.md b/docs/source/theory/composition_enumeration.md new file mode 100644 index 0000000..47aa05d --- /dev/null +++ b/docs/source/theory/composition_enumeration.md @@ -0,0 +1,235 @@ +# Composition Enumeration + +## The Composition Enumeration Problem + +### Single vs Multiple Compositions + +The `unique_configurations()` method finds all symmetry-inequivalent configurations for a **fixed composition** - a specific distribution of object types. For example, `{0: 2, 1: 2}` specifies exactly 2 objects of type 0 and 2 of type 1. + +However, many problems require exploring **multiple compositions**: + +- Building phase diagrams (e.g., LixNa1-xCoO2 across all x) +- Finding energetically favorable compositions +- Surveying disorder across different stoichiometries + +### The Challenge + +For an n-site system with k species, there are many possible compositions. A naive approach would call `unique_configurations()` separately for each composition, but this is inefficient because many compositions are related by symmetry. + +### Example: Binary System + +Consider a 4-site system with 2 species (labels 0 and 1): + +Possible compositions: +- `(4, 0)` - all sites are species 0 +- `(3, 1)` - 3 sites species 0, 1 site species 1 +- `(2, 2)` - 2 sites of each species +- `(1, 3)` - 1 site species 0, 3 sites species 1 +- `(0, 4)` - all sites are species 1 + +If we are studying Li/Na substitution on 4 sites, `(3, 1)` might represent Li3Na, while `(1, 3)` represents LiNa3. These are different chemically, but from a **configuration enumeration perspective** they are equivalent — the same counting problem with labels swapped. + +## Species Exchange Symmetry + +### What is Species Exchange Symmetry? + +Two compositions are related by **species exchange symmetry** if one can be obtained from the other by permuting the species labels. + +For a composition represented as a tuple `(n₀, n₁, n₂, ...)` where ni is the count of species i: + +- `(3, 1, 0)` and `(1, 3, 0)` are related by swapping species 0 ↔ species 1 +- `(2, 1, 1)` and `(1, 2, 1)` are related by swapping species 0 ↔ species 1 +- `(2, 1, 1)`, `(1, 2, 1)`, `(1, 1, 2)` are all related by various species permutations + +### Why This Matters + +Compositions related by species exchange produce **identical sets** of symmetry-inequivalent configurations (up to relabeling). + +If we find the unique configurations for composition `(3, 1)`, we can generate the configurations for `(1, 3)` simply by swapping the species labels in each configuration. No expensive symmetry analysis is needed for the second composition. + +## Partitions and Canonical Compositions + +### Integer Partitions + +An **integer partition** of n into k parts is a way of writing n as a sum of k non-negative integers, in non-increasing order. + +For n=4 sites and k=2 species: +- `(4, 0)` - partition: 4 + 0 = 4 +- `(3, 1)` - partition: 3 + 1 = 4 +- `(2, 2)` - partition: 2 + 2 = 4 + +Note: `(3, 1)` and `(1, 3)` represent the **same partition**. When written in non-increasing order, both become `(3, 1)`. + +### Partitions Group Compositions + +Each partition corresponds to all compositions related by species exchange: + +**Partition (3, 1)** corresponds to: +- Composition `(3, 1)` - 3 of species 0, 1 of species 1 +- Composition `(1, 3)` - 1 of species 0, 3 of species 1 + +**Partition (2, 2)** corresponds to: +- Composition `(2, 2)` - 2 of each species (only one arrangement - symmetric) + +**Partition (4, 0)** corresponds to: +- Composition `(4, 0)` - all sites species 0 +- Composition `(0, 4)` - all sites species 1 + +### Canonical Composition + +The **canonical composition** for a partition is the partition tuple itself. + +- For partition `(3, 1)`: canonical composition is `(3, 1)` +- For partition `(2, 2)`: canonical composition is `(2, 2)` + +All other permutations of the partition are **non-canonical compositions**. + +### The Optimization Strategy + +Instead of performing symmetry analysis on every composition: + +1. Generate all partitions of n sites into k species +2. For each partition, perform symmetry analysis **only on the canonical composition** +3. For non-canonical compositions, generate configurations by **relabeling species** in the canonical results + +This reduces computational cost from O(compositions) to O(partitions). + +### Example: 4 Sites, 2 Species + +All compositions: `(4,0)`, `(3,1)`, `(2,2)`, `(1,3)`, `(0,4)` - **5 total** + +All partitions: `(4,0)`, `(3,1)`, `(2,2)` - **3 total** + +**Without optimization** (naive approach): +- 5 separate symmetry analyses (one per composition) + +**With optimization**: +- Analyze partition `(4,0)` → generate composition `(0,4)` by relabeling +- Analyze partition `(3,1)` → generate composition `(1,3)` by relabeling +- Analyze partition `(2,2)` (symmetric - no additional compositions) +- **3 symmetry analyses** + +**Reduction: 40%** (5 analyses → 3 analyses) + +## Species Mapping and Relabeling + +### The Mapping Vector + +To generate configurations for a non-canonical composition from canonical results, we compute a **species mapping vector** that describes how to relabel species. + +For partition `(3, 1)` with compositions `(3, 1)` → `(1, 3)`: +- Species 0 in canonical (count 3) appears at position 1 in `(1, 3)` → mapping[0] = 1 +- Species 1 in canonical (count 1) appears at position 0 in `(1, 3)` → mapping[1] = 0 + +Mapping vector: `[1, 0]` (swap species 0↔1) + +### Applying the Mapping + +Apply the mapping vector to each configuration by relabeling species: + +**Canonical configuration for (3, 1)**: +``` +[0, 0, 0, 1] # 3 sites are species 0, 1 site is species 1 +``` + +**After applying mapping [1, 0]**: +``` +[1, 1, 1, 0] # 3 sites are species 1, 1 site is species 0 +``` + +This produces a valid configuration for composition `(1, 3)`. + +The degeneracy (count) is preserved during relabeling - if the canonical configuration represents 4 equivalent arrangements, the relabeled configuration also represents 4 equivalent arrangements. + +## The Algorithm + +### Overview + +The composition enumeration algorithm: + +1. **Generate partitions**: Find all partitions of n sites into k species +2. **Generate compositions**: For each partition, create all permutations to get all compositions +3. **Filter by bounds** (if specified): Keep only compositions satisfying occupancy constraints +4. **Analyze canonical compositions**: For each partition, perform symmetry analysis on the canonical composition only +5. **Generate non-canonical results**: For other compositions from the same partition, relabel the canonical configurations using species mapping vectors + +### Step-by-Step Process + +**Given**: n-site configuration space, k species, optional occupancy bounds + +**Step 1: Generate Partitions** + +Generate all integer partitions of n into k parts: +```python +# For n=4, k=2: +partitions = [(4, 0), (3, 1), (2, 2)] +``` + +**Step 2: Generate Compositions from Each Partition** + +For each partition, generate all distinct permutations: +```python +# For partition (3, 1): +permutations = [(3, 1), (1, 3)] +``` + +**Step 3: Filter by Bounds (Optional)** + +If occupancy bounds are specified, check each composition: +```python +# bounds = {0: (1, 3), 1: (1, 3)} +# Composition (4, 0) violates bounds (species 1 has 0, needs at least 1) +# Composition (3, 1) satisfies bounds ✓ +``` + +**Step 4: Analyze Canonical Composition** + +For each partition, perform expensive symmetry analysis on the canonical composition: +```python +# For partition (3, 1): +canonical = (3, 1) +site_distribution = {0: 3, 1: 1} +canonical_configs = config_space.unique_configurations(site_distribution) +``` + +**Step 5: Generate Non-Canonical Configurations** + +For each non-canonical composition from the same partition: +```python +# For composition (1, 3) from partition (3, 1): +mapping = compute_mapping_vector((3, 1), (1, 3)) # Returns [1, 0] +relabeled_configs = [apply_species_mapping(config, mapping) + for config in canonical_configs] +results[(1, 3)] = relabeled_configs +``` + +### Implementation in bsym + +The algorithm is implemented in `ConfigurationSpace.unique_configurations_by_composition()`: +```python +from bsym import ConfigurationSpace + +config_space = ConfigurationSpace( + objects=[1, 2, 3, 4], + symmetry_group=my_symmetry_group +) + +# Enumerate all compositions for 2 species +results = config_space.unique_configurations_by_composition(n_species=2) + +# Results is a dictionary: {composition_tuple: [Configuration, ...]} +# e.g., {(4, 0): [...], (3, 1): [...], (2, 2): [...], (1, 3): [...], (0, 4): [...]} +``` + +With optional bounds: +```python +# Only compositions with 1-3 of each species +bounds = {0: (1, 3), 1: (1, 3)} +results = config_space.unique_configurations_by_composition( + n_species=2, + bounds=bounds +) + +# Results: {(3, 1): [...], (2, 2): [...], (1, 3): [...]} +# (4, 0) and (0, 4) excluded by bounds +``` diff --git a/docs/source/theory/configuration_spaces.md b/docs/source/theory/configuration_spaces.md new file mode 100644 index 0000000..771c22e --- /dev/null +++ b/docs/source/theory/configuration_spaces.md @@ -0,0 +1,181 @@ +# Configuration Spaces + +## What is a Configuration Space? + +A **configuration space** is an abstract vector space that represents possible arrangements of distinguishable objects. A configuration space consists of: + +1. **A vector space** - n positions in a vector (indexed 0, 1, 2, ..., n-1) +2. **Object labels** - integers representing different types or species + +This abstract framework can represent many different systems: atoms distributed across crystallographic sites, molecular conformations, states at discrete time steps, or any other system where discrete objects can be arranged in different ways. + +### A Simple Example + +Consider a 3-dimensional vector space. We can represent arrangements of two types of objects (labelled 0 and 1) as vectors like `[1, 1, 0]`. + +If we interpret this vector as three sites in a triangle: + +![Triangular configuration space](./figures/triangular_configuration_space.pdf) + +then `[1, 1, 0]` represents: + +![Triangular configuration example](./figures/triangular_configuration_example_1.pdf) + +where positions 0 and 1 have object type 1 (shown in black), and position 2 has object type 0 (shown in white). + +Different arrangements like `[0, 1, 1]` or `[1, 0, 1]` represent different **configurations** within the same **configuration space**. + +### Configurations vs Configuration Spaces + +- **Configuration Space**: The vector space itself (e.g., "3-dimensional space") +- **Configuration**: A specific assignment of labels (e.g., `[1, 1, 0]`) + +## Mathematical Representation + +### Configuration Vectors + +Each configuration is represented as a vector of integers. For a configuration space with n positions, a configuration is an n-element vector: + +$$\mathbf{v} = \begin{pmatrix}v_0\\v_1\\v_2\\\vdots\\v_{n-1}\end{pmatrix}$$ + +where each $v_i$ is a non-negative integer representing the type of object at position $i$. + +### Object Labels + +Object labels are arbitrary integers. Objects with the same label are considered indistinguishable. For example: + +- Binary system: labels `0` and `1` (e.g., vacant/occupied, or species A/species B) +- Ternary system: labels `0`, `1`, and `2` (e.g., three different atomic species) +- Multi-species: any number of distinct integer labels + +The specific integers used as labels are arbitrary - what matters is which positions have the same or different labels. + +### Examples + +For a 4-dimensional configuration space: + +- `[0, 0, 1, 1]` - positions 0 and 1 have type 0, positions 2 and 3 have type 1 +- `[0, 1, 0, 1]` - positions 0 and 2 have type 0, positions 1 and 3 have type 1 +- `[2, 2, 1, 0]` - position 0 has type 2, position 1 has type 2, position 2 has type 1, position 3 has type 0 +- `[1, 1, 1, 1]` - all positions have type 1 + +Each vector represents a distinct configuration. Whether two configurations like `[0, 0, 1, 1]` and `[0, 1, 0, 1]` are equivalent depends on the symmetry operations defined for the configuration space (discussed in the next section). + +## The Configuration and ConfigurationSpace Classes + +### The Configuration Class + +In bsym, individual configurations are represented by `Configuration` objects. A `Configuration` stores: + +- **A vector**: The integer array representing the configuration +- **Metadata**: Optional attributes like degeneracy counts + +Creating a configuration: +```python +from bsym import Configuration + +config = Configuration([1, 1, 0, 0]) +``` + +### Numeric Representation + +Each configuration has a numeric representation accessed via the `as_number` property. This provides a unique integer identifier for the configuration: +```python +config = Configuration([1, 2, 0]) +print(config.as_number) # Output: 120 +``` + +This numeric representation is primarily used internally for efficient comparison and hashing of configurations during symmetry analysis. + +### The ConfigurationSpace Class + +A `ConfigurationSpace` object combines: + +- **Objects**: A list defining the dimensionality of the space +- **Symmetry group**: Optional symmetry operations (defaults to identity only) + +The objects list defines the vector space dimension: +```python +from bsym import ConfigurationSpace + +# Create a 4-dimensional configuration space +config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) +``` + +The integers in the objects list serve as labels for the vector positions - they don't represent the configuration itself. They're often just sequential integers `[1, 2, 3, ..., n]`, but can be any distinct values. + +### Configuration Space Without Symmetry + +A `ConfigurationSpace` can be created without specifying symmetry operations. In this case, it contains only the identity operation, meaning no configurations are considered equivalent: +```python +config_space = ConfigurationSpace(objects=[1, 2, 3]) +# Implicitly has only the identity symmetry operation +``` + +This is useful when you want to use the configuration space framework but don't need to identify symmetry-equivalent configurations. + +## Why Use Abstract Representation? + +### Separation of Concerns + +The abstract vector representation separates the mathematical logic of symmetry analysis from the physical details of specific systems. This means: + +- **Symmetry algorithms** work at the vector level, independent of coordinates or structures +- **Physical interpretation** is added as a separate layer when needed +- **The same code** handles crystals, molecules, or any other symmetric system + +### Computational Efficiency + +Working with integer vectors is computationally efficient: + +- Integer comparisons are fast +- Vectors can be hashed and stored in sets/dictionaries +- No floating-point arithmetic or coordinate transformations needed during enumeration +- **Symmetry operations are simple permutations** of integer indices - just rearranging vector elements rather than matrix-vector multiplication with floating-point coordinates + +### Generality + +The abstract approach makes bsym applicable to any problem involving symmetric arrangements of discrete objects. You're not limited to crystallographic applications - the same framework handles: + +- Disorder in crystal structures +- Molecular conformations +- Combinatorial problems with symmetry constraints +- Abstract group theory problems + +### From Abstract to Physical + +When working with real systems, the workflow is: + +1. **Define the abstract configuration space** - vector dimension and symmetry operations +2. **Enumerate configurations** - find unique arrangements using vector-based algorithms +3. **Map to physical structures** - interpret abstract configurations as coordinates, structures, etc. + +This separation allows the expensive symmetry analysis to happen at the abstract level, then efficiently generate corresponding physical structures only for the unique configurations. + +## Connecting to Real Structures + +### The CoordinateConfigSpace Class + +For systems where vector positions correspond to physical coordinates, bsym provides `CoordinateConfigSpace`, which extends `ConfigurationSpace` with coordinate information: +```python +from bsym import CoordinateConfigSpace +import numpy as np + +# Define coordinates for each position +coordinates = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) + +# Create configuration space with coordinates +coord_space = CoordinateConfigSpace(coordinates, symmetry_group=my_symmetry_group) +``` + +The `CoordinateConfigSpace` maintains the abstract vector representation internally while also storing the associated coordinates. This allows symmetry analysis to happen at the abstract level, with results mapped back to coordinates when needed. + +### The Pymatgen Interface + +For crystallographic applications, bsym provides an interface to work with pymatgen `Structure` objects. This handles: + +- Extracting symmetry operations from crystal structures +- Converting between abstract configurations and atomic structures +- Generating symmetry-inequivalent crystal structures from substitution patterns + +The pymatgen interface is covered in detail in the [User Guide](../user_guide/index.rst). The key point is that it operates as a wrapper around the abstract `ConfigurationSpace` machinery - symmetry analysis happens at the vector level, then results are converted to `Structure` objects. diff --git a/examples/figures/square_configuration_space.pdf b/docs/source/theory/figures/square_configuration_space.pdf similarity index 100% rename from examples/figures/square_configuration_space.pdf rename to docs/source/theory/figures/square_configuration_space.pdf diff --git a/examples/figures/square_unique_configurations.pdf b/docs/source/theory/figures/square_unique_configurations.pdf similarity index 100% rename from examples/figures/square_unique_configurations.pdf rename to docs/source/theory/figures/square_unique_configurations.pdf diff --git a/examples/figures/square_unique_configurations_2.pdf b/docs/source/theory/figures/square_unique_configurations_2.pdf similarity index 100% rename from examples/figures/square_unique_configurations_2.pdf rename to docs/source/theory/figures/square_unique_configurations_2.pdf diff --git a/examples/figures/triangular_c3_inversion.pdf b/docs/source/theory/figures/triangular_c3_inversion.pdf similarity index 100% rename from examples/figures/triangular_c3_inversion.pdf rename to docs/source/theory/figures/triangular_c3_inversion.pdf diff --git a/examples/figures/triangular_configuration_example_1.pdf b/docs/source/theory/figures/triangular_configuration_example_1.pdf similarity index 100% rename from examples/figures/triangular_configuration_example_1.pdf rename to docs/source/theory/figures/triangular_configuration_example_1.pdf diff --git a/examples/figures/triangular_configuration_space.pdf b/docs/source/theory/figures/triangular_configuration_space.pdf similarity index 100% rename from examples/figures/triangular_configuration_space.pdf rename to docs/source/theory/figures/triangular_configuration_space.pdf diff --git a/examples/figures/triangular_example_symmetry_operations.pdf b/docs/source/theory/figures/triangular_example_symmetry_operations.pdf similarity index 100% rename from examples/figures/triangular_example_symmetry_operations.pdf rename to docs/source/theory/figures/triangular_example_symmetry_operations.pdf diff --git a/examples/figures/triangular_rotation_operation.pdf b/docs/source/theory/figures/triangular_rotation_operation.pdf similarity index 100% rename from examples/figures/triangular_rotation_operation.pdf rename to docs/source/theory/figures/triangular_rotation_operation.pdf diff --git a/docs/source/theory/index.rst b/docs/source/theory/index.rst new file mode 100644 index 0000000..c3ce81e --- /dev/null +++ b/docs/source/theory/index.rst @@ -0,0 +1,14 @@ +Theory and Core Concepts +======================== + +This section explains the theoretical foundations and core concepts underlying ``bsym``. + +Understanding these concepts will help you use ``bsym`` more effectively and understand what the code is doing under the hood. + +.. toctree:: + :maxdepth: 2 + + configuration_spaces + symmetry_operations + unique_configurations + composition_enumeration diff --git a/docs/source/theory/symmetry_operations.md b/docs/source/theory/symmetry_operations.md new file mode 100644 index 0000000..71db9d5 --- /dev/null +++ b/docs/source/theory/symmetry_operations.md @@ -0,0 +1,262 @@ +# Symmetry Operations + +## What are Symmetry Operations? + +A **symmetry operation** is a transformation of a configuration space that maps the vector positions onto each other. In the abstract vector representation, a symmetry operation is simply a permutation of the vector indices. + +For a configuration space with n positions, a symmetry operation rearranges these positions while preserving the structure of the space. When applied to a configuration, a symmetry operation produces a new configuration by permuting the object labels according to the index permutation. + +### Example: Triangular Configuration Space + +Consider three positions arranged conceptually as an equilateral triangle: + +![Triangular configuration space](./figures/triangular_configuration_space.pdf) + +This is a 3-dimensional vector space with positions that we can label 0, 1, and 2 (corresponding to corners a, b, and c). + +A specific configuration, such as `[1, 1, 0]`, assigns labels to each position: + +![Triangular configuration example](./figures/triangular_configuration_example_1.pdf) + +where positions 0 and 1 have object type 1 (shown in black), and position 2 has object type 0 (shown in white). + +### Identity Operation + +Every configuration space has at least one symmetry operation: the **identity operation** (often labelled E), which leaves all positions unchanged: + +$$E = \begin{pmatrix}1 & 0 & 0\\0 & 1 & 0\\0 & 0 & 1\end{pmatrix}$$ + +This is the trivial permutation where every position maps to itself. + +### Rotations and Reflections + +For the triangular system, there are other symmetry operations including rotations and reflections: + +![Triangular symmetry operations](./figures/triangular_example_symmetry_operations.pdf) + +A reflection operation σ might swap positions, for example mapping position 1 to position 2 and position 2 to position 1, while leaving position 0 unchanged. + +### Physical Interpretation + +While symmetry operations are defined abstractly as permutations, they often correspond to physical transformations: + +- **Rotations**: Rotating a structure around an axis +- **Reflections**: Mirroring across a plane +- **Inversions**: Mapping through a centre point +- **Translations**: Shifting by a lattice vector (for space groups) + +These physical transformations, when applied to discrete sites, simply permute the site indices. + +## Matrix and Vector Representation + +### Permutation Matrices + +A symmetry operation can be represented as a permutation matrix that transforms configuration vectors. For an n-dimensional space, this is an n × n matrix where each row and column contains exactly one 1, with all other elements 0. + +For the identity operation on a 3-dimensional space: + +$$E = \begin{pmatrix}1 & 0 & 0\\0 & 1 & 0\\0 & 0 & 1\end{pmatrix}$$ + +For a rotation operation that maps position 0→1, 1→2, 2→0: + +$$C_3 = \begin{pmatrix}0 & 0 & 1\\1 & 0 & 0\\0 & 1 & 0\end{pmatrix}$$ + +Applying this matrix to configuration `[1, 1, 0]` gives: + +$$\begin{pmatrix}0 & 0 & 1\\1 & 0 & 0\\0 & 1 & 0\end{pmatrix} \begin{pmatrix}1\\1\\0\end{pmatrix} = \begin{pmatrix}0\\1\\1\end{pmatrix}$$ + +### Vector Notation + +A more compact representation uses a permutation vector showing where each position maps. Instead of the full matrix, we can write the C₃ rotation as: +``` +[2, 3, 1] +``` + +meaning: +- Position 1 → Position 2 +- Position 2 → Position 3 +- Position 3 → Position 1 + +This vector notation is more compact and easier to read. In bsym, symmetry operations can be created from this vector form: +```python +from bsym import SymmetryOperation + +# Create rotation operation using vector notation +c3 = SymmetryOperation.from_vector([2, 3, 1], label='C3') +``` + +Note that the vector notation uses 1-based indexing (positions numbered 1, 2, 3, ...), even though configurations use 0-based indexing internally (positions 0, 1, 2, ...). + +### Applying Operations to Configurations + +A symmetry operation transforms a configuration by permuting its elements: +```python +from bsym import Configuration, SymmetryOperation + +config = Configuration([1, 1, 0]) +c3 = SymmetryOperation.from_vector([2, 3, 1], label='C3') + +# Apply the operation +result = c3.operate_on(config) +# Or equivalently: +result = c3 * config + +print(result) # Configuration([0, 1, 1]) +``` + +![Rotation operation example](./figures/triangular_rotation_operation.pdf) + +## Inverting Symmetry Operations + +### Mathematical Inverse + +For every symmetry operation A, there exists an inverse operation A⁻¹ such that: + +$$A \cdot A^{-1} = E$$ + +where E is the identity operation. The inverse operation "undoes" the original operation. + +### Example: C₃ and C₃⁻¹ + +For the triangular configuration space, the inverse of C₃ (clockwise rotation by 120°) is C₃⁻¹ (anticlockwise rotation by 120°): +```python +from bsym import SymmetryOperation + +c3 = SymmetryOperation.from_vector([2, 3, 1], label='C3') +c3_inv = SymmetryOperation.from_vector([3, 1, 2], label='C3_inv') + +# The product gives the identity +result = c3 * c3_inv +print(result.matrix) +# [[1. 0. 0.] +# [0. 1. 0.] +# [0. 0. 1.]] +``` + +![C3 inversion](./figures/triangular_c3_inversion.pdf) + +### The `.invert()` Method + +Symmetry operations can be inverted programmatically using the `.invert()` method: +```python +c3 = SymmetryOperation.from_vector([2, 3, 1], label='C3') + +# Generate the inverse +c3_inv = c3.invert() +``` + +The resulting operation initially has no label. Labels can be set directly or using the `.set_label()` method: +```python +# Set label during inversion +c3_inv = c3.invert(label='C3_inv') + +# Or set label afterwards +c3_inv = c3.invert().set_label('C3_inv') +``` + +## Symmetry Groups + +### Definition + +A **symmetry group** is a collection of symmetry operations. In bsym, a `SymmetryGroup` object contains a set of `SymmetryOperation` objects that describe the symmetries of a configuration space. +```python +from bsym import SymmetryGroup, SymmetryOperation + +# Create individual operations +e = SymmetryOperation.from_vector([1, 2, 3], label='E') +c3 = SymmetryOperation.from_vector([2, 3, 1], label='C3') +c3_inv = SymmetryOperation.from_vector([3, 1, 2], label='C3_inv') + +# Combine into a symmetry group +symmetry_group = SymmetryGroup([e, c3, c3_inv]) +``` + +### Mathematical Groups + +The term "symmetry group" is used by convention, but a `SymmetryGroup` in bsym is not required to be a complete mathematical group. It is simply a collection of symmetry operations that are relevant for a particular analysis. + +A bsym `SymmetryGroup` may contain only a subset of operations - for example, you might include only the operations needed for a specific analysis, even if they don't form a complete group. + +### The C3v Point Group Example + +For the triangular configuration space, the complete C3v point group contains six operations: one identity, two rotations, and three reflections: + +![C3v symmetry operations](./figures/triangular_c3v_symmetry_operations.pdf) +```python +from bsym import PointGroup, SymmetryOperation + +# Identity +e = SymmetryOperation.from_vector([1, 2, 3], label='E') + +# Rotations +c3 = SymmetryOperation.from_vector([2, 3, 1], label='C3') +c3_inv = SymmetryOperation.from_vector([3, 1, 2], label='C3_inv') + +# Reflections +sigma_a = SymmetryOperation.from_vector([1, 3, 2], label='sigma_a') +sigma_b = SymmetryOperation.from_vector([3, 2, 1], label='sigma_b') +sigma_c = SymmetryOperation.from_vector([2, 1, 3], label='sigma_c') + +# Create point group +c3v = PointGroup([e, c3, c3_inv, sigma_a, sigma_b, sigma_c]) +``` + +### PointGroup and SpaceGroup + +For convenience, bsym provides `PointGroup` and `SpaceGroup` classes that are functionally equivalent to `SymmetryGroup`: +```python +from bsym import PointGroup, SpaceGroup + +# These are all equivalent: +group1 = SymmetryGroup([e, c3]) +group2 = PointGroup([e, c3]) +group3 = SpaceGroup([e, c3]) +``` + +The different class names simply make code more readable by indicating what type of symmetry is being described. + +## Creating Symmetry Groups from Files + +### Reading Unlabelled Operations + +Symmetry groups can be read from files containing permutation vectors. Each line represents one symmetry operation: +``` +# example file: pair_symmetry.txt +1 2 +2 1 +``` + +This defines a symmetry group for two equivalent sites with the identity (1→1, 2→2) and a swap operation (1→2, 2→1). +```python +from bsym import SymmetryGroup + +symmetry_group = SymmetryGroup.read_from_file('pair_symmetry.txt') +``` + +### Reading Labelled Operations + +For better readability, operations can be labelled: +``` +# example file: c3v_symmetry.txt +E 1 2 3 +C3 2 3 1 +C3_inv 3 1 2 +sigma_a 1 3 2 +sigma_b 3 2 1 +sigma_c 2 1 3 +``` +```python +from bsym import SymmetryGroup + +c3v = SymmetryGroup.read_from_file_with_labels('c3v_symmetry.txt') +``` + +The labels are used when displaying the symmetry group and can help identify specific operations during analysis. + +### File Format + +Both file formats use space-separated values: +- **Unlabelled**: Each line contains n integers representing the permutation +- **Labelled**: Each line starts with a label string, followed by n integers +- Comment lines starting with `#` are ignored +- Permutation vectors use 1-based indexing diff --git a/docs/source/theory/unique_configurations.md b/docs/source/theory/unique_configurations.md new file mode 100644 index 0000000..0ee6af5 --- /dev/null +++ b/docs/source/theory/unique_configurations.md @@ -0,0 +1,299 @@ +# Unique Configuration Enumeration + +## Configuration Equivalence + +Two configurations are **equivalent** if one can be transformed into the other by applying a symmetry operation from the configuration space's symmetry group. + +### Example: Square Configuration Space + +Consider four sites arranged in a square: + +![Square configuration space](./figures/square_configuration_space.pdf) + +Without considering symmetry, there are six distinct ways to place two occupied sites (label 1) and two unoccupied sites (label 0): + +- `[0, 0, 1, 1]` - positions 2 and 3 occupied +- `[0, 1, 0, 1]` - positions 1 and 3 occupied +- `[0, 1, 1, 0]` - positions 1 and 2 occupied +- `[1, 0, 0, 1]` - positions 0 and 3 occupied +- `[1, 0, 1, 0]` - positions 0 and 2 occupied +- `[1, 1, 0, 0]` - positions 0 and 1 occupied + +However, if the square has C4v symmetry (rotations and reflections), some of these configurations become equivalent. For instance, `[0, 0, 1, 1]` (adjacent sites occupied) is equivalent to `[0, 1, 1, 0]`, `[1, 1, 0, 0]`, and `[1, 0, 0, 1]` through rotations. + +### Checking Equivalence + +To check if configuration A is equivalent to configuration B under a symmetry group: + +1. Apply each symmetry operation in the group to configuration A +2. If any resulting configuration equals B, then A and B are equivalent +3. If no operation transforms A into B, they are inequivalent + +In code: +```python +from bsym import Configuration, ConfigurationSpace, SymmetryOperation + +# Create two configurations +config_a = Configuration([0, 0, 1, 1]) +config_b = Configuration([1, 1, 0, 0]) + +# Check if equivalent under a rotation +c2 = SymmetryOperation.from_vector([3, 4, 1, 2], label='C2') +result = c2.operate_on(config_a) + +print(result == config_b) # True - they are equivalent +``` + +## The Enumeration Problem + +### Finding All Unique Configurations + +Given: +- A configuration space with n positions +- A symmetry group with symmetry operations +- A site distribution specifying how many objects of each type to place + +The goal is to find **all symmetry-inequivalent configurations**. This is the complete set of configurations where no two members are equivalent to each other under the symmetry group. + +Equivalently, this is the minimal set that represents all possible arrangements: every possible permutation of the site distribution is equivalent (under symmetry) to exactly one configuration in this set. + +### Example: Square with C4v Symmetry + +For the square configuration space with 2 occupied and 2 unoccupied sites: + +Without symmetry, there are $\binom{4}{2} = 6$ distinct configurations. + +With C4v symmetry (8 operations: identity, 3 rotations, 4 reflections), these reduce to 2 unique configurations: + +![Square unique configurations](./figures/square_unique_configurations.pdf) + +1. **Adjacent sites occupied** - 4 equivalent configurations +2. **Diagonal sites occupied** - 2 equivalent configurations + +Total: 2 unique configurations representing all 6 possible arrangements. + +### Degeneracy + +Each unique configuration has an associated **degeneracy** (or multiplicity) - the number of equivalent configurations it represents. This is stored in the `count` attribute: +```python +from bsym import ConfigurationSpace, PointGroup + +# Create C4v symmetry group (8 operations) +c4v = PointGroup([e, c4, c4_inv, c2, sigma_x, sigma_y, sigma_ac, sigma_bd]) + +# Create configuration space +config_space = ConfigurationSpace(['a', 'b', 'c', 'd'], symmetry_group=c4v) + +# Find unique configurations +unique_configs = config_space.unique_configurations({1: 2, 0: 2}) + +for config in unique_configs: + print(f"{config.tolist()}: degeneracy = {config.count}") + +# Output: +# [0, 0, 1, 1]: degeneracy = 4 +# [0, 1, 0, 1]: degeneracy = 2 +``` + +The degeneracies sum to the total number of configurations: 4 + 2 = 6. + +## The Algorithm + +### Overview + +The bsym enumeration algorithm works by: + +1. **Generate candidates**: Create all possible distinct permutations of the object labels +2. **Check for equivalence**: For each candidate, check if it's equivalent to any previously identified unique configuration +3. **Store unique configurations**: If not equivalent, add it to the set of unique configurations +4. **Track degeneracy**: Count how many configurations are equivalent to each unique configuration + +### Key Algorithmic Insight + +The algorithm maintains a "seen" set containing all configurations that have been identified (either as unique or as equivalent to a unique configuration). + +For each candidate permutation: +- **If already in "seen"**: Skip it (it's equivalent to a previously found unique configuration) +- **If not in "seen"**: + 1. This is a new unique configuration + 2. Generate all its symmetry equivalents by applying all symmetry operations + 3. Add all equivalents to the "seen" set + 4. Record the count of equivalents as the configuration's degeneracy + +This ensures symmetry operations are only applied to configurations that represent genuinely new unique arrangements. + +### Generating Candidates + +For a given site distribution, the algorithm generates all unique permutations of object labels using Knuth's Algorithm L, which efficiently handles indistinguishable objects. + +For example, with site distribution `{1: 2, 0: 2}`: +``` +[0, 0, 1, 1] +[0, 1, 0, 1] +[0, 1, 1, 0] +[1, 0, 0, 1] +[1, 0, 1, 0] +[1, 1, 0, 0] +``` + +### Testing Equivalence with Hashing + +Configurations are stored using their byte array representation, allowing fast hash-based lookup and comparison. + +### Worked Example: Square with C2 Rotation + +Consider a simple square with only one non-identity symmetry operation: 180° rotation (C2), which maps positions 0↔2 and 1↔3. + +We'll enumerate configurations with site distribution `{1: 2, 0: 2}`. + +**Permutations to check**: `[0,0,1,1]`, `[0,1,0,1]`, `[0,1,1,0]`, `[1,0,0,1]`, `[1,0,1,0]`, `[1,1,0,0]` + +--- + +**Step 1**: Process `[0, 0, 1, 1]` + +1. Check if in "seen" set → No +2. This is a new unique configuration +3. Generate symmetry equivalents: + - Identity: `[0, 0, 1, 1]` + - C2: `[1, 1, 0, 0]` (positions 0↔2, 1↔3) +4. Add both to "seen" set: `{[0,0,1,1], [1,1,0,0]}` +5. Record unique configuration `[0, 0, 1, 1]` with degeneracy = 2 + +--- + +**Step 2**: Process `[0, 1, 0, 1]` + +1. Check if in "seen" set → No +2. This is a new unique configuration +3. Generate symmetry equivalents: + - Identity: `[0, 1, 0, 1]` + - C2: `[0, 1, 0, 1]` (same! This configuration is symmetric) +4. Add to "seen" set: `{[0,0,1,1], [1,1,0,0], [0,1,0,1]}` +5. Record unique configuration `[0, 1, 0, 1]` with degeneracy = 1 + +--- + +**Step 3**: Process `[0, 1, 1, 0]` + +1. Check if in "seen" set → No +2. This is a new unique configuration +3. Generate symmetry equivalents: + - Identity: `[0, 1, 1, 0]` + - C2: `[1, 0, 0, 1]` +4. Add both to "seen" set: `{[0,0,1,1], [1,1,0,0], [0,1,0,1], [0,1,1,0], [1,0,0,1]}` +5. Record unique configuration `[0, 1, 1, 0]` with degeneracy = 2 + +--- + +**Step 4**: Process `[1, 0, 0, 1]` + +1. Check if in "seen" set → **Yes!** +2. Skip (already counted as equivalent to `[0, 1, 1, 0]`) + +--- + +**Step 5**: Process `[1, 0, 1, 0]` + +1. Check if in "seen" set → No +2. This is a new unique configuration +3. Generate symmetry equivalents: + - Identity: `[1, 0, 1, 0]` + - C2: `[1, 0, 1, 0]` (same! This configuration is symmetric) +4. Add to "seen" set +5. Record unique configuration `[1, 0, 1, 0]` with degeneracy = 1 + +--- + +**Step 6**: Process `[1, 1, 0, 0]` + +1. Check if in "seen" set → **Yes!** +2. Skip (already counted as equivalent to `[0, 0, 1, 1]`) + +--- + +**Result**: 4 unique configurations with degeneracies [2, 1, 2, 1] + +Total configurations represented: 2 + 1 + 2 + 1 = 6 ✓ + +### Implementation in bsym + +The same enumeration can be performed using bsym: +```python +from bsym import ConfigurationSpace, SymmetryGroup, SymmetryOperation + +# Define C2 symmetry group +identity = SymmetryOperation.from_vector([1, 2, 3, 4], label='E') +c2 = SymmetryOperation.from_vector([3, 4, 1, 2], label='C2') +symmetry_group = SymmetryGroup([identity, c2]) + +# Create configuration space +config_space = ConfigurationSpace( + objects=[1, 2, 3, 4], + symmetry_group=symmetry_group +) + +# Find unique configurations +unique_configs = config_space.unique_configurations({1: 2, 0: 2}) + +# Display results +for config in unique_configs: + print(f"{config.tolist()}: degeneracy = {config.count}") + +# Output: +# [0, 0, 1, 1]: degeneracy = 2 +# [0, 1, 0, 1]: degeneracy = 1 +# [0, 1, 1, 0]: degeneracy = 2 +# [1, 0, 1, 0]: degeneracy = 1 +``` + +The algorithm produces 4 unique configurations with the correct degeneracies, matching our manual enumeration. + +## Site Distributions and Composition + +### Specifying Object Distributions + +The `unique_configurations()` method requires a **site distribution** dictionary that specifies how many objects of each type to place in the configuration space. + +Format: `{label: count, ...}` + +Examples: +- `{1: 2, 0: 2}` - 2 objects of type 1, 2 objects of type 0 +- `{0: 1, 1: 2, 2: 1}` - ternary system with 1 of type 0, 2 of type 1, 1 of type 2 +- `{1: 4}` - 4 objects of type 1 (remaining sites implicitly unoccupied) + +### Label Conventions + +Object labels are arbitrary integers. Common conventions: + +- **Binary systems**: Use 0 and 1 + - 0 for vacant, 1 for occupied + - 0 for species A, 1 for species B + +- **Multi-species systems**: Use 0, 1, 2, 3, ... + - Sequential integers for different species + +The specific integers don't matter - what matters is which positions have the same vs different labels. + +### Total Site Count + +The sum of counts in the site distribution must equal the dimension of the configuration space: +```python +config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) # 4-dimensional + +# Valid: +config_space.unique_configurations({1: 2, 0: 2}) # 2 + 2 = 4 ✓ + +# Invalid: +config_space.unique_configurations({1: 3, 0: 2}) # 3 + 2 = 5 ✗ +``` + +### Partial Occupancy + +If you want some sites to remain unoccupied or unlabelled, include them explicitly: +```python +# 10-site system with 3 occupied sites and 7 vacant +config_space.unique_configurations({1: 3, 0: 7}) +``` + +This is particularly relevant for crystallographic applications where not all sites are substituted. diff --git a/docs/source/user_guide/basic_substitutions.ipynb b/docs/source/user_guide/basic_substitutions.ipynb new file mode 100644 index 0000000..6df8a82 --- /dev/null +++ b/docs/source/user_guide/basic_substitutions.ipynb @@ -0,0 +1,393 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic Substitutions\n", + "\n", + "This guide demonstrates how to use `bsym` to generate symmetry-inequivalent structures by substituting atoms in a parent structure.\n", + "\n", + "## Overview\n", + "\n", + "The `bsym.interface.pymatgen.unique_structure_substitutions` function takes a parent structure and generates all symmetry-unique configurations when substituting specific sites with different species.\n", + "\n", + "**Key parameters:**\n", + "- `structure`: The parent pymatgen Structure\n", + "- `to_substitute`: The atomic species label to be replaced\n", + "- `site_distribution`: Dictionary specifying how many of each substituting species (e.g., `{'Na': 1, 'Li': 15}`)\n", + "\n", + "**Returns:** A list of Structure objects, each representing a symmetry-inequivalent configuration." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simple Example: Single Substitution\n", + "\n", + "Let's start with a simple 4×4 square lattice of lithium atoms and substitute one Li with Na.\n", + "\n", + "### Setting Up the Parent Structure" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from pymatgen.core import Structure, Lattice\n", + "from bsym.interface.pymatgen import unique_structure_substitutions\n", + "\n", + "# Create a simple square lattice\n", + "coords = np.array([[0.0, 0.0, 0.0]])\n", + "atom_list = ['Li']\n", + "lattice = Lattice.from_parameters(a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90)\n", + "parent_structure = Structure(lattice, atom_list, coords) * [4, 4, 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This creates a 4×4 supercell with 16 lithium atoms." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Full Formula (Li16)\n", + "Reduced Formula: Li\n", + "abc : 4.000000 4.000000 1.000000\n", + "angles: 90.000000 90.000000 90.000000\n", + "pbc : True True True\n", + "Sites (16)\n", + " # SP a b c\n", + "--- ---- ---- ---- ---\n", + " 0 Li 0 0 0\n", + " 1 Li 0 0.25 0\n", + " 2 Li 0 0.5 0\n", + " 3 Li 0 0.75 0\n", + " 4 Li 0.25 0 0\n", + " 5 Li 0.25 0.25 0\n", + " 6 Li 0.25 0.5 0\n", + " 7 Li 0.25 0.75 0\n", + " 8 Li 0.5 0 0\n", + " 9 Li 0.5 0.25 1\n", + " 10 Li 0.5 0.5 0\n", + " 11 Li 0.5 0.75 0\n", + " 12 Li 0.75 0 0\n", + " 13 Li 0.75 0.25 0\n", + " 14 Li 0.75 0.5 1\n", + " 15 Li 0.75 0.75 0\n" + ] + } + ], + "source": [ + "print(parent_structure)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Performing the Substitution\n", + "\n", + "Now we substitute one Li atom with Na:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of unique structures: 1\n" + ] + } + ], + "source": [ + "unique_structures = unique_structure_substitutions(\n", + " parent_structure, \n", + " 'Li',\n", + " {'Na': 1, 'Li': 15}\n", + ")\n", + "\n", + "print(f\"Number of unique structures: {len(unique_structures)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Due to the high symmetry of the square lattice, all single-site substitutions are symmetry-equivalent, so we get only **one unique structure**.\n", + "\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Full Formula (Na1 Li15)\n", + "Reduced Formula: NaLi15\n", + "abc : 4.000000 4.000000 1.000000\n", + "angles: 90.000000 90.000000 90.000000\n", + "pbc : True True True\n", + "Sites (16)\n", + " # SP a b c\n", + "--- ---- ---- ---- ---\n", + " 0 Na 0 0 0\n", + " 1 Li 0 0.25 0\n", + " 2 Li 0 0.5 0\n", + " 3 Li 0 0.75 0\n", + " 4 Li 0.25 0 0\n", + " 5 Li 0.25 0.25 0\n", + " 6 Li 0.25 0.5 0\n", + " 7 Li 0.25 0.75 0\n", + " 8 Li 0.5 0 0\n", + " 9 Li 0.5 0.25 1\n", + " 10 Li 0.5 0.5 0\n", + " 11 Li 0.5 0.75 0\n", + " 12 Li 0.75 0 0\n", + " 13 Li 0.75 0.25 0\n", + " 14 Li 0.75 0.5 1\n", + " 15 Li 0.75 0.75 0\n" + ] + } + ], + "source": [ + "print(unique_structures[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Understanding the Output\n", + "\n", + "Each structure in the returned list has a special attribute that tracks degeneracy:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of equivalent configurations: 16\n" + ] + } + ], + "source": [ + "na_substituted = unique_structures[0]\n", + "print(f\"Number of equivalent configurations: {na_substituted.number_of_equivalent_configurations}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `number_of_equivalent_configurations` attribute tells us that this unique configuration has 16 symmetry-equivalent variants (one for substituting at each of the 16 sites)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## More Complex Example: TiOF2 in ReO₃ Structure\n", + "\n", + "Let's look at a more realistic example: finding unique O/F orderings in TiOF2 with a pseudo-ReO3 structure.\n", + "\n", + "### Setting Up the Structure" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created supercell with 32 atoms\n", + "Number of X sites to substitute: 0\n" + ] + } + ], + "source": [ + "a = 3.798 # lattice parameter in Ångströms\n", + "\n", + "coords = np.array([[0.0, 0.0, 0.0],\n", + " [0.5, 0.0, 0.0],\n", + " [0.0, 0.5, 0.0],\n", + " [0.0, 0.0, 0.5]])\n", + "atom_list = ['Ti', 'X', 'X', 'X']\n", + "lattice = Lattice.from_parameters(a=a, b=a, c=a, alpha=90, beta=90, gamma=90)\n", + "unit_cell = Structure(lattice, atom_list, coords)\n", + "\n", + "# Create a 2×2×2 supercell\n", + "parent_structure = unit_cell * [2, 2, 2]\n", + "print(f\"Created supercell with {len(parent_structure)} atoms\")\n", + "print(f\"Number of X sites to substitute: {len([s for s in parent_structure if s.species_string == 'X'])}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The 'X' atoms represent the anion sites that we'll substitute with O and F." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Finding Unique O/F Orderings\n", + "\n", + "A 2 × 2 × 2 supercell has 24 anion sites. To get the right stoichiometry for TiOF2, we need 8 oxygen atoms and 16 fluorine atoms:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of symmetry-inequivalent configurations: 2664\n" + ] + } + ], + "source": [ + "unique_structures = unique_structure_substitutions(\n", + " parent_structure, \n", + " 'X', \n", + " {'O': 8, 'F': 16}\n", + ")\n", + "\n", + "print(f\"Number of symmetry-inequivalent configurations: {len(unique_structures)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each structure in `unique_structures` represents a distinct arrangement of O and F atoms that cannot be transformed into each other by the symmetry operations of the parent structure." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Checking the Degeneracy\n", + "\n", + "You can check how many equivalent configurations each unique structure represents:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Structure 0: 3 equivalent configurations\n", + "Structure 1: 192 equivalent configurations\n", + "Structure 2: 192 equivalent configurations\n", + "Structure 3: 96 equivalent configurations\n", + "Structure 4: 96 equivalent configurations\n", + "\n", + "Total configurations (including equivalent): 735471\n" + ] + } + ], + "source": [ + "for i, structure in enumerate(unique_structures[:5]): # check the first 5 structures\n", + " n_equiv = structure.number_of_equivalent_configurations\n", + " print(f\"Structure {i}: {n_equiv} equivalent configurations\")\n", + "\n", + "total_configs = sum(s.number_of_equivalent_configurations for s in unique_structures)\n", + "print(f\"\\nTotal configurations (including equivalent): {total_configs}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The total number of unique structures multiplied by their degeneracies should equal the total number of ways to choose the O/F distribution without considering symmetry." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Key Points\n", + "\n", + "- `unique_structure_substitutions()` automatically detects the symmetry operations of your parent structure\n", + "- The function only returns symmetry-inequivalent structures, dramatically reducing the number of configurations you need to consider\n", + "- The `number_of_equivalent_configurations` attribute tracks degeneracy for statistical mechanics calculations\n", + "- All sites labelled with `to_substitute` are considered as potential substitution sites\n", + "\n", + "## Next Steps\n", + "\n", + "- For sequential substitutions where you want to track the full degeneracy chain, see [Fixed Composition Substitutions](fixed_composition.ipynb)\n", + "- For generating structures across different compositions, see [Varying Composition Substitutions](varying_composition.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/figures/pymatgen_example_one_site.pdf b/docs/source/user_guide/figures/pymatgen_example_one_site.pdf similarity index 100% rename from examples/figures/pymatgen_example_one_site.pdf rename to docs/source/user_guide/figures/pymatgen_example_one_site.pdf diff --git a/examples/figures/pymatgen_example_two_sites.pdf b/docs/source/user_guide/figures/pymatgen_example_two_sites.pdf similarity index 100% rename from examples/figures/pymatgen_example_two_sites.pdf rename to docs/source/user_guide/figures/pymatgen_example_two_sites.pdf diff --git a/docs/source/user_guide/fixed_composition.ipynb b/docs/source/user_guide/fixed_composition.ipynb new file mode 100644 index 0000000..f64d4f6 --- /dev/null +++ b/docs/source/user_guide/fixed_composition.ipynb @@ -0,0 +1,361 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Fixed Composition Substitutions\n", + "\n", + "This guide demonstrates how to perform sequential substitutions and track the full configuration degeneracy across multiple substitution steps.\n", + "\n", + "## Sequential Substitutions\n", + "\n", + "When performing multiple substitutions in sequence, each step generates new symmetry-inequivalent structures. Understanding how degeneracy accumulates across these steps is important for statistical mechanics calculations.\n", + "\n", + "### The Li-Na-Mg Example\n", + "\n", + "Let's start with the same 4×4 lithium lattice from the basic substitutions example, and perform two sequential substitutions." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created structure with 16 Li sites\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from pymatgen.core import Structure, Lattice\n", + "from bsym.interface.pymatgen import unique_structure_substitutions\n", + "\n", + "# Create a 4×4 square lattice\n", + "coords = np.array([[0.0, 0.0, 0.0]])\n", + "atom_list = ['Li']\n", + "lattice = Lattice.from_parameters(a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90)\n", + "parent_structure = Structure(lattice, atom_list, coords) * [4, 4, 1]\n", + "\n", + "print(f\"Created structure with {len(parent_structure)} Li sites\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Substitute Li → Na\n", + "\n", + "First, we substitute one lithium with sodium:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of unique structures after Na substitution: 1\n", + "Degeneracy: 16\n" + ] + } + ], + "source": [ + "unique_structures = unique_structure_substitutions(\n", + " parent_structure,\n", + " 'Li',\n", + " {'Na': 1, 'Li': 15}\n", + ")\n", + "\n", + "print(f\"Number of unique structures after Na substitution: {len(unique_structures)}\")\n", + "na_substituted = unique_structures[0]\n", + "print(f\"Degeneracy: {na_substituted.number_of_equivalent_configurations}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we saw before, due to the high symmetry of the square lattice, we get one unique structure with 16 equivalent configurations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Substitute Li → Mg\n", + "\n", + "Now we take the Na-substituted structure and perform a second substitution, replacing one more Li with Mg. The Li→Na substitution has broken the symmetry of the parent lattice, so we now get multiple unique structures:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of unique structures: 5\n" + ] + } + ], + "source": [ + "unique_structures_with_mg = unique_structure_substitutions(\n", + " na_substituted,\n", + " 'Li',\n", + " {'Mg': 1, 'Li': 14}\n", + ")\n", + "\n", + "print(f\"Number of unique structures: {len(unique_structures_with_mg)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now have five symmetry-inequivalent configurations, distinguished by the distance between the Na and Mg atoms:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can verify these correspond to the five distinct Na-Mg separation distances:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Squared distances between Na and Mg: [1. 2. 4. 5. 8.]\n" + ] + } + ], + "source": [ + "distances_squared = np.array(sorted([\n", + " s.get_distance(s.indices_from_symbol('Na')[0],\n", + " s.indices_from_symbol('Mg')[0])**2\n", + " for s in unique_structures_with_mg\n", + "]))\n", + "\n", + "print(f\"Squared distances between Na and Mg: {distances_squared}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding Configuration Degeneracy\n", + "\n", + "When performing sequential substitutions, there are two relevant degeneracy values:\n", + "\n", + "### `number_of_equivalent_configurations`\n", + "\n", + "This tracks the degeneracy from the **most recent substitution step only**:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Structure 0: 4 equivalent configurations (from Mg substitution)\n", + "Structure 1: 2 equivalent configurations (from Mg substitution)\n", + "Structure 2: 4 equivalent configurations (from Mg substitution)\n", + "Structure 3: 4 equivalent configurations (from Mg substitution)\n", + "Structure 4: 1 equivalent configurations (from Mg substitution)\n" + ] + } + ], + "source": [ + "for i, structure in enumerate(unique_structures_with_mg):\n", + " n_equiv = structure.number_of_equivalent_configurations\n", + " print(f\"Structure {i}: {n_equiv} equivalent configurations (from Mg substitution)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `full_configuration_degeneracy`\n", + "\n", + "This tracks the **total degeneracy** from all substitution steps, relative to the original parent structure:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Structure 0: 64 total configurations (from both substitutions)\n", + "Structure 1: 32 total configurations (from both substitutions)\n", + "Structure 2: 64 total configurations (from both substitutions)\n", + "Structure 3: 64 total configurations (from both substitutions)\n", + "Structure 4: 16 total configurations (from both substitutions)\n" + ] + } + ], + "source": [ + "for i, structure in enumerate(unique_structures_with_mg):\n", + " full_deg = structure.full_configuration_degeneracy\n", + " print(f\"Structure {i}: {full_deg} total configurations (from both substitutions)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `full_configuration_degeneracy` equals `number_of_equivalent_configurations` multiplied by the degeneracy from the previous step (16 for the Na substitution).\n", + "\n", + "This is important for statistical mechanics: if you're calculating configuration probabilities or partition functions, you need the full degeneracy relative to your starting structure." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Single-Step Multi-Species Substitution\n", + "\n", + "You can also perform both substitutions in a single step by specifying all species at once:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of unique structures: 5\n" + ] + } + ], + "source": [ + "unique_structures_single_step = unique_structure_substitutions(\n", + " parent_structure,\n", + " 'Li',\n", + " {'Mg': 1, 'Na': 1, 'Li': 14}\n", + ")\n", + "\n", + "print(f\"Number of unique structures: {len(unique_structures_single_step)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We get the same five unique structures. However, the degeneracy values are different:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Single-step substitution degeneracies:\n", + "Structure 0: number_of_equivalent = 64, full_degeneracy = 64\n", + "Structure 1: number_of_equivalent = 32, full_degeneracy = 32\n", + "Structure 2: number_of_equivalent = 64, full_degeneracy = 64\n", + "Structure 3: number_of_equivalent = 64, full_degeneracy = 64\n", + "Structure 4: number_of_equivalent = 16, full_degeneracy = 16\n", + "\n", + "They are equal because there was only one substitution step.\n" + ] + } + ], + "source": [ + "print(\"Single-step substitution degeneracies:\")\n", + "for i, structure in enumerate(unique_structures_single_step):\n", + " n_equiv = structure.number_of_equivalent_configurations\n", + " full_deg = structure.full_configuration_degeneracy\n", + " print(f\"Structure {i}: number_of_equivalent = {n_equiv}, full_degeneracy = {full_deg}\")\n", + " \n", + "print(\"\\nThey are equal because there was only one substitution step.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When performing substitutions in a single step, `number_of_equivalent_configurations` and `full_configuration_degeneracy` contain the same values, since there's only one substitution step to track." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Key Points\n", + "\n", + "- **Sequential substitutions** can break symmetry at each step, generating different numbers of unique structures\n", + "- **`number_of_equivalent_configurations`** tracks degeneracy from the most recent substitution only\n", + "- **`full_configuration_degeneracy`** tracks total degeneracy across all substitution steps\n", + "- **Single-step multi-species substitution** is convenient when you don't need to track intermediate structures\n", + "- For statistical mechanics calculations, use `full_configuration_degeneracy` to get correct configuration weights\n", + "\n", + "## Next Steps\n", + "\n", + "- For generating structures across different compositions, see [Varying Composition Substitutions](varying_composition.ipynb)\n", + "- For performance tips with large systems, see [Performance and Progress Tracking](performance.ipynb)\n", + "- To understand the theory behind configuration enumeration, see the [Theory section](../theory/unique_configurations.md)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst new file mode 100644 index 0000000..f74f931 --- /dev/null +++ b/docs/source/user_guide/index.rst @@ -0,0 +1,14 @@ +User Guide +========== + +Practical guides for using ``bsym`` with pymatgen structures. + +These guides show you how to solve common crystallographic problems using the ``bsym.interface.pymatgen`` module. + +.. toctree:: + :maxdepth: 2 + + basic_substitutions + fixed_composition + varying_composition + multi_level_disorder diff --git a/docs/source/user_guide/multi_level_disorder.ipynb b/docs/source/user_guide/multi_level_disorder.ipynb new file mode 100644 index 0000000..411c49c --- /dev/null +++ b/docs/source/user_guide/multi_level_disorder.ipynb @@ -0,0 +1,372 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Multi-Level Disorder\n", + "\n", + "This guide demonstrates how to enumerate structures with disorder on **multiple independent subsets** of sites, such as simultaneous cation and anion disorder.\n", + "\n", + "## The Problem\n", + "\n", + "Sometimes you need to explore disorder on more than one subset of sites. For example, we might be interested in mixed cation/anion disorder in (Ti,Zr)O2:\n", + "- **Cation disorder**: Ti/Zr substitution on metal sites\n", + "- **Anion disorder**: O/F substitution on anion sites\n", + "\n", + "These disorders are independent—they occur on different sets of sites—but they interact through symmetry breaking.\n", + "\n", + "## The Hierarchical Approach\n", + "\n", + "When you disorder one subset of sites, the resulting structure typically has lower symmetry than the parent. This reduced symmetry should be used when enumerating disorder on the second subset.\n", + "\n", + "### Algorithm\n", + "\n", + "1. **Level 1**: Enumerate disorder on the first subset using the parent structure's symmetry\n", + "2. **Level 2**: For each Level 1 configuration:\n", + " - The configuration has its own (typically reduced) symmetry\n", + " - Enumerate disorder on the second subset using this reduced symmetry\n", + "3. Collect all Level 2 structures\n", + "\n", + "This hierarchical approach ensures:\n", + "- No duplicate structures\n", + "- Correct symmetry analysis at each level\n", + "- Computational efficiency through symmetry reduction at each level" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example: Ti/Zr and O/F Disorder in TiOF2\n", + "\n", + "Let us work through a realistic example: a 2×2×2 supercell of TiOF2 with disorder on both cation and anion sublattices.\n", + "\n", + "### Setting Up the Parent Structure" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created supercell with 32 atoms\n", + " - 8 Ti sites\n", + " - 0 X sites (to be O/F)\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from pymatgen.core import Structure, Lattice\n", + "from bsym.interface.pymatgen import unique_structure_substitutions\n", + "\n", + "# Create 2×2×2 TiOF2 supercell\n", + "a = 3.798 # lattice parameter in Ångströms\n", + "\n", + "coords = np.array([[0.0, 0.0, 0.0],\n", + " [0.5, 0.0, 0.0],\n", + " [0.0, 0.5, 0.0],\n", + " [0.0, 0.0, 0.5]])\n", + "atom_list = ['Ti', 'X', 'X', 'X']\n", + "lattice = Lattice.from_parameters(a=a, b=a, c=a, alpha=90, beta=90, gamma=90)\n", + "unit_cell = Structure(lattice, atom_list, coords)\n", + "\n", + "# Create a 2×2×2 supercell\n", + "parent_structure = unit_cell * [2, 2, 2]\n", + "print(f\"Created supercell with {len(parent_structure)} atoms\")\n", + "print(f\" - {len([s for s in parent_structure if s.species_string == 'Ti'])} Ti sites\")\n", + "print(f\" - {len([s for s in parent_structure if s.species_string == 'X'])} X sites (to be O/F)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hierarchical Enumeration\n", + "\n", + "We will enumerate Ti/Zr disorder first (smaller combinatorial space), then O/F disorder for each Ti/Zr arrangement." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Level 1: Enumerating Ti/Zr arrangements...\n", + "Found 3 unique Ti/Zr arrangements\n", + "\n" + ] + } + ], + "source": [ + "# Level 1: Ti/Zr disorder on cation sites\n", + "print(\"Level 1: Enumerating Ti/Zr arrangements...\")\n", + "level1_structures = unique_structure_substitutions(\n", + " parent_structure,\n", + " 'Ti', # Substitute Ti sites\n", + " {'Ti': 6, 'Zr': 2} # 6 Ti, 2 Zr\n", + ")\n", + "print(f\"Found {len(level1_structures)} unique Ti/Zr arrangements\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Level 2: Enumerating O/F arrangements for each Ti/Zr configuration...\n", + " Ti/Zr config 1: 29371 O/F arrangements\n", + " Ti/Zr config 2: 28955 O/F arrangements\n", + " Ti/Zr config 3: 9782 O/F arrangements\n", + "\n", + "Total unique structures: 68108\n" + ] + } + ], + "source": [ + "# Level 2: O/F disorder for each Ti/Zr arrangement\n", + "print(\"Level 2: Enumerating O/F arrangements for each Ti/Zr configuration...\")\n", + "all_structures = []\n", + "\n", + "for i, structure in enumerate(level1_structures):\n", + " level2_structures = unique_structure_substitutions(\n", + " structure, # Uses the reduced symmetry of this Ti/Zr arrangement\n", + " 'X',\n", + " {'O': 8, 'F': 16}\n", + " )\n", + " print(f\" Ti/Zr config {i+1}: {len(level2_structures)} O/F arrangements\")\n", + " all_structures.extend(level2_structures)\n", + "\n", + "print(f\"\\nTotal unique structures: {len(all_structures)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding the Output\n", + "\n", + "In this example:\n", + "- **Level 1** produces a modest number of unique Ti/Zr cation arrangements\n", + "- **Level 2** varies for each Ti/Zr configuration:\n", + " - Some Ti/Zr arrangements preserve more symmetry → fewer O/F arrangements needed\n", + " - Other Ti/Zr arrangements break more symmetry → more O/F arrangements needed\n", + "- The total combines all possibilities across both disorder types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementation Pattern\n", + "\n", + "The general pattern for multi-level disorder is:\n", + "\n", + "```python\n", + "# Level 1: First disorder type\n", + "level1_configs = unique_structure_substitutions(\n", + " parent_structure, \n", + " species_to_substitute_1, \n", + " composition_1\n", + ")\n", + "\n", + "# Level 2: Second disorder type\n", + "all_configs = []\n", + "for config in level1_configs:\n", + " level2_configs = unique_structure_substitutions(\n", + " config, # Each has its own symmetry\n", + " species_to_substitute_2, \n", + " composition_2\n", + " )\n", + " all_configs.extend(level2_configs)\n", + "```\n", + "\n", + "This pattern extends naturally to three or more levels by adding additional nested loops." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Iterative Approach for Multiple Levels\n", + "\n", + "For cases with three or more disorder types, nested loops become unwieldy. An iterative approach can be used instead that generalises to any number of levels:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def enumerate_multilevel_disorder(parent_structure, disorder_specs):\n", + " \"\"\"\n", + " Enumerate structures with multiple levels of disorder.\n", + " \n", + " Args:\n", + " parent_structure: Initial pymatgen Structure\n", + " disorder_specs: List of dicts, each containing:\n", + " - 'to_substitute': species label to replace\n", + " - 'site_distribution': dict of {species: count}\n", + " \n", + " Returns:\n", + " List of Structure objects with all disorder levels applied\n", + " \"\"\"\n", + " structures = [parent_structure]\n", + " \n", + " for level, spec in enumerate(disorder_specs, 1):\n", + " print(f\"Level {level}: Enumerating {spec['to_substitute']} → {spec['site_distribution']}\")\n", + " new_structures = []\n", + " \n", + " for i, structure in enumerate(structures):\n", + " if i % 100 == 0 and len(structures) > 100:\n", + " print(f\" Processing structure {i+1}/{len(structures)}...\")\n", + " \n", + " level_structures = unique_structure_substitutions(\n", + " structure,\n", + " spec['to_substitute'],\n", + " spec['site_distribution']\n", + " )\n", + " new_structures.extend(level_structures)\n", + " \n", + " print(f\" Generated {len(new_structures)} structures\\n\")\n", + " structures = new_structures\n", + " \n", + " return structures" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Level 1: Enumerating Ti → {'Ti': 6, 'Zr': 2}\n", + " Generated 3 structures\n", + "\n", + "Level 2: Enumerating X → {'O': 8, 'F': 16}\n", + " Generated 68108 structures\n", + "\n", + "Total unique structures: 68108\n" + ] + } + ], + "source": [ + "disorder_specs = [\n", + " {\n", + " 'to_substitute': 'Ti',\n", + " 'site_distribution': {'Ti': 6, 'Zr': 2}\n", + " },\n", + " {\n", + " 'to_substitute': 'X',\n", + " 'site_distribution': {'O': 8, 'F': 16}\n", + " }\n", + "]\n", + "\n", + "# Run the multi-level enumeration\n", + "all_structures = enumerate_multilevel_disorder(parent_structure, disorder_specs)\n", + "\n", + "print(f\"Total unique structures: {len(all_structures)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This iterative approach:\n", + "- Generalizes easily to 3+ disorder levels\n", + "- Avoids deeply nested loops\n", + "- Makes it easy to modify or reorder disorder specifications\n", + "- Provides progress tracking for long enumerations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Notes\n", + "\n", + "### Current Implementation\n", + "\n", + "This hierarchical enumeration can be implemented in two ways:\n", + "\n", + "1. **Manual chaining** (shown in the first example): Explicitly nest the `unique_structure_substitutions` calls. Best for 2 levels or when you need fine control over the process.\n", + "\n", + "2. **Iterative approach** (shown above): Use the `enumerate_multilevel_disorder` function to handle an arbitrary number of levels. Best for 3+ levels or when you want cleaner, more maintainable code.\n", + "\n", + "### Choosing the Level Order\n", + "\n", + "You can enumerate the subsets in any order—the final set of structures will be the same. However:\n", + "- Starting with the **smaller combinatorial space** (fewer permutations) means fewer level-1 structures to loop over\n", + "- Starting with disorder that **breaks symmetry more** may lead to faster level-2 enumerations\n", + "- In practice, performance is often similar regardless of ordering\n", + "\n", + "When using the iterative approach, simply reorder the entries in `disorder_specs` to change the enumeration order.\n", + "\n", + "### Memory Considerations\n", + "\n", + "For very large structure sets:\n", + "- Process structures in batches rather than storing all in memory\n", + "- In the iterative approach, you can modify the function to write structures to disk after each level rather than keeping them all in the `structures` list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "- **Multi-level disorder** requires enumerating permutations on multiple independent subsets of sites\n", + "- **The hierarchical approach** enumerates one level at a time, using the appropriate symmetry at each step\n", + "- **Two implementation patterns** are available:\n", + " - Manual chaining for 2 levels or fine-grained control\n", + " - Iterative approach for 3+ levels or cleaner code\n", + "- Each level uses the symmetry of configurations from the previous level, ensuring correct and efficient enumeration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/user_guide/varying_composition.ipynb b/docs/source/user_guide/varying_composition.ipynb new file mode 100644 index 0000000..19db480 --- /dev/null +++ b/docs/source/user_guide/varying_composition.ipynb @@ -0,0 +1,494 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Varying Composition Substitutions\n", + "\n", + "This guide demonstrates how to generate symmetry-inequivalent structures across multiple compositions in a single operation using `unique_structure_substitutions_by_composition`.\n", + "\n", + "## Overview\n", + "\n", + "The `unique_structure_substitutions_by_composition` function extends the basic substitution functionality by systematically exploring all possible compositions of your specified species.\n", + "\n", + "**Key differences from `unique_structure_substitutions`:**\n", + "- Input: Provide a **list of species** instead of a fixed `site_distribution` dict\n", + "- Output: Returns a **dictionary** mapping composition tuples to lists of structures\n", + "- Explores **all possible compositions** (or a constrained range)\n", + "\n", + "This is particularly useful when you want to:\n", + "- Survey structures across a composition range\n", + "- Build phase diagrams\n", + "- Screen materials with variable stoichiometry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Example: Binary Substitution\n", + "\n", + "Let's start with a simple example: a 4-site system where we substitute with two species (A and B)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created structure with 4 sites\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from pymatgen.core import Structure, Lattice\n", + "from bsym.interface.pymatgen import unique_structure_substitutions_by_composition\n", + "\n", + "# Create a simple 2×2 square lattice\n", + "coords = np.array([[0.0, 0.0, 0.0]])\n", + "atom_list = ['Li']\n", + "lattice = Lattice.from_parameters(a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90)\n", + "parent_structure = Structure(lattice, atom_list, coords) * [2, 2, 1]\n", + "\n", + "print(f\"Created structure with {len(parent_structure)} sites\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we'll generate all unique structures for substituting with species A and B:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generated structures for 5 different compositions:\n", + " Composition (0, 4): 1 unique structure(s)\n", + " Composition (4, 0): 1 unique structure(s)\n", + " Composition (1, 3): 1 unique structure(s)\n", + " Composition (3, 1): 1 unique structure(s)\n", + " Composition (2, 2): 2 unique structure(s)\n" + ] + } + ], + "source": [ + "results = unique_structure_substitutions_by_composition(\n", + " parent_structure,\n", + " 'Li',\n", + " ['A', 'B'] # List of species (order matters for composition tuples)\n", + ")\n", + "\n", + "print(f\"Generated structures for {len(results)} different compositions:\")\n", + "for composition, structures in results.items():\n", + " print(f\" Composition {composition}: {len(structures)} unique structure(s)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Understanding the Output Format\n", + "\n", + "The function returns a dictionary where:\n", + "- **Keys** are composition tuples: `(n_A, n_B)` representing the count of each species\n", + "- **Values** are lists of Structure objects\n", + "\n", + "The order of species in the tuple matches the order in your species list.\n", + "\n", + "Let's examine a specific composition:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Composition (2A, 2B) has 2 unique structure(s)\n", + "\n", + "Structure 0:\n", + "Full Formula (A2 B2)\n", + "Reduced Formula: AB\n", + "abc : 2.000000 2.000000 1.000000\n", + "angles: 90.000000 90.000000 90.000000\n", + "pbc : True True True\n", + "Sites (4)\n", + " # SP a b c\n", + "--- ---- --- --- ---\n", + " 0 A0+ 0 0 0\n", + " 1 A0+ 0 0.5 0\n", + " 2 B 0.5 0 0\n", + " 3 B 0.5 0.5 0\n", + "\n", + "Degeneracy: 4\n" + ] + } + ], + "source": [ + "# Access structures with 2 A atoms and 2 B atoms\n", + "composition_2_2 = results[(2, 2)]\n", + "\n", + "print(f\"Composition (2A, 2B) has {len(composition_2_2)} unique structure(s)\")\n", + "print(f\"\\nStructure 0:\")\n", + "print(composition_2_2[0])\n", + "print(f\"\\nDegeneracy: {composition_2_2[0].number_of_equivalent_configurations}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example: Li-Na Binary System\n", + "\n", + "Let's look at a more realistic example with actual elements on a larger lattice:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Created structure with 9 sites\n" + ] + } + ], + "source": [ + "# Create a 3×3 lattice\n", + "coords = np.array([[0.0, 0.0, 0.0]])\n", + "atom_list = ['X'] # Placeholder to substitute\n", + "lattice = Lattice.from_parameters(a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90)\n", + "parent_structure = Structure(lattice, atom_list, coords) * [3, 3, 1]\n", + "\n", + "print(f\"Created structure with {len(parent_structure)} sites\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of compositions explored: 10\n", + "\n", + "Composition summary:\n", + " Li0Na9: 1 unique, 1 total configurations\n", + " Li1Na8: 1 unique, 9 total configurations\n", + " Li2Na7: 2 unique, 36 total configurations\n", + " Li3Na6: 4 unique, 84 total configurations\n", + " Li4Na5: 5 unique, 126 total configurations\n", + " Li5Na4: 5 unique, 126 total configurations\n", + " Li6Na3: 4 unique, 84 total configurations\n", + " Li7Na2: 2 unique, 36 total configurations\n", + " Li8Na1: 1 unique, 9 total configurations\n", + " Li9Na0: 1 unique, 1 total configurations\n" + ] + } + ], + "source": [ + "# Generate Li-Na structures across all compositions\n", + "li_na_results = unique_structure_substitutions_by_composition(\n", + " parent_structure,\n", + " 'X',\n", + " ['Li', 'Na']\n", + ")\n", + "\n", + "print(f\"Number of compositions explored: {len(li_na_results)}\")\n", + "print(\"\\nComposition summary:\")\n", + "for composition, structures in sorted(li_na_results.items()):\n", + " n_li, n_na = composition\n", + " total_configs = sum(s.number_of_equivalent_configurations for s in structures)\n", + " print(f\" Li{n_li}Na{n_na}: {len(structures)} unique, {total_configs} total configurations\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraining Compositions with Bounds\n", + "\n", + "Often you don't want to explore all possible compositions. The `bounds` parameter lets you constrain the range:\n", + "\n", + "**Format:** `{'species_name': (min_count, max_count)}`\n", + "\n", + "For example, to explore only lithium-rich compositions:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Li-rich compositions (6+ Li):\n", + " Li6Na3: 4 unique structures\n", + " Li7Na2: 2 unique structures\n", + " Li8Na1: 1 unique structures\n", + " Li9Na0: 1 unique structures\n" + ] + } + ], + "source": [ + "# Only compositions with at least 6 Li atoms\n", + "li_rich_results = unique_structure_substitutions_by_composition(\n", + " parent_structure,\n", + " 'X',\n", + " ['Li', 'Na'],\n", + " bounds={'Li': (6, 9)} # 6-9 Li atoms\n", + ")\n", + "\n", + "print(f\"Li-rich compositions (6+ Li):\")\n", + "for composition in sorted(li_rich_results.keys()):\n", + " n_li, n_na = composition\n", + " print(f\" Li{n_li}Na{n_na}: {len(li_rich_results[composition])} unique structures\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can constrain multiple species:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Constrained compositions:\n", + " Li5Na4: 5 unique structures\n", + " Li6Na3: 4 unique structures\n" + ] + } + ], + "source": [ + "# Compositions with 3-6 Li and 1-4 Na\n", + "constrained_results = unique_structure_substitutions_by_composition(\n", + " parent_structure,\n", + " 'X',\n", + " ['Li', 'Na'],\n", + " bounds={'Li': (3, 6), 'Na': (1, 4)}\n", + ")\n", + "\n", + "print(f\"Constrained compositions:\")\n", + "for composition in sorted(constrained_results.keys()):\n", + " n_li, n_na = composition\n", + " print(f\" Li{n_li}Na{n_na}: {len(constrained_results[composition])} unique structures\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Three-Species Example\n", + "\n", + "The function works with any number of species. Here's an example with three:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generated 12 compositions\n", + "\n", + "Example compositions:\n", + " Li0Na2K2: 2 unique structures\n", + " Li0Na3K1: 1 unique structures\n", + " Li0Na4K0: 1 unique structures\n", + " Li1Na1K2: 2 unique structures\n", + " Li1Na2K1: 2 unique structures\n" + ] + } + ], + "source": [ + "# Create a smaller structure for 3-species exploration\n", + "small_structure = Structure(lattice, ['X'], [[0.0, 0.0, 0.0]]) * [2, 2, 1]\n", + "\n", + "# Li-Na-K ternary system\n", + "ternary_results = unique_structure_substitutions_by_composition(\n", + " small_structure,\n", + " 'X',\n", + " ['Li', 'Na', 'K'],\n", + " bounds={'Li': (0, 4), 'Na': (0, 4), 'K': (0, 2)} # Limit K content\n", + ")\n", + "\n", + "print(f\"Generated {len(ternary_results)} compositions\")\n", + "print(\"\\nExample compositions:\")\n", + "for i, (composition, structures) in enumerate(sorted(ternary_results.items())[:5]):\n", + " n_li, n_na, n_k = composition\n", + " print(f\" Li{n_li}Na{n_na}K{n_k}: {len(structures)} unique structures\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using Progress Bars\n", + "\n", + "When running from a terminal or Python script, you can enable progress bars to monitor the computation:\n", + "```python\n", + "# Create a 5×5 lattice\n", + "coords = np.array([[0.0, 0.0, 0.0]])\n", + "atom_list = ['X'] # Placeholder to substitute\n", + "lattice = Lattice.from_parameters(a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90)\n", + "parent_structure = Structure(lattice, atom_list, coords) * [5, 5, 1]\n", + "\n", + "# Enable progress bars\n", + "results_with_progress = unique_structure_substitutions_by_composition(\n", + " parent_structure,\n", + " 'X',\n", + " ['Li', 'Na'],\n", + " show_progress=True,\n", + " verbose=True\n", + ")\n", + "```\n", + "\n", + "This produces terminal output showing progress for each composition:\n", + "```\n", + "100%|████████████████████████| 1/1 [00:00<00:00, 1971.93 permutations/s, found=1]\n", + "100%|█████████████████████| 25/25 [00:00<00:00, 50291.41 permutations/s, found=1]\n", + "100%|██████████████████| 300/300 [00:00<00:00, 115196.48 permutations/s, found=5]\n", + "100%|███████████████| 2300/2300 [00:00<00:00, 197986.64 permutations/s, found=19]\n", + "100%|█████████████| 12650/12650 [00:00<00:00, 218314.01 permutations/s, found=88]\n", + "100%|████████████| 53130/53130 [00:00<00:00, 240692.40 permutations/s, found=309]\n", + "100%|██████████| 177100/177100 [00:00<00:00, 246178.45 permutations/s, found=975]\n", + "...\n", + "```\n", + "\n", + "Each progress bar corresponds to one composition being processed, showing the number of permutations evaluated and unique configurations found.\n", + "\n", + "**Note:** For Jupyter notebooks, use `show_progress='notebook'` to display interactive progress widgets instead of ASCII bars." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exporting Structures\n", + "\n", + "You might want to write structures to files, organised by composition:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Structures can be exported using structure.to(filename='...', fmt='cif')\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "# Example: save structures for each composition\n", + "# (Commented out to avoid creating files in documentation)\n", + "\n", + "# output_dir = 'li_na_structures'\n", + "# os.makedirs(output_dir, exist_ok=True)\n", + "\n", + "# for composition, structures in li_na_results.items():\n", + "# n_li, n_na = composition\n", + "# comp_dir = os.path.join(output_dir, f'Li{n_li}Na{n_na}')\n", + "# os.makedirs(comp_dir, exist_ok=True)\n", + "# \n", + "# for i, structure in enumerate(structures):\n", + "# filename = os.path.join(comp_dir, f'structure_{i}.cif')\n", + "# structure.to(filename=filename, fmt='cif')\n", + "\n", + "print(\"Structures can be exported using structure.to(filename='...', fmt='cif')\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Key Points\n", + "\n", + "- **`unique_structure_substitutions_by_composition`** explores multiple compositions in one call\n", + "- Returns a **dictionary** with composition tuples as keys\n", + "- Composition tuple order matches the species list order\n", + "- Use **`bounds`** to constrain composition ranges\n", + "- Each structure retains its `number_of_equivalent_configurations` attribute\n", + "- Efficient for surveying composition space and building phase diagrams\n", + "\n", + "## When to Use This vs Fixed Composition\n", + "\n", + "**Use `unique_structure_substitutions_by_composition` when:**\n", + "- You want to explore multiple compositions\n", + "- Building a phase diagram or composition-property map\n", + "- You don't know the optimal composition in advance\n", + "\n", + "**Use `unique_structure_substitutions` when:**\n", + "- You know the exact composition you need\n", + "- Performing sequential substitutions with intermediate analysis\n", + "- You need to track `full_configuration_degeneracy` through multiple steps\n", + "\n", + "## Next Steps\n", + "\n", + "- For single-composition substitutions, see [Basic Substitutions](basic_substitutions.ipynb)\n", + "- For sequential substitutions, see [Fixed Composition Substitutions](fixed_composition.ipynb)\n", + "- To understand the composition enumeration algorithm, see [Composition Enumeration Theory](../theory/composition_enumeration.md)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index fa65608..0000000 --- a/examples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.ipynb diff --git a/examples/bsym_examples.ipynb b/examples/bsym_examples.ipynb deleted file mode 100644 index c56f52b..0000000 --- a/examples/bsym_examples.ipynb +++ /dev/null @@ -1,1673 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# `bsym` – a basic symmetry module\n", - "\n", - "`bsym` is a basic Python symmetry module. It consists of some core classes that describe configuration vector spaces, their symmetry operations, and specific configurations of objects withing these spaces. The module also contains an interface for working with [`pymatgen`](http://pymatgen.org) `Structure` objects, to allow simple generation of disordered symmetry-inequivalent structures from a symmetric parent crystal structure.\n", - "\n", - "API documentation is [here](http://bsym.readthedocs.io)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration Spaces, Symmetry Operations, and Groups\n", - "\n", - "The central object described by `bsym` is the **configuration space**. This defines a vector space that can be occupied by other objects. For example; the three points $a, b, c$ defined by an equilateral triangle,\n", - "\n", - "\n", - "\n", - "which can be described by a length 3 vector:\n", - "\n", - "\\begin{pmatrix}a\\\\b\\\\c\\end{pmatrix}\n", - "\n", - "If these points can be coloured black or white, then we can define a **configuration** for each different colouring (0 for white, 1 for black), e.g. \n", - "\n", - "\n", - "\n", - "with the corresponding vector\n", - "\n", - "\\begin{pmatrix}1\\\\1\\\\0\\end{pmatrix}\n", - "\n", - "A specific **configuration** therefore defines how objects are distributed within a particular **configuration space**.\n", - "\n", - "The symmetry relationships between the different vectors in a **configuration space** are described by **symmetry operations**. A **symmetry operation** describes a transformation of a **configuration space** that leaves it indistinguishable. Each **symmetry operation** can be describes as a matrix that maps the vectors in a **configuration space** onto each other, e.g. in the case of the equiateral triangle the simplest **symmetry operation** is the identity, $E$, which leaves every corner unchanged, and can be represented by the matrix \n", - "\n", - "\\begin{equation}\n", - "E=\\begin{pmatrix}1 & 0 & 0\\\\0 & 1 & 0 \\\\ 0 & 0 & 1\\end{pmatrix}\n", - "\\end{equation}\n", - "\n", - "For this triangular example, there are other **symmetry operations**, including reflections, $\\sigma$ and rotations, $C_n$:\n", - "\n", - "\n", - "\n", - "In this example reflection operation, $b$ is mapped to $c$; $b\\to c$, and $c$ is mapped to $b$; $b\\to c$. \n", - "\n", - "The matrix representation of this **symmetry operation** is\n", - "\n", - "\\begin{equation}\n", - "\\sigma_\\mathrm{a}=\\begin{pmatrix}1 & 0 & 0\\\\0 & 0 & 1 \\\\ 0 & 1 & 0\\end{pmatrix}\n", - "\\end{equation}\n", - "\n", - "For the example rotation operation, $a\\to b$, $b\\to c$, and $c\\to a$, with matrix representation\n", - "\n", - "\\begin{equation}\n", - "C_3=\\begin{pmatrix}0 & 0 & 1\\\\ 1 & 0 & 0 \\\\ 0 & 1 & 0\\end{pmatrix}\n", - "\\end{equation}\n", - "\n", - "Using this matrix and vector notation, the effect of a symmetry operation on a specific **configuration** can be calculated as the [matrix product](https://en.wikipedia.org/wiki/Matrix_multiplication#Square_matrix_and_column_vector) of the **symmetry operation** matrix and the **configuration** vector:\n", - "\n", - "\n", - "\n", - "In matrix notation this is represented as\n", - "\n", - "\\begin{equation}\n", - "\\begin{pmatrix}0\\\\1\\\\1\\end{pmatrix} = \\begin{pmatrix}0 & 0 & 1\\\\ 1 & 0 & 0 \\\\ 0 & 1 & \n", - "0\\end{pmatrix}\\begin{pmatrix}1\\\\1\\\\0\\end{pmatrix}\n", - "\\end{equation}\n", - "\n", - "or more compactly\n", - "\n", - "\\begin{equation}\n", - "c_\\mathrm{f} = C_3 c_\\mathrm{i}.\n", - "\\end{equation}\n", - "\n", - "The set of all symmetry operations for a particular **configuration space** is a **group**. \n", - "\n", - "For an equilateral triangle this group is the $C_{3v}$ [point group](https://en.wikipedia.org/wiki/Point_group), which contains six symmetry operations: the identity, three reflections (each with a mirror plane bisecting the triangle and passing through $a$, $b$, or $c$ respectively) and two rotations (120° clockwise and counterclockwise).\n", - "\n", - "\\begin{equation}\n", - "C_{3v} = \\left\\{ E, \\sigma_\\mathrm{a}, \\sigma_\\mathrm{b}, \\sigma_\\mathrm{c}, C_3, C_3^\\prime \\right\\}\n", - "\\end{equation}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Modelling this using `bsym`\n", - "\n", - "### The `SymmetryOperation` class\n", - "\n", - "In `bsym`, a **symmetry operation** is represented by an instance of the `SymmetryOperation` class. A `SymmetryOperation` instance can be initialised from the matrix representation of the corresponding **symmetry operation**. \n", - "\n", - "For example, in the trigonal **configuration space** above, a `SymmetryOperation` describing the identify, $E$, can be created with" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from bsym import SymmetryOperation" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(---)\n", - "array([[1, 0, 0],\n", - " [0, 1, 0],\n", - " [0, 0, 1]])" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "SymmetryOperation([[ 1, 0, 0 ], \n", - " [ 0, 1, 0 ], \n", - " [ 0, 0, 1 ]])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each `SymmetryOperation` has an optional `label` attribute. This can be set at records the matrix representation of the **symmetry operation** and an optional label. We can provide the label when creating a `SymmetryOperation`:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(E)\n", - "array([[1, 0, 0],\n", - " [0, 1, 0],\n", - " [0, 0, 1]])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "SymmetryOperation([[ 1, 0, 0 ], \n", - " [ 0, 1, 0 ], \n", - " [ 0, 0, 1 ]], label='E' )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "or set it afterwards:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(E)\n", - "array([[1, 0, 0],\n", - " [0, 1, 0],\n", - " [0, 0, 1]])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "e = SymmetryOperation([[ 1, 0, 0 ], \n", - " [ 0, 1, 0 ], \n", - " [ 0, 0, 1 ]])\n", - "e.label = 'E'\n", - "e" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or for $C_3$:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(C3)\n", - "array([[0, 0, 1],\n", - " [1, 0, 0],\n", - " [0, 1, 0]])" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3 = SymmetryOperation( [ [ 0, 0, 1 ],\n", - " [ 1, 0, 0 ],\n", - " [ 0, 1, 0 ] ], label='C3' )\n", - "c_3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Vector representations of symmetry operations\n", - "\n", - "The matrix representation of a **symmetry operation** is a [permutation matrix](https://en.wikipedia.org/wiki/Permutation_matrix). Each row maps one position in the corresponding **configuration space** to one other position. An alternative, condensed, representation for each **symmetry operation** matrix uses vector notation, where each element gives the row containing `1` in the equivalent matrix column. e.g. for $C_3$ the vector mapping is given by $\\left[2,3,1\\right]$, corresponding to the mapping $1\\to2$, $2\\to3$, $3\\to1$." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(C3)\n", - "array([[0., 0., 1.],\n", - " [1., 0., 0.],\n", - " [0., 1., 0.]])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3_from_vector = SymmetryOperation.from_vector( [ 2, 3, 1 ], label='C3' )\n", - "c_3_from_vector" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The vector representation of a `SymmetryOperation` can be accessed using the `as_vector()` method." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[2, 3, 1]" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3.as_vector()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Inverting symmetry operations\n", - "\n", - "For every **symmetry operation**, $A$, there is an **inverse** operation, $A^{-1}$, such that \n", - "\n", - "\\begin{equation}\n", - "A \\cdot A^{-1}=E.\n", - "\\end{equation}\n", - "\n", - "For example, the inverse of $C_3$ (clockwise rotation by 120°) is $C_3^\\prime$ (anticlockwise rotation by 120°):" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SymmetryOperation\n", - "label(C3)\n", - "array([[0., 0., 1.],\n", - " [1., 0., 0.],\n", - " [0., 1., 0.]]) \n", - "\n", - "SymmetryOperation\n", - "label(C3_inv)\n", - "array([[0., 1., 0.],\n", - " [0., 0., 1.],\n", - " [1., 0., 0.]]) \n", - "\n" - ] - } - ], - "source": [ - "c_3 = SymmetryOperation.from_vector( [ 2, 3, 1 ], label='C3' )\n", - "c_3_inv = SymmetryOperation.from_vector( [ 3, 1, 2 ], label='C3_inv' )\n", - "\n", - "print( c_3, '\\n' )\n", - "print( c_3_inv, '\\n' )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The product of $C_3$ and $C_3^\\prime$ is the identity, $E$." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(---)\n", - "array([[1., 0., 0.],\n", - " [0., 1., 0.],\n", - " [0., 0., 1.]])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3 * c_3_inv" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`c_3_inv` can also be generated using the `.invert()` method" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(---)\n", - "array([[0, 1, 0],\n", - " [0, 0, 1],\n", - " [1, 0, 0]])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3.invert()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The resulting `SymmetryOperation` does not have a label defined. This can be set directly, or by chaining the `.set_label()` method, e.g." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(C3_inv)\n", - "array([[0, 1, 0],\n", - " [0, 0, 1],\n", - " [1, 0, 0]])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3.invert( label= 'C3_inv')" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryOperation\n", - "label(C3_inv)\n", - "array([[0, 1, 0],\n", - " [0, 0, 1],\n", - " [1, 0, 0]])" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3.invert().set_label( 'C3_inv' )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `SymmetryGroup` class\n", - "\n", - "A `SymmetryGroup` is a collections of `SymmetryOperation` objects. A `SymmetryGroup` is not required to contain _all_ the symmetry operations of a particular **configuration space**, and therefore is not necessarily a complete mathematical group.\n", - "\n", - "For convenience `bsym` has `PointGroup` and `SpaceGroup` classes, that are equivalent to the `SymmetryGroup` parent class." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "from bsym import PointGroup" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "# construct SymmetryOperations for C_3v group\n", - "e = SymmetryOperation.from_vector( [ 1, 2, 3 ], label='e' )\n", - "c_3 = SymmetryOperation.from_vector( [ 2, 3, 1 ], label='C_3' )\n", - "c_3_inv = SymmetryOperation.from_vector( [ 3, 1, 2 ], label='C_3_inv' )\n", - "sigma_a = SymmetryOperation.from_vector( [ 1, 3, 2 ], label='S_a' )\n", - "sigma_b = SymmetryOperation.from_vector( [ 3, 2, 1 ], label='S_b' )\n", - "sigma_c = SymmetryOperation.from_vector( [ 2, 1, 3 ], label='S_c' )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "c3v = PointGroup( [ e, c_3, c_3_inv, sigma_a, sigma_b, sigma_c ] )" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "PointGroup\n", - "e\t[1, 2, 3]\n", - "C_3\t[2, 3, 1]\n", - "C_3_inv\t[3, 1, 2]\n", - "S_a\t[1, 3, 2]\n", - "S_b\t[3, 2, 1]\n", - "S_c\t[2, 1, 3]" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c3v" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `ConfigurationSpace` class\n", - "\n", - "A `ConfigurationSpace` consists of a set of objects that represent the **configuration space** vectors, and the `SymmetryGroup` containing the relevant **symmetry operations**." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "from bsym import ConfigurationSpace" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "c = ConfigurationSpace( objects=['a', 'b', 'c' ], symmetry_group=c3v )" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigurationSpace\n", - "['a', 'b', 'c']\n", - "e\t[1, 2, 3]\n", - "C_3\t[2, 3, 1]\n", - "C_3_inv\t[3, 1, 2]\n", - "S_a\t[1, 3, 2]\n", - "S_b\t[3, 2, 1]\n", - "S_c\t[2, 1, 3]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `Configuration` class\n", - "\n", - "A `Configuration` instance describes a particular **configuration**, i.e. how a set of objects are arranged within a **configuration space**. Internally, a `Configuration` is represented as a vector (as a `numpy` array).\n", - "Each element in a configuration is represented by a single digit non-negative integer." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Configuration([1 1 0])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from bsym import Configuration\n", - "\n", - "conf_1 = Configuration( [ 1, 1, 0 ] )\n", - "conf_1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The effect of a particular **symmetry operation** acting on a **configuration** can now be calculated using the `SymmetryOperation.operate_on()` method, or by direct multiplication, e.g." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Configuration([0 1 1])" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c1 = Configuration( [ 1, 1, 0 ] )\n", - "c_3 = SymmetryOperation.from_vector( [ 2, 3, 1 ] )\n", - "c_3.operate_on( c1 )" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Configuration([0 1 1])" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c_3 * conf_1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Finding symmetry-inequivalent permutations." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A common question that comes up when considering the symmetry properties of arrangements of objects is: how many ways can these be arranged that are not equivalent by symmetry?\n", - "\n", - "As a simple example of solving this problem using `bsym` consider four equivalent sites arranged in a square.\n", - "\n", - "" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "c = ConfigurationSpace( [ 'a', 'b', 'c', 'd' ] ) # four vector configuration space" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This `ConfigurationSpace` has been created without a `symmetry_group` argument. The default behaviour in this case is to create a `SymmetryGroup` containing only the identity, $E$." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigurationSpace\n", - "['a', 'b', 'c', 'd']\n", - "E\t[1, 2, 3, 4]" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now calculate all symmetry inequivalent arrangements where two sites are occupied and two are unoccupied, using the `unique_configurations()` method. This takes as a argument a `dict` with the numbers of labels to be arranged in the **configuration space**. Here, we use the labels `1` and `0` to represent occupied and unoccupied sites, respectively, and the distribution of sites is given by `{ 1:2, 0:2 }`." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Configuration([0 0 1 1]),\n", - " Configuration([0 1 0 1]),\n", - " Configuration([0 1 1 0]),\n", - " Configuration([1 0 0 1]),\n", - " Configuration([1 0 1 0]),\n", - " Configuration([1 1 0 0])]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c.unique_configurations( {1:2, 0:2} )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because we have not yet taken into account the symmetry of the **configuration space**, we get\n", - "\n", - "\\begin{equation}\n", - "\\frac{4\\times3}{2}\n", - "\\end{equation}\n", - "\n", - "unique configurations (where the factor of 2 comes from the occupied sites being indistinguishable).\n", - "\n", - "The configurations generated by `unique_configurations` have a `count` attribute that records the number of *symmetry equivalent* configurations of each case:\n", - "\n", - "In this example, each configuration appears once:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 1, 1, 1, 1, 1]" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ uc.count for uc in c.unique_configurations( {1:2, 0:2} ) ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also calculate the result when all symmetry operations of this **configuration space** are included. " - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigurationSpace\n", - "['a', 'b', 'c', 'd']\n", - "E\t[1, 2, 3, 4]\n", - "C4\t[2, 3, 4, 1]\n", - "C4i\t[4, 1, 2, 3]\n", - "C2\t[3, 4, 1, 2]\n", - "s_x\t[4, 3, 2, 1]\n", - "s_y\t[2, 1, 4, 3]\n", - "s_ac\t[1, 4, 3, 2]\n", - "s_bd\t[3, 2, 1, 4]" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# construct point group\n", - "e = SymmetryOperation.from_vector( [ 1, 2, 3, 4 ], label='E' )\n", - "c4 = SymmetryOperation.from_vector( [ 2, 3, 4, 1 ], label='C4' )\n", - "c4_inv = SymmetryOperation.from_vector( [ 4, 1, 2, 3 ], label='C4i' )\n", - "c2 = SymmetryOperation.from_vector( [ 3, 4, 1, 2 ], label='C2' )\n", - "sigma_x = SymmetryOperation.from_vector( [ 4, 3, 2, 1 ], label='s_x' )\n", - "sigma_y = SymmetryOperation.from_vector( [ 2, 1, 4, 3 ], label='s_y' )\n", - "sigma_ac = SymmetryOperation.from_vector( [ 1, 4, 3, 2 ], label='s_ac' )\n", - "sigma_bd = SymmetryOperation.from_vector( [ 3, 2, 1, 4 ], label='s_bd' )\n", - "c4v = PointGroup( [ e, c4, c4_inv, c2, sigma_x, sigma_y, sigma_ac, sigma_bd ] )\n", - "\n", - "# create ConfigurationSpace with the c4v PointGroup.\n", - "c = ConfigurationSpace( [ 'a', 'b', 'c', 'd' ], symmetry_group=c4v )\n", - "c" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Configuration([0 0 1 1]), Configuration([0 1 0 1])]" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c.unique_configurations( {1:2, 0:2} )" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[4, 2]" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ uc.count for uc in c.unique_configurations( {1:2, 0:2 } ) ]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "Taking symmetry in to account, we now only have two unique configurations: either two adjacent site are occupied (four possible ways), or two diagonal sites are occupied (two possible ways):\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `unique_configurations()` method can also handle non-binary site occupations:" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Configuration([0 0 1 2]), Configuration([0 1 0 2])]" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c.unique_configurations( {2:1, 1:1, 0:2} )" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[8, 4]" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ uc.count for uc in c.unique_configurations( {2:1, 1:1, 0:2 } ) ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "## Working with crystal structures using `pymatgen`\n", - "\n", - "One example where the it can be useful to identify symmetry-inequivalent arrangements of objects in a vector space, is when considering the possible arrangements of disordered atoms on a crystal lattice. \n", - "\n", - "To solve this problem for an arbitrary crystal structure, `bsym` contains an interface to [`pymatgen`](http://pymatgen.org) that will identify symmetry-inequivalent atom substitutions in a given `pymatgen` `Structure`.\n", - "\n", - "As an example, consider a $4\\times4$ square-lattice supercell populated by lithium atoms." - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [], - "source": [ - "from pymatgen.core.lattice import Lattice\n", - "from pymatgen.core.structure import Structure\n", - "import numpy as np" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0., 0., 0.],\n", - " [-0., 1., 0.],\n", - " [-0., 2., 0.],\n", - " [-0., 3., 0.],\n", - " [ 1., 0., 0.],\n", - " [ 1., 1., 0.],\n", - " [ 1., 2., 0.],\n", - " [ 1., 3., 0.],\n", - " [ 2., 0., 0.],\n", - " [ 2., 1., 0.],\n", - " [ 2., 2., 0.],\n", - " [ 2., 3., 0.],\n", - " [ 3., 0., 0.],\n", - " [ 3., 1., 0.],\n", - " [ 3., 2., 0.],\n", - " [ 3., 3., 0.]])" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# construct a pymatgen Structure instance using the site fractional coordinates\n", - "coords = np.array( [ [ 0.0, 0.0, 0.0 ] ] )\n", - "atom_list = [ 'Li' ]\n", - "lattice = Lattice.from_parameters( a=1.0, b=1.0, c=1.0, alpha=90, beta=90, gamma=90 )\n", - "parent_structure = Structure( lattice, atom_list, coords ) * [ 4, 4, 1 ]\n", - "parent_structure.cart_coords.round(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the `bsym.interface.pymatgen.unique_structure_substitutions()` function to identify symmetry-inequivalent structures generated by substituting at different sites." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [], - "source": [ - "from bsym.interface.pymatgen import unique_structure_substitutions" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " Generate all symmetry-unique structures formed by substituting a set of sites in a `pymatgen` structure.\n", - "\n", - " Args:\n", - " structure (pymatgen.Structure): The parent structure.\n", - " to_substitute (str): atom label for the sites to be substituted.\n", - " site_distribution (dict): A dictionary that defines the number of each substituting element.\n", - " verbose (bool): verbose output.\n", - " atol (Optional [float]): tolerance factor for the ``pymatgen`` `coordinate mapping`_ under each symmetry operation. Default=1e-5.\n", - " show_progress (opt:default=False): Show a progress bar.\n", - " Setting to `True` gives a simple progress bar.\n", - " Setting to `\"notebook\"` gives a Jupyter notebook compatible progress bar.\n", - "\n", - "\n", - " Returns:\n", - " (list[Structure]): A list of Structure objects for each unique substitution.\n", - " \n", - " Notes:\n", - " The number of symmetry-equivalent configurations for each structure \n", - " is stored in the `number_of_equivalent_configurations` attribute. \n", - " \n", - " If the parent structure was previously generated using this function\n", - " (as part of a sequence of substitutions) the full configuration\n", - " degeneracy of each symmetry inequivalent configuration is stored in\n", - " the `full_configuration_degeneracy` attribute. If the parent structure\n", - " is a standard Pymatgen Structure object, `number_of_equivalent_configurations`\n", - " and `full_configuration_degeneracy` will be equal.\n", - "\n", - " .. _coordinate mapping:\n", - " http://pymatgen.org/pymatgen.util.coord_utils.html#pymatgen.util.coord_utils.coord_list_mapping_pbc\n", - "\n", - " \n" - ] - } - ], - "source": [ - "print( unique_structure_substitutions.__doc__ )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a trivial example, when substituting one Li atom for Na, we get a single unique structure" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "unique_structures = unique_structure_substitutions( parent_structure, 'Li', { 'Na':1, 'Li':15 } )\n", - "len( unique_structures )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [], - "source": [ - "na_substituted = unique_structures[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This Li$\\to$Na substitution breaks the symmetry of the $4\\times4$ supercell. \n", - "\n", - "If we now replace a second lithium with a magnesium atom, we generate five symmetry inequivalent structures:" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "unique_structures_with_Mg = unique_structure_substitutions( na_substituted, 'Li', { 'Mg':1, 'Li':14 } )\n", - "len( unique_structures_with_Mg )" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[4, 2, 4, 4, 1]" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ s.number_of_equivalent_configurations for s in unique_structures_with_Mg ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`number_of_equivalent_configurations` only lists the number of equivalent configurations found when performing the second substitution, when the list of structures `unique_structures_with_Mg` was created. The full configuration degeneracy relative to the initial empty 4×4 lattice can be queried using `full_configuration_degeneracy`." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[64, 32, 64, 64, 16]" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ s.full_configuration_degeneracy for s in unique_structures_with_Mg ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 2., 4., 5., 8.])" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Check the squared distances between the Na and Mg sites in these unique structures are [1, 2, 4, 5, 8]\n", - "np.array( sorted( [ s.get_distance( s.indices_from_symbol('Na')[0], \n", - " s.indices_from_symbol('Mg')[0] )**2 for s in unique_structures_with_Mg ] ) )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This double substitution can also be done in a single step:" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "unique_structures = unique_structure_substitutions( parent_structure, 'Li', { 'Mg':1, 'Na':1, 'Li':14 } )" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "len(unique_structures)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1., 2., 4., 5., 8.])" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.array( sorted( [ s.get_distance( s.indices_from_symbol('Na')[0], \n", - " s.indices_from_symbol('Mg')[0] ) for s in unique_structures ] ) )**2" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[64, 32, 64, 64, 16]" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ s.number_of_equivalent_configurations for s in unique_structures ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because both substitutions were performed in a single step, `number_of_equivalent_configurations` and `full_configuration_degeneracy` now contain the same data:" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[64, 32, 64, 64, 16]" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "[ s.full_configuration_degeneracy for s in unique_structures ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Constructing `SpaceGroup` and `ConfigurationSpace` objects using `pymatgen`\n", - "\n", - "The `bsym.interface.pymatgen` module contains functions for generating `SpaceGroup` and `ConfigurationSpace` objects directly from `pymatgen` `Structure` objects." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [], - "source": [ - "from bsym.interface.pymatgen import ( space_group_symbol_from_structure, \n", - " space_group_from_structure, \n", - " configuration_space_from_structure )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Documentation:\n", - "\n", - "- [`space_group_symbol_from_structure`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.space_group_symbol_from_structure)\n", - "- [`space_group_from_structure`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.space_group_from_structure)\n", - "- [`configuration_space_from_structure`](http://bsym.readthedocs.io/en/latest/api/interface/pymatgen.html#bsym.interface.pymatgen.configuration_space_from_structure)" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "coords = np.array( [ [ 0.0, 0.0, 0.0 ],\n", - " [ 0.5, 0.5, 0.0 ],\n", - " [ 0.0, 0.5, 0.5 ],\n", - " [ 0.5, 0.0, 0.5 ] ] )\n", - "atom_list = [ 'Li' ] * len( coords )\n", - "lattice = Lattice.from_parameters( a=3.0, b=3.0, c=3.0, alpha=90, beta=90, gamma=90 )\n", - "structure = Structure( lattice, atom_list, coords )" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Fm-3m'" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "space_group_symbol_from_structure( structure )" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymmetryGroup\n", - "None\t[1, 2, 3, 4]\n", - "None\t[3, 4, 1, 2]\n", - "None\t[1, 4, 3, 2]\n", - "None\t[2, 3, 4, 1]\n", - "None\t[4, 3, 2, 1]\n", - "None\t[3, 2, 1, 4]\n", - "None\t[2, 1, 4, 3]\n", - "None\t[4, 1, 2, 3]\n", - "None\t[4, 1, 3, 2]\n", - "None\t[3, 2, 4, 1]\n", - "None\t[2, 4, 1, 3]\n", - "None\t[1, 3, 2, 4]\n", - "None\t[1, 4, 2, 3]\n", - "None\t[3, 1, 4, 2]\n", - "None\t[2, 3, 1, 4]\n", - "None\t[4, 2, 3, 1]\n", - "None\t[3, 1, 2, 4]\n", - "None\t[3, 4, 2, 1]\n", - "None\t[2, 4, 3, 1]\n", - "None\t[2, 1, 3, 4]\n", - "None\t[1, 3, 4, 2]\n", - "None\t[1, 2, 4, 3]\n", - "None\t[4, 2, 1, 3]\n", - "None\t[4, 3, 1, 2]" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "space_group_from_structure( structure )" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigurationSpace\n", - "[1, 2, 3, 4]\n", - "None\t[1, 2, 3, 4]\n", - "None\t[3, 4, 1, 2]\n", - "None\t[1, 4, 3, 2]\n", - "None\t[2, 3, 4, 1]\n", - "None\t[4, 3, 2, 1]\n", - "None\t[3, 2, 1, 4]\n", - "None\t[2, 1, 4, 3]\n", - "None\t[4, 1, 2, 3]\n", - "None\t[4, 1, 3, 2]\n", - "None\t[3, 2, 4, 1]\n", - "None\t[2, 4, 1, 3]\n", - "None\t[1, 3, 2, 4]\n", - "None\t[1, 4, 2, 3]\n", - "None\t[3, 1, 4, 2]\n", - "None\t[2, 3, 1, 4]\n", - "None\t[4, 2, 3, 1]\n", - "None\t[3, 1, 2, 4]\n", - "None\t[3, 4, 2, 1]\n", - "None\t[2, 4, 3, 1]\n", - "None\t[2, 1, 3, 4]\n", - "None\t[1, 3, 4, 2]\n", - "None\t[1, 2, 4, 3]\n", - "None\t[4, 2, 1, 3]\n", - "None\t[4, 3, 1, 2]" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "configuration_space_from_structure( structure )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Progress bars\n", - "\n", - "`bsym.ConfigurationSpace.unique_configurations()` and `bsym.interface.pymatgen.unique_structure_substitutions()` both accept optional `show_progress` arguments, which can be used to display progress bars (using `tqdm`(https://tqdm.github.io). \n", - "\n", - "Setting `show_progress=True` will give a simple progress bar. If you are running `bsym` in a Jupyter notebook, setting `show_progress=\"notebook\"` will give you a progress bar as a notebook widget.\n", - "\n", - "(note, the widget status is not saved with this notebook, and may not display correctly on GitHub or using nbviewer)\n", - "\n", - "In the example below, we find all unique configurations for the pseudo-ReO3 structured TiOF2 in a 2×2×2 supercell." - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9dc42b56c8d84b5b845e337d076ec324", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, max=735471), HTML(value='')))" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "a = 3.798 # lattice parameter\n", - "\n", - "coords = np.array( [ [ 0.0, 0.0, 0.0 ],\n", - " [ 0.5, 0.0, 0.0 ],\n", - " [ 0.0, 0.5, 0.0 ],\n", - " [ 0.0, 0.0, 0.5 ] ] )\n", - "atom_list = [ 'Ti', 'X', 'X', 'X' ]\n", - "lattice = Lattice.from_parameters( a=a, b=a, c=a, alpha=90, beta=90, gamma=90 )\n", - "unit_cell = Structure( lattice, atom_list, coords )\n", - "\n", - "parent_structure = unit_cell * [ 2, 2, 2 ]\n", - "unique_structures = unique_structure_substitutions( parent_structure, 'X', { 'O':8, 'F':16 }, \n", - " show_progress='notebook' )" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "data": { - "application/json": { - "Software versions": [ - { - "module": "Python", - "version": "3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)]" - }, - { - "module": "IPython", - "version": "7.0.1" - }, - { - "module": "OS", - "version": "Darwin 18.2.0 x86_64 i386 64bit" - }, - { - "module": "bsym", - "version": "1.1.1" - }, - { - "module": "numpy", - "version": "1.15.2" - }, - { - "module": "jupyter", - "version": "1.0.0" - }, - { - "module": "pymatgen", - "version": "2018.10.18" - }, - { - "module": "tqdm", - "version": "4.28.1" - } - ] - }, - "text/html": [ - "
SoftwareVersion
Python3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)]
IPython7.0.1
OSDarwin 18.2.0 x86_64 i386 64bit
bsym1.1.1
numpy1.15.2
jupyter1.0.0
pymatgen2018.10.18
tqdm4.28.1
Sun Feb 10 12:08:49 2019 GMT
" - ], - "text/latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)] \\\\ \\hline\n", - "IPython & 7.0.1 \\\\ \\hline\n", - "OS & Darwin 18.2.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "bsym & 1.1.1 \\\\ \\hline\n", - "numpy & 1.15.2 \\\\ \\hline\n", - "jupyter & 1.0.0 \\\\ \\hline\n", - "pymatgen & 2018.10.18 \\\\ \\hline\n", - "tqdm & 4.28.1 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Sun Feb 10 12:08:49 2019 GMT} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "text/plain": [ - "Software versions\n", - "Python 3.7.0 64bit [Clang 10.0.0 (clang-1000.10.44.2)]\n", - "IPython 7.0.1\n", - "OS Darwin 18.2.0 x86_64 i386 64bit\n", - "bsym 1.1.1\n", - "numpy 1.15.2\n", - "jupyter 1.0.0\n", - "pymatgen 2018.10.18\n", - "tqdm 4.28.1\n", - "Sun Feb 10 12:08:49 2019 GMT" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%load_ext version_information\n", - "%version_information bsym, numpy, jupyter, pymatgen, tqdm" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/examples/figures/pymatgen_example_one_site.svg b/examples/figures/pymatgen_example_one_site.svg deleted file mode 100644 index ccdd88d..0000000 --- a/examples/figures/pymatgen_example_one_site.svg +++ /dev/null @@ -1,4007 +0,0 @@ - - - - - - - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Every site is equivalent by symmetry,so there is only 1 unique single-sitesubstituted structure. - - - - - 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 -P3UWT9ohmo2rrqjca6qKPTnLlzDdebGack/saM5ss4q3Pgu1Cc7lXVI9BSnv7+l811qOtUY3mHlq -oZq57Oxqy5udl+5bSmZTLCyyzA+m/StTs/2cJaO76y/J84I1y8NfupbucTBbO5MVs0YrOUb1EWZl -ttRYeaoKk5Cm4CsNs8hc+srLntBj5cshc1HaXSK5Jcmm6nKqsJzNTvO5MbMn2ZfXTmmZa7rKYjb7 -x5f5vrTsNsxMtMwrK3Rml/ZL/PEsjfYsYtrNfvvmanZUnrSetY5olnXPMmiHxbK0f4qh5Z0sbTSF -0ubmEss9h0fLdqjQ2d2XljdDs5tmz9JuXH5mhoZnWffLndEze5lTLFeW6KpymbOis/TdlJ9ZzRIt -73iFzqq9XtY0C5b1hlhf0mWZZaEyp53pskVZqoyBAfKBj5tzeYMvY/bLmhua3U2h5ZhVqTMb7Zu1 -7keXZrXisjLq3OVluoytnIZp5TXfpQ0VqhXz7lM+5mvdYFbdFKohvjSYLmNx7eqXZoTlam7ZFNPq -KlOrGqIsNmYzhsXEwys4ZkVUzbEsKlfrXdaeMjYqwtIlPq1Y2RGxZVfDtMxxy83IeacXLLu5G1uV -TX9ZNTTZb8XIzG7jy0mcQmVJOXNJvSy2Zi2r0Ox1V0tztmDmCYcsLa3qxvKIqPRmPhskj80IrUT3 -sx5azZEsKwkLZTFtln1VIby6fiwrX3S1xJYLpo3H0nlW6JZlhWRJNy+UdmhjvU3xlVTzUrcsK0Zl -WcWceK4MKymz0l6mkszLv7JHWXy75OzuyOZvlanEqNytTjA9r0rCenExzyV3h2Wog72Vyle5l9SR -FbSklfOkMr0EPS6DiX91r8xii9385CZLOXPJJ10Skzu6rExLsFVcCdFdVqU9NmsuzV8Tuiq5y3Nb -yIlRSZsylVTH/WuZLXZimmI6sa5K2qolnVze1kK6Qk6KPDSVV2aFfErqsk2n3WSZSqosSUleZstJ -PmNmQUoOb3CcddUyeElaFVPizMusgkOlZcVsZjFKjpeTlJNYaDpl0r0YHl5JJ1akHGfeJaYeFmec -hJecSiWdkH1HWM6J/2dSYp4sp1AldzV4SnL7FgtWkrWtzjHMcmebF6zklUg1eG7JSnw1eTuWqYTS -yunuk6WW3JVJlW69lC45unJFd5GRyyupI9Zt/JVaUjQ6OmnQSusSu2pJS1pWXGVV6BLXbuhCdYm1 -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 -KWfUFERTKfRkzenNo6uXe53Lh5jodKms6yezWOUqvSqzOldlntULi129VolZWC3N6uXgMZOw5bDQ -aKW7QstCVn39mXnXIRHR86gq28tsWWYNWbGVFZXtqpxLKmdllFlTlrqyykRnlrPy1eHYUWZd0RBe -+YXNvPqUvLJO+dVkGmuaiXmsZhYryTx0C12VqTLTqjJr5JJW2DSrMleTZZXgXO3yVVR1NuVVPtNd -lqxKnGeFY1aWOVY1/TKxisgw06roWNI1RfO0s0q5fi6yUlXGQsTMupr5NBeZM3POXlr1Stcz0XXL -Z5+ZfdIUzafq2Cqq1rN8Vt66vHwmFSGS+7wsU7WmWPqjWampSd9zWnMye7pVvCw5ldxv/U84NJh2 -p3wi3QpN4RMLz5Z9UlFmz1rcaquWT9dYOpVZNuNXNp+TnuKpxKjOL0OFrkw0s0xboVWrMvF1Vcwq -qXOZiKVSq07d0dP8LEVVWdeeM9RiLTNSySn0k6isL3ad3TCzqvrEmecQtZjrc8ujgqXm01PlphJ7 -Gtrz0FeolWeZpVRXnsLfVN5Xl6fZ0TzPls3TVBrxCPNlpvI5WNTDymenZnwnUYmMOnOn7/qnbzuj -uelXLDtzP7NDg2nTf88tO5mvOTTN7zd5NqSbGsR5jofq5kauMuj0mKER565vfh6XPJ/qlHO7TvHZ -2raolLnMNkfT26Zi5pkbdBVZ+UanwUmqNLMrdcTWjP26LnoVzc02vhm+FNLpiHnKe5L1RiaPs//7 -bLFcjdfTs8rJJXPThIV29sxMNK7bnWg8TGcZFT3Pb+uo9EebKvqJTok5Nf3uVnq6ufvRq44mpCwd -C6dgWl6hxI5vvssh0S2PdONNsayx0ei2fPENi050WfBE/2HRE6lc8HRFRp/2xZ6m1yCVjhNssYhe -+Cw3JioiHBPhmJylop885yp97C9Tk1kh0avNYmPf1EqOr0rHZpapne+ndsEaC/NKyThOKt3GteeN -Nn1/N17WeJmD/ZQtp3ocGyZa9Xa3pF2VbZYTX7UP5653TsuYsO/1CpLLboclvy+7x5spLuPcv0ud -W8y2NSfuzVHfrBcrp5X7GlqhstnxjAqWkzzKz5mNqr/J82/6kq3YXLV37MwuNslUeXZ/4xcqY01v -vUuZ582qQmebjYxsvVW2mI3HwitZn09letW0eFzUkqcptKLX2F5N0fhuPNGL81JjwzLkIl9fpjJL -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 - - - ZhA0oMgF13C7EKDVYrW6sSTOsJCfpP5gVAYNaNBANRcYUdVSQwYhgH7OL278YlEjVbnygrFGsy+8 -myO5Mv8a3Dok2g+RQehd/6zFJJqk4UA876X8831htIGCXCZ3bFfcDeM4yfd8xsbpZ/bNLaD+XU4D -1A22oI5b3DLpGXCbBk7OqQ4BUc7omqBj8DeAkg+EwvsLOuDcU9IgYeknNwCetLh1ezOkL+s6YsZJ -C1ZtSnVGmAIkBgR6iBJ5LJBE1nEDbxjVRne5FjODj3kSPCRGBBIozLvs7t4pEx3yElH/q1YBgJAI -AAACLwb2Bi8IuEpcs205rb3iZ6sTr8tsq702W1utzo1OrUtr/Vrbfeul2CmN0/FFL2upYr2ovVek -1d7G+cr0Zun4ihhT3BfuUPzYZceMr/ieL57e6XvFeV9SGq+l0trLXje/vbDLaq/Y9JLWXvHmy159 -Rb+PK7b4Sou72xpJUGlfKGp0KjxlIl9We6K0LzTCaRt2IuXbtNb6Xyn9r9fi/5lrZ3urtdV+X13x -nRVbO2ucP+dbp4hptflS6t1vnd46Ne15bVVZ1OcEUVqrU0rnRZ1ebwetTv9P76bz3m6aac55zjn7 -Iv3IznnlnJ/znJ/nnG6v95y0dvt2krPO60T///+7Wrrh7q27t2zvlLPr1268zZ9y1jxnz2mx9e3n -T1rrRf3qp5s79nd36Seaq3+7u/8/7v/q09reHLv7dR+d/NJaa711y1XW6tVSpxuetGk37e5uSd+7 -rW3737a/aT992l1rfmvf2reWUtvW22tXr15rvbxrrTV7V7fe1vrbt9/2u37X+t/1u379zl5vttV+ -1+7u7urVq1N3r+7u7u7u7u7u1N3d3d3d3d29enWv7tRzrV7dr5jprO5e3b169erVv/v/u7+/3f2+ -Zyz9/19e+2///mNKKa73/f59+f/3/z9fV9VaN1pprlu1Ndeaa621Ulnr11rtV1lrPVnHsk5Znda/ -2Kn9ulF739K3b5/at9Q+tX/pfUm78aX3uvlKav9p/9P7tJteOimtlFJM/fHjp/4bPsn/x/+Y/mN6 -fTumvWHHbt3d7W+eqWfq7r3h/Jnm6+mlF9tMa821Vr8ophdtijGVFFNMJaWUXuy0+8L4ijjXK9Ns -qWdqKaWZ0kznV0kzzbTmWWmmlE5Jc81UUpqppJleEdtrJX3qt9JTpvI///9/lp8fX/nelNbqtPP9 -6bXtf7W36bQbtdRK6u0VO8U+rb1+fdNra712RGmlTSm9uNLt3alTv3SjlFJJ7azU79/71d5M3b2d -0lr9XpJ6bb8X/btdt35zvlu+eX7963dOt37t379Uvnuu/U6dbu91/v17r+2vyu8V86z5Xptvto9n -xZTix/Ri/LT2Y6cYX1ydYlyxe73zL6WT1reT9sSZ4sb4vrV54qe4X1KXFF/4ik8rru6Sel8RX/hx -V3r/3ortP7254pplxRR7thXb7Pjyxrm+dfr23W2/9YovC4DY/iCJI17E+bmvy0qN3HM3vt22+78r -fW85f4u4r8sCOeKMnDUaja44Z1s+spO2ka4cSUINgMpHBFMfJhFV2ocBQOzG2CslShaKbDY2Yr+T -9jVrtX+n188y23p6b9oRX0qxX+w2X/7yW151NsVYZhXNGbvMEdvqkjZtt5ftv3/V238j/naLn171 -ep7ZsbQtW1L6Pz3Tp3LGdmxlf7Xt1ztueumU/ddfGF/r+PrHqzu2V4t7dt/ZNF6cpU+3s6m11lLR -aSWeU2L697o5X3/dnpJSiTtafGuNnW2mOcd8qceaa5a1qf9tabtd4pi/ZfaW+aotr5wd3X1a7+v0 -tqy3G+O2bSXtWO9befVj+dbp3itv25hr5yttT5/X0ntx25i7raTf9po3d+2uc9q3VPHSrzJn2bjv -pNdXr/dztZM2xZhmv1rS99qR/pxz5innR+zXOt3GEmOX3bHOfL1K7E/dOn1nv3Ne2z/lzHm6X/mv -fL/+Lv+aF8V9ye6TpB3tzT39c3br1un2lLHz9GrzfdzY3zrViWN3zXTerv1X38a38a1+xXaJT5Xi -zlP2X964OzfFkrbjt/Kvj7VjtTNL6xSzpB473phvv8Q+rZW4f74/fXrtlPVpdIypxJYqfrt80DrF -l7jmlrXtbGqv2NNWS+WVHXtaidu/o3vO122P94q02tr/TXNLHDP2r9I63bbWvsyOT9HiWNvd2p+f -J8UeqZ0e78Svetk0zypn5etEMk6HQXIKMYnRaFOJqHhEJCFW4WEkJE4FtE6PcX1pr5yvaGud9sK0 -XrH/L3ypX7ZjpvO2S3v19/fPeztPd+sUv6PHaiVu3NYp5+4rqUd6cffluGVta6n6LypnN9eZTNpF -zR1HJYrlibNEyfDrtq9jpzV7YoNUVsq+2C3H/6oVXxhfFk+Kb75dc87YUmnbGuHEKmduEk4xO/k6 -XHnhiuJLlBhWgEBUKw2VSXhUtg7MLrAAtItIAmYSMYs2/Z1oIbZl9fq6bgOFpAKhtmmdNpJ9kUC2 -edp2wjbMdHJ9WQhi332KSxRO+7wvO+lidvJlYPZ58cu4LAyxTSTOFm36eh1Gvt0pI3FZSOCzbVlH -JBvZfqzqwrDjZBy2VeAS5dNABDIUJ/qybTXCaSSgxmEaIcmIdlFCaV+oRLFy49jXYq+riNkIthrh -sFH0VmFGirFbtGkwzEBwk20mDBOeNE8lBliImBLFwkLMk1Hcx0UbioUUKoWgNuJU2H6bs5wt6+VU -YtPZ0/udfq32XoxxzvlmmnPOePZIRexoOVVYUWHeW+9ni+9TmomI1WY64rR12mplrTQjESetktJM -R6yekYjVqZzutf5nOn+rZyJi9eqZjlhnz66ZjlhvplO5dOL7nyuut8rpmU7FZUYiVll/zq414yxn -xJmIWHGmI86ckYgz06lkOTsTEWfOdMQ5OyMRa2c6FSuFhVw2gknMbLVxGivQmVyWfaHuQGUXMaJD -olNJnbCN06cKywI54jRVZGGYbVgFivuUsmhD6c1TaSSQcFthIpnHY/a0LkIvegS/jPu6TpQzViu7 -u78nR7yQXgR5W/26psckhUrZCKZtHWh6lTPO9/v0Ps7dfS/uSuWM7bVv45eWpldO13jQyE46VQdH -eMo2kex7mJw2CitKD9lJp5ocpqGwC07zuvCEgUoanX0jmaeUhVjYfQFoqg7FYZ9FteJC1ddlOmkK -XHk+DfOyk+0EdurDUDWAryPxE+Yp/OkDTyhuM41IFRm4Ar9MI0YUh3kEPtF29LrP4/OiISjSXUsh -WlTapqKWQrR4IVdCEmIV8WJ2CsQkYoEAAliBPONqs885p5zR0nWGIh0pbtlJxqkknK4zIlxRTK7T -wtUpI/DBWBxOdMvEodg2DYSi4LKTTwZmbJsGahSXbRws21KggtJAToSJlvCUiYh8YnaijaJC3DQQ -Jlo8D5OVjjE8aZVoRQs1PS5gPorNs1T3haTwwyow0QoQaisU1500EOOkTlhI2kwaqGlgIqctRBCK -W2GyEy08ZSpsAmMtRA6AjovAWGC2FQhJ4bfJM85uWrEs1WP12NcttrN2rFhXjBjMjDNibMREr1iW -imXhMFAJxa26i1KdsZI6pQIfZtJG2RaC2scpiTbRxio71hvjrFgWr5OoWBanHmd/rPaKM8fZG3ZC -kWusIbaB6owdo/X48b9jtVQsy9Z1GFFGCyb7socYrjpeJxExKO1E83wZSYhloHjFDetOH0bSiRiS -06rDiJgQ20Aj2oadUEQZevG7Yl3RgkFxq+4iytCLNWKi5ctQH0X8shGMwyQopv1v/LhmS6sqZh5G -AkYUt4pzRmurW51pc8XXxzvdPzt97xpvu6Xyj9n7OqXy3yPSl/51s7U6sb9ir9XquFeKZU8rHdeu -0WmulManVofSnrSr/cfSr6fU6vh96xRp56fxbex677U6mWaP979a2rUj9p6P/e/9nKtP7760+vXG -t/9+jrft/3+k1H51qxPxR/pf8bQ68W+kj2vXSWuO9t/qxIu7pa1v/73aTC19q1PppH+tTt9L7bXx -0klttDbji+19q2Mf44xpx96Lrc70djtv5/wX236vlOLs1158G1/ZOeb2xnTebHXqpTj/rT1t33+M -bZTV2nwttdT2vThnK9va7lkzdtk12muzpTQ/fkybfmfO/urtbrO3e1//rzj7Nb9TL7WxVuzZ9ryO -p8yZxq6zzklpU2plS2/ZF356yVm/Xrnmzra2zBe+cL282steOOdsrzurvPdS+/Rvvm5f+Ppte69/ -ed3br7vL+fStTr+/vfK/7Znr+2VvvW5ux+/1yl0v3F/xRX3Wy9pu/7+cYqf3//3C+Zr1ZN1rh9LL -W3buv/D/vL5i6ZXa+5RSekXqkX5bHYrjvZRSfFknWvvn9T6/yvs5/6zv/Rf2zhK/1blun/71tP4V -rbxWB+ef9XrF7dXpZY1UkeJqgZvV7I+1bSfWHC2N1eJIraXC0X5TiR/fSSedOFqf+O/ba/O9fv9a -nRsxvdGvif3me117L0t1r7cR01ypjXVel6Q25ht9Uuv1afX88lb8H/N1e2P7dd2+tbE+vT+v/Yvp -pNPSrp2O+W+ksyt1Of/Gv/if3hivpVTaO71eWqu0stbL6tx7I7Z+b401X3faC2OrQ++0dtZqbXUc -cY3WXlbna43YXvFrpnVWOuXM9OLL6tDrWnrNOulFa87v2VqqeiX9K3ZfNV+f+FqqLN1eq9Pz6ayY -Xm3p4/t/n958Ja701npFmq1ObBvrFa2NnmmmtesVcfZ6xabZ6tQrtq1XxLle1e3V9qLWXrdXxG91 -rju9ll7V1mtxzQEbSqYPvYFBv7ALa+qEMEixMkITkC/kS71F9dgMEApLZ6ByfYCaCQtOQAVcQhik -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 -bBCZ5UtCipd6XQjhZyMxOvJ6obrgpDoxXjCrAw== - - - diff --git a/examples/figures/triangular_c3v_symmetry_operations.pdf b/examples/figures/triangular_c3v_symmetry_operations.pdf deleted file mode 100644 index a4ab64e..0000000 Binary files a/examples/figures/triangular_c3v_symmetry_operations.pdf and /dev/null differ diff --git a/examples/figures/triangular_rotation_operation.png b/examples/figures/triangular_rotation_operation.png deleted file mode 100644 index ac0e9ff..0000000 Binary files a/examples/figures/triangular_rotation_operation.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index f57b5de..8725d4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ {name = "Benjamin J. Morgan", email = "b.j.morgan@bath.ac.uk"}, ] license = {text = "MIT"} -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "numpy", "pymatgen", @@ -19,14 +19,34 @@ dependencies = [ ] classifiers = [ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Topic :: Scientific/Engineering", ] +[project.optional-dependencies] +dev = [ + "pytest>=7.0", + "pytest-cov>=4.0", + "mypy>=1.0", + "types-tqdm" +] +docs = [ + "sphinx>=7.0.0", + "sphinx_rtd_theme>=1.0.0", + "nbsphinx>=0.8.9", + "ipykernel>=6.0.0", + "matplotlib>=3.4.0", + "pypandoc>=1.5", + "myst-parser", + "myst-nb", +] + [project.urls] "Homepage" = "https://github.com/bjmorgan/bsym" "Bug Tracker" = "https://github.com/bjmorgan/bsym/issues" @@ -37,3 +57,36 @@ packages = ["bsym", "bsym.interface"] [tool.setuptools.dynamic] version = {attr = "bsym.version.__version__"} + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = [ + "--verbose", + "--strict-markers", +] + +[tool.coverage.run] +source = ["bsym"] +omit = [ + "*/python?.?/*", + "*/lib-python/?.?/*.py", + "*/lib_pypy/_*.py", + "*/site-packages/ordereddict.py", + "*/site-packages/nose/*", + "*/unittest2/*", + "docs", +] + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = false +disallow_incomplete_defs = false +check_untyped_defs = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true diff --git a/tests/integration_tests/test_interface_pymatgen.py b/tests/integration_tests/test_interface_pymatgen.py index e831ac6..19b1df4 100644 --- a/tests/integration_tests/test_interface_pymatgen.py +++ b/tests/integration_tests/test_interface_pymatgen.py @@ -3,14 +3,15 @@ import numpy as np from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Molecule, Structure -from bsym.interface.pymatgen import ( unique_symmetry_operations_as_vectors_from_structure, - space_group_from_structure, - parse_site_distribution, - unique_structure_substitutions, - new_structure_from_substitution, - configuration_space_from_structure, - space_group_symbol_from_structure, - configuration_space_from_molecule ) +from bsym.interface.pymatgen import (unique_symmetry_operations_as_vectors_from_structure, + space_group_from_structure, + parse_site_distribution, + unique_structure_substitutions, + new_structure_from_substitution, + configuration_space_from_structure, + space_group_symbol_from_structure, + configuration_space_from_molecule, + unique_structure_substitutions_by_composition) from itertools import permutations from bsym import ( SymmetryOperation, @@ -185,6 +186,76 @@ def test_unique_structure_substitutions_with_mismatched_site_distribution_raises def test_space_group_symbol_from_structure( self ): # integration test self.assertEqual( space_group_symbol_from_structure( self.structure ), 'Fm-3m' ) + + def test_unique_structure_substitutions_by_composition_binary_on_square(self): + """Test binary substitution on 4-site square lattice gives all expected compositions""" + # Create a 4-site square lattice structure + coords = np.array([[0.0, 0.0, 0.0], + [0.5, 0.0, 0.0], + [0.0, 0.5, 0.0], + [0.5, 0.5, 0.0]]) + atom_list = ['X'] * 4 # Placeholder atoms to be substituted + lattice = Lattice.from_parameters(a=2.0, b=2.0, c=2.0, alpha=90, beta=90, gamma=90) + parent_structure = Structure(lattice, atom_list, coords) + + # Perform composition-based substitution + results = unique_structure_substitutions_by_composition( + parent_structure, + 'X', + ['Li', 'Na'] + ) + + # Should have 5 compositions: (4,0), (3,1), (2,2), (1,3), (0,4) + self.assertEqual(len(results), 5) + + # Check (4, 0): all Li + self.assertIn((4, 0), results) + self.assertEqual(len(results[(4, 0)]), 1) + self.assertEqual(results[(4, 0)][0].composition.get_atomic_fraction('Li'), 1.0) + self.assertEqual(results[(4, 0)][0].number_of_equivalent_configurations, 1) + + # Check (3, 1): 3 Li, 1 Na + self.assertIn((3, 1), results) + self.assertEqual(len(results[(3, 1)]), 1) + self.assertEqual(results[(3, 1)][0].composition.get_atomic_fraction('Li'), 0.75) + self.assertEqual(results[(3, 1)][0].number_of_equivalent_configurations, 4) + + # Check (2, 2): 2 Li, 2 Na - should have 2 unique structures + self.assertIn((2, 2), results) + self.assertEqual(len(results[(2, 2)]), 2) + total_degeneracy_2_2 = sum(s.number_of_equivalent_configurations for s in results[(2, 2)]) + self.assertEqual(total_degeneracy_2_2, 6) # Should be C(4,2) = 6 + # Check stoichiometry + for s in results[(2, 2)]: + self.assertEqual(s.composition.get_atomic_fraction('Li'), 0.5) + self.assertEqual(s.composition.get_atomic_fraction('Na'), 0.5) + + # Verify one structure has adjacent arrangement, one has diagonal + distances_squared = [] + for s in results[(2, 2)]: + li_indices = s.indices_from_symbol('Li') + na_indices = s.indices_from_symbol('Na') + # Distance between the two Li atoms + dist_sq = s.get_distance(li_indices[0], li_indices[1])**2 + distances_squared.append(dist_sq) + distances_squared = sorted(distances_squared) + np.testing.assert_array_almost_equal(distances_squared, [1.0, 2.0]) # adjacent=1, diagonal=√2 + + # Check (1, 3): 1 Li, 3 Na + self.assertIn((1, 3), results) + self.assertEqual(len(results[(1, 3)]), 1) + self.assertEqual(results[(1, 3)][0].composition.get_atomic_fraction('Li'), 0.25) + self.assertEqual(results[(1, 3)][0].number_of_equivalent_configurations, 4) + + # Check (0, 4): all Na + self.assertIn((0, 4), results) + self.assertEqual(len(results[(0, 4)]), 1) + self.assertEqual(results[(0, 4)][0].composition.get_atomic_fraction('Na'), 1.0) + self.assertEqual(results[(0, 4)][0].number_of_equivalent_configurations, 1) + + # Verify total unique configurations + total_unique = sum(len(configs) for configs in results.values()) + self.assertEqual(total_unique, 6) if __name__ == '__main__': unittest.main() diff --git a/tests/notebook_tests/__init__.py b/tests/notebook_tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/notebook_tests/requirements.txt b/tests/notebook_tests/requirements.txt deleted file mode 100644 index 40f02cf..0000000 --- a/tests/notebook_tests/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -jupyter -version_information diff --git a/tests/notebook_tests/test_notebooks.py b/tests/notebook_tests/test_notebooks.py deleted file mode 100644 index 3b9b59b..0000000 --- a/tests/notebook_tests/test_notebooks.py +++ /dev/null @@ -1,29 +0,0 @@ -import unittest -import nbformat -from nbconvert.preprocessors import ExecutePreprocessor -import os -import glob - -def get_cwd(): - path = os.path.abspath(__file__) - return os.path.dirname(path) - -def execute_notebook( filename ): - with open( filename ) as f: - nb = nbformat.read( f, as_version=4 ) - ep = ExecutePreprocessor( timeout=600, kernel_name='python3' ) - ep.preprocess( nb, { 'metadata': { 'path': os.path.dirname( filename ) } } ) - -def get_notebook_filenames( notebook_dir ): - return glob.glob( os.path.join( get_cwd(), notebook_dir, '*.ipynb' ) ) - -class JupyterNotebookTestCase( unittest.TestCase ): - - def test_notebooks_execute( self ): - notebook_dir = '../../examples' - notebook_filenames = get_notebook_filenames( notebook_dir ) - for nf in notebook_filenames: - execute_notebook( nf ) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit_tests/test_bsym.py b/tests/unit_tests/test_bsym.py index d58b1ff..d256b4f 100644 --- a/tests/unit_tests/test_bsym.py +++ b/tests/unit_tests/test_bsym.py @@ -25,9 +25,6 @@ def test_bsym_imports_ConfigurationSpace( self ): def test_bsym_imports_CoordinateConfigSpace( self ): from bsym import CoordinateConfigSpace - def test_bsym_imports_ColourOperation( self ): - from bsym import ColourOperation - class TestOldBsymModule( unittest.TestCase ): def test_old_bsym_import_quits( self ): diff --git a/tests/unit_tests/test_colour_operation.py b/tests/unit_tests/test_colour_operation.py deleted file mode 100644 index defc5f8..0000000 --- a/tests/unit_tests/test_colour_operation.py +++ /dev/null @@ -1,69 +0,0 @@ -import unittest -import numpy as np -from bsym import ColourOperation, Configuration -from unittest.mock import patch - -class ColourOperationTestCase( unittest.TestCase ): - """Tests for colour operation methods""" - - def test_symmetry_operation_is_initialised_from_a_matrix( self ): - matrix = np.array( [ [ 1, 0 ], [ 0, 1 ] ] ) - mapping = [ { 1: 0, 0: 1 }, { 1: 1, 0: 0 } ] - co = ColourOperation( matrix, colour_mapping=mapping ) - np.testing.assert_array_equal( co.matrix, matrix ) - self.assertEqual( co.colour_mapping, mapping ) - - def test_from_vector( self ): - vector = [ 2, 3, 1 ] - mapping = [ { 1: 0, 0: 1 }, { 1: 1, 0: 0 }, { 1: 1, 0: 0 } ] - co = ColourOperation.from_vector( vector, mapping ) - np.testing.assert_array_equal( co.matrix, np.array( [ [ 0, 0, 1 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ] ) ) - self.assertEqual( co.colour_mapping, mapping ) - - def test_from_vector_with_label( self ): - vector = [ 2, 3, 1 ] - mapping = [ { 1: 0, 0: 1 }, { 1: 1, 0: 0 } ] - label = 'A' - co = ColourOperation.from_vector( vector, mapping, label=label ) - np.testing.assert_array_equal( co.matrix, np.array( [ [ 0, 0, 1 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ] ) ) - self.assertEqual( co.label, label ) - self.assertEqual( co.colour_mapping, mapping ) - - def test_symmetry_operation_is_initialised_with_label( self ): - matrix = np.array( [ [ 1, 0 ], [ 0, 1 ] ] ) - label = 'E' - mapping = [ { 1: 0, 0: 1 }, { 1: 1, 0: 0 } ] - co = ColourOperation( matrix, mapping, label=label ) - self.assertEqual( co.label, label ) - self.assertEqual( co.colour_mapping, mapping ) - - def test_from_vector_counting_from_zero( self ): - vector = [ 1, 2, 0 ] - mapping = [ { 1: 0, 0: 1 }, { 1: 1, 0: 0 } ] - co = ColourOperation.from_vector( vector, mapping, count_from_zero=True ) - np.testing.assert_array_equal( co.matrix, np.array( [ [ 0, 0, 1 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ] ) ) - self.assertEqual( co.colour_mapping, mapping ) - - def test_operate_on( self ): - matrix = np.array( [ [ 0, 1, 0 ], [ 0, 0, 1 ], [ 1, 0, 0 ] ] ) - colour_mapping = [ { 1:1, 2:2, 3:3 }, - { 1:2, 2:3, 3:1 }, - { 1:3, 2:2, 3:1 } ] - co = ColourOperation( matrix, colour_mapping ) - configuration = Configuration( [ 1, 2, 3 ] ) - co.operate_on( configuration ) - np.testing.assert_array_equal( co.operate_on( configuration ).vector, np.array( [ 2, 1, 3 ] ) ) - - def test_mul( self ): - matrix_a = np.array( [ [ 1, 0 ], [ 0, 1 ] ] ) - colour_mapping_a = [ { 0:1, 1:0 }, { 0:1, 1:0 } ] - matrix_b = np.array( [ [ 0, 1 ], [ 1, 0 ] ] ) - colour_mapping_b = [ { 0:1, 1:0 }, { 0:1, 1:0 } ] - co_a = ColourOperation( matrix_a, colour_mapping_a ) - co_b = ColourOperation( matrix_b, colour_mapping_b ) - co_c = co_a * co_b - np.testing.assert_array_equal( co_c.matrix , np.array( [ [ 0, 1 ], [ 1, 0 ] ] ) ) - self.assertEqual( co_c.colour_mapping, [ { 0:0, 1:1 }, { 0:0, 1:1 } ] ) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit_tests/test_configuration.py b/tests/unit_tests/test_configuration.py index ea49b11..6f4fe64 100644 --- a/tests/unit_tests/test_configuration.py +++ b/tests/unit_tests/test_configuration.py @@ -1,6 +1,7 @@ import unittest from unittest.mock import Mock, patch from bsym.configuration import Configuration +from bsym.symmetry_group import SymmetryGroup from bsym import SymmetryOperation import numpy as np @@ -127,12 +128,156 @@ def test_tolist( self ): def test_position( self ): self.assertEqual( self.configuration.position( 0 ), [ 1, 2 ] ) - def test_map_objects( self ): - self.assertEqual( self.configuration.map_objects( [ 'A', 'B', 'C' ] ), { 1: [ 'A' ], 0: [ 'B', 'C' ] } ) + def test_map_objects(self): + self.assertEqual(self.configuration.map_objects(['A', 'B', 'C']), {1: ['A'], 0: ['B', 'C']}) - def test_map_objects_with_incompatible_object_list_raises_ValueError( self ): - with self.assertRaises( ValueError ): - self.configuration.map_objects( [ 'A', 'B' ] ) + def test_map_objects_with_incompatible_object_list_raises_ValueError(self): + with self.assertRaises(ValueError): + self.configuration.map_objects(['A', 'B']) + + def test_get_byte_equivalents_returns_set_of_bytes(self): + """Test that get_byte_equivalents returns a set of bytes.""" + s0 = SymmetryOperation.from_vector([1, 2, 3]) + sg = SymmetryGroup(symmetry_operations=[s0]) + + config = Configuration([1, 0, 0]) + result = config.get_byte_equivalents(sg) + + self.assertIsInstance(result, set) + for item in result: + self.assertIsInstance(item, bytes) + + def test_get_byte_equivalents_returns_correct_values(self): + """Test that get_byte_equivalents returns correct byte representations.""" + s0 = SymmetryOperation.from_vector([1, 2, 3]) + s1 = SymmetryOperation.from_vector([2, 1, 3]) + sg = SymmetryGroup(symmetry_operations=[s0, s1]) + + config = Configuration([1, 0, 0]) + byte_equivalents = config.get_byte_equivalents(sg) + + # Should get byte representations of [1, 0, 0] and [0, 1, 0] + expected = { + np.array([1, 0, 0], dtype=np.int8).tobytes(), + np.array([0, 1, 0], dtype=np.int8).tobytes() + } + self.assertEqual(byte_equivalents, expected) + + def test_get_byte_equivalents_uses_unique_operations(self): + """Test that get_byte_equivalents only applies unique operations.""" + s0 = SymmetryOperation.from_vector([1, 2, 3]) + s1 = SymmetryOperation.from_vector([2, 1, 3]) + s2 = SymmetryOperation.from_vector([1, 2, 3]) # Duplicate of s0 + sg = SymmetryGroup(symmetry_operations=[s0, s1, s2]) + + config = Configuration([1, 0, 0]) + byte_equivalents = config.get_byte_equivalents(sg) + + # Should only apply 2 unique operations, not 3 + self.assertEqual(len(byte_equivalents), 2) + + def test_get_byte_equivalents_with_larger_configuration(self): + """Test get_byte_equivalents with larger configuration space.""" + s0 = SymmetryOperation.from_vector([1, 2, 3, 4, 5]) + s1 = SymmetryOperation.from_vector([5, 4, 3, 2, 1]) + sg = SymmetryGroup(symmetry_operations=[s0, s1]) + + # Use an asymmetric configuration so reverse gives different result + config = Configuration([1, 0, 0, 0, 0]) + byte_equivalents = config.get_byte_equivalents(sg) + + # Should have 2 results (identity and reverse are different) + self.assertEqual(len(byte_equivalents), 2) + # All should be bytes + for val in byte_equivalents: + self.assertIsInstance(val, bytes) + + def test_get_byte_equivalents_produces_different_values_for_different_configs(self): + """Test that different configurations produce different byte representations.""" + s0 = SymmetryOperation.from_vector([1, 2, 3]) + sg = SymmetryGroup(symmetry_operations=[s0]) + + config1 = Configuration([1, 0, 0]) + config2 = Configuration([0, 1, 0]) + + bytes1 = config1.get_byte_equivalents(sg) + bytes2 = config2.get_byte_equivalents(sg) + + # Different configurations should produce different byte sets + self.assertNotEqual(bytes1, bytes2) + + def test_as_bytes_returns_byte_representation(self): + """Test that as_bytes returns the byte representation of the configuration.""" + config = Configuration([1, 0, 1]) + result = config.as_bytes() + + self.assertIsInstance(result, bytes) + expected = np.array([1, 0, 1], dtype=np.int8).tobytes() + self.assertEqual(result, expected) + + def test_as_bytes_consistent_with_hash(self): + """Test that as_bytes is consistent with __hash__.""" + config1 = Configuration([1, 0, 1]) + config2 = Configuration([1, 0, 1]) + + # Same configuration should have same bytes and hash + self.assertEqual(config1.as_bytes(), config2.as_bytes()) + self.assertEqual(hash(config1), hash(config2)) + + def test_tuple_to_bytes_returns_bytes(self): + """Test that tuple_to_bytes returns bytes.""" + result = Configuration.tuple_to_bytes((1, 0, 1)) + self.assertIsInstance(result, bytes) + + def test_tuple_to_bytes_converts_to_int8(self): + """Test that tuple_to_bytes uses int8 representation.""" + result = Configuration.tuple_to_bytes((1, 0, 1)) + expected = np.array([1, 0, 1], dtype=np.int8).tobytes() + self.assertEqual(result, expected) + + def test_tuple_to_bytes_consistent_results(self): + """Test that same tuple produces same bytes.""" + result1 = Configuration.tuple_to_bytes((1, 0, 1)) + result2 = Configuration.tuple_to_bytes((1, 0, 1)) + self.assertEqual(result1, result2) + + def test_tuple_to_bytes_different_for_different_tuples(self): + """Test that different tuples produce different bytes.""" + result1 = Configuration.tuple_to_bytes((1, 0, 0)) + result2 = Configuration.tuple_to_bytes((0, 1, 0)) + self.assertNotEqual(result1, result2) + + def test_array_to_bytes_returns_bytes(self): + """Test that array_to_bytes returns bytes.""" + arr = np.array([1, 0, 1], dtype=np.int8) + result = Configuration.array_to_bytes(arr) + self.assertIsInstance(result, bytes) + + def test_array_to_bytes_uses_array_directly(self): + """Test that array_to_bytes converts int8 array to bytes.""" + arr = np.array([1, 0, 1], dtype=np.int8) + result = Configuration.array_to_bytes(arr) + expected = arr.tobytes() + self.assertEqual(result, expected) + + def test_array_to_bytes_consistent_with_tuple_to_bytes(self): + """Test that tuple and array conversions produce same bytes for same values.""" + tup = (1, 0, 1) + arr = np.array([1, 0, 1], dtype=np.int8) + + tuple_result = Configuration.tuple_to_bytes(tup) + array_result = Configuration.array_to_bytes(arr) + + self.assertEqual(tuple_result, array_result) + + def test_as_bytes_uses_array_to_bytes(self): + """Test that as_bytes is consistent with array_to_bytes.""" + config = Configuration([1, 0, 1]) + + instance_result = config.as_bytes() + static_result = Configuration.array_to_bytes(config.vector) + + self.assertEqual(instance_result, static_result) if __name__ == '__main__': unittest.main() diff --git a/tests/unit_tests/test_configuration_space.py b/tests/unit_tests/test_configuration_space.py index c949715..f767a11 100644 --- a/tests/unit_tests/test_configuration_space.py +++ b/tests/unit_tests/test_configuration_space.py @@ -1,8 +1,10 @@ import unittest from unittest.mock import Mock, patch -from bsym import ConfigurationSpace, SymmetryGroup, SymmetryOperation, Configuration -from bsym.configuration_space import permutation_as_config_number, colourings_generator +from bsym import ConfigurationSpace,SymmetryOperation, Configuration +from bsym.configuration_space import permutation_as_config_number, colourings_generator, apply_species_mapping +from bsym.symmetry_group import SymmetryGroup import numpy as np +import io class ConfigurationSpaceTestCase( unittest.TestCase ): @@ -69,22 +71,483 @@ def test_unique_colourings( self ): configuration_space.enumerate_configurations.assert_called_with( mock_colourings_generator(), verbose=False ) self.assertEqual( colourings, [ mock_configuration ] ) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_returns_dict( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test that method returns a dict with composition tuples as keys + and lists of Configuration objects as values. + """ + mock_generate_partitions.return_value = [(2, 0)] + mock_unique_permutations.return_value = iter([(2, 0)]) + mock_config = Mock(spec=Configuration) + + config_space = ConfigurationSpace(objects=[1, 2]) + with patch.object(config_space, 'unique_configurations', return_value=[mock_config]): + result = config_space.unique_configurations_by_composition(n_species=2) + + self.assertIsInstance(result, dict) + self.assertEqual(len(result), 1) + self.assertIn((2, 0), result) + self.assertIsInstance(result[(2, 0)], list) + self.assertEqual(result[(2, 0)], [mock_config]) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_excludes_zero_counts_from_site_distribution( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test that species with count=0 are excluded from site_distribution + passed to unique_configurations. + """ + mock_generate_partitions.return_value = [(3, 0, 1)] + mock_unique_permutations.return_value = iter([(3, 0, 1)]) + + config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) + with patch.object(config_space, 'unique_configurations', return_value=[]) as mock_unique_configs: + config_space.unique_configurations_by_composition(n_species=3) + + call_kwargs = mock_unique_configs.call_args[1] + # Species 1 (index 1) has count 0, so should be excluded + self.assertEqual(call_kwargs['site_distribution'], {0: 3, 2: 1}) + self.assertNotIn(1, call_kwargs['site_distribution']) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_stores_configs_under_correct_keys( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test that configurations are stored under correct composition keys. + Canonical composition is analyzed, non-canonical are relabeled. + """ + mock_generate_partitions.return_value = [(2, 1)] + mock_unique_permutations.return_value = iter([(2, 1), (1, 2)]) + + # Create mock configuration for canonical + config_a = Mock(spec=Configuration) + config_a.label = 'a' + config_a.vector = np.array([0, 0, 1]) # 2 of species 0, 1 of species 1 + config_a.count = 3 + + config_space = ConfigurationSpace(objects=[1, 2, 3]) + + def mock_unique_configs_side_effect(site_distribution, **kwargs): + if site_distribution == {0: 2, 1: 1}: # canonical (2, 1) + return [config_a] + else: + raise ValueError(f"Unexpected site_distribution: {site_distribution}") + + with patch.object(config_space, 'unique_configurations', + side_effect=mock_unique_configs_side_effect): + result = config_space.unique_configurations_by_composition(n_species=2) + + # Both compositions should be in results + self.assertEqual(len(result), 2) + self.assertIn((2, 1), result) + self.assertIn((1, 2), result) + + # Canonical (2, 1) should have the original mock + self.assertEqual(result[(2, 1)], [config_a]) + + # Non-canonical (1, 2) should have relabeled version + self.assertEqual(len(result[(1, 2)]), 1) + relabeled = result[(1, 2)][0] + + # Check relabelled config has correct species mapping: [0,0,1] → [1,1,0] + np.testing.assert_array_equal(relabeled.vector, np.array([1, 1, 0])) + self.assertEqual(relabeled.count, 3) # Count should be preserved + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + @patch('bsym.configuration_space.satisfies_bounds') + def test_unique_configurations_by_composition_filters_by_bounds( + self, mock_satisfies_bounds, mock_unique_permutations, mock_generate_partitions): + """ + Test that compositions are filtered by bounds before processing. + """ + mock_generate_partitions.return_value = [(3, 1), (2, 2)] + # First partition generates 2 permutations, second generates 1 + mock_unique_permutations.side_effect = [ + iter([(3, 1), (1, 3)]), # permutations of (3, 1) + iter([(2, 2)]) # permutations of (2, 2) + ] + + # Only allow (3, 1) and (2, 2) to pass bounds check + def satisfies_bounds_side_effect(composition_dict, bounds): + if composition_dict == {0: 3, 1: 1}: + return True + elif composition_dict == {0: 1, 1: 3}: + return False + elif composition_dict == {0: 2, 1: 2}: + return True + return False + + mock_satisfies_bounds.side_effect = satisfies_bounds_side_effect + + config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) + bounds = {0: (2, 3), 1: (1, 2)} + + with patch.object(config_space, 'unique_configurations', return_value=[]) as mock_unique_configs: + result = config_space.unique_configurations_by_composition(n_species=2, bounds=bounds) + + # Should only process 2 compositions (3,1) and (2,2) + self.assertEqual(mock_unique_configs.call_count, 2) + self.assertEqual(len(result), 2) + self.assertIn((3, 1), result) + self.assertIn((2, 2), result) + self.assertNotIn((1, 3), result) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + @patch('bsym.configuration_space.satisfies_bounds') + def test_unique_configurations_by_composition_no_bounds_skips_filtering( + self, mock_satisfies_bounds, mock_unique_permutations, mock_generate_partitions): + """ + Test that when bounds=None, satisfies_bounds is not called. + """ + mock_generate_partitions.return_value = [(2, 2)] + mock_unique_permutations.return_value = iter([(2, 2)]) + + config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) + + with patch.object(config_space, 'unique_configurations', return_value=[]): + config_space.unique_configurations_by_composition(n_species=2, bounds=None) + + mock_satisfies_bounds.assert_not_called() + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_single_species( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test edge case: n_species=1 (only one possible composition). + """ + mock_generate_partitions.return_value = [(4,)] + mock_unique_permutations.return_value = iter([(4,)]) + + config_space = ConfigurationSpace(objects=[1, 2, 3, 4]) + + with patch.object(config_space, 'unique_configurations', return_value=[]) as mock_unique_configs: + result = config_space.unique_configurations_by_composition(n_species=1) + + self.assertEqual(len(result), 1) + self.assertIn((4,), result) + + # Check that site_distribution is correct + call_kwargs = mock_unique_configs.call_args[1] + self.assertEqual(call_kwargs['site_distribution'], {0: 4}) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_more_species_than_sites( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test edge case: n_species > n_sites (some species must have count 0). + """ + mock_generate_partitions.return_value = [(2, 0, 0), (1, 1, 0)] + mock_unique_permutations.side_effect = [ + iter([(2, 0, 0), (0, 2, 0), (0, 0, 2)]), + iter([(1, 1, 0), (1, 0, 1), (0, 1, 1)]) + ] + + config_space = ConfigurationSpace(objects=[1, 2]) + + with patch.object(config_space, 'unique_configurations', return_value=[]) as mock_unique_configs: + result = config_space.unique_configurations_by_composition(n_species=3) + + self.assertEqual(len(result), 6) + + # Check that zero counts are excluded from site_distributions + for call in mock_unique_configs.call_args_list: + site_dist = call[1]['site_distribution'] + # No species should have count 0 in site_distribution + for count in site_dist.values(): + self.assertNotEqual(count, 0) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + @patch('sys.stdout', new_callable=io.StringIO) + def test_unique_configurations_by_composition_verbose_false( + self, mock_stdout, mock_unique_permutations, mock_generate_partitions): + """ + Test that verbose=False doesn't print output. + """ + mock_generate_partitions.return_value = [(2, 0)] + mock_unique_permutations.return_value = iter([(2, 0)]) + + config_space = ConfigurationSpace(objects=[1, 2]) + + with patch.object(config_space, 'unique_configurations', return_value=[]): + config_space.unique_configurations_by_composition(n_species=2, verbose=False) + + output = mock_stdout.getvalue() + self.assertEqual(output, '') + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + @patch('sys.stdout', new_callable=io.StringIO) + def test_unique_configurations_by_composition_verbose_true( + self, mock_stdout, mock_unique_permutations, mock_generate_partitions): + """ + Test that verbose=True prints per-partition and summary information. + """ + mock_generate_partitions.return_value = [(2, 1)] + mock_unique_permutations.return_value = iter([(2, 1), (1, 2)]) + + mock_config = Mock(spec=Configuration) + mock_config.vector = np.array([0, 0, 1]) + mock_config.count = 1 + + config_space = ConfigurationSpace(objects=[1, 2, 3]) + + with patch.object(config_space, 'unique_configurations', return_value=[mock_config]): + config_space.unique_configurations_by_composition(n_species=2, verbose=True) + + output = mock_stdout.getvalue() + + # Check for per-partition output + self.assertIn('Processing partition (2, 1)', output) + self.assertIn('Found 1 unique configurations', output) + + # Check for summary output + self.assertIn('Summary:', output) + self.assertIn('Analyzed 1 partitions', output) + self.assertIn('Generated 2 compositions', output) + self.assertIn('Total unique configurations: 2', output) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + @patch('bsym.configuration_space.tqdm') + def test_unique_configurations_by_composition_show_progress_true( + self, mock_tqdm, mock_unique_permutations, mock_generate_partitions): + """ + Test that show_progress=True creates progress bar. + """ + mock_generate_partitions.return_value = [(2, 1)] + mock_unique_permutations.return_value = iter([(2, 1), (1, 2)]) + + # Create mock progress bar + mock_progress_bar = Mock() + mock_tqdm.return_value = mock_progress_bar + + config_space = ConfigurationSpace(objects=[1, 2, 3]) + + with patch.object(config_space, 'unique_configurations', return_value=[]): + config_space.unique_configurations_by_composition(n_species=2, show_progress=True) + + # Verify tqdm was called (without checking total since it's now indeterminate) + mock_tqdm.assert_called_once() + + # Verify progress bar was updated (once per composition: (2,1) and (1,2)) + self.assertEqual(mock_progress_bar.update.call_count, 2) + + # Verify progress bar was closed + mock_progress_bar.close.assert_called_once() + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_passes_show_progress_to_unique_configurations( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test that show_progress parameter is passed through to + self.unique_configurations for nested progress bars. + """ + mock_generate_partitions.return_value = [(2, 0)] + mock_unique_permutations.side_effect = lambda x: iter([(2, 0)]) + + config_space = ConfigurationSpace(objects=[1, 2]) + + # Test with show_progress=True + with patch.object(config_space, 'unique_configurations', return_value=[]) as mock_unique_configs: + config_space.unique_configurations_by_composition(n_species=2, show_progress=True) + + call_kwargs = mock_unique_configs.call_args[1] + self.assertEqual(call_kwargs['show_progress'], True) + + # Test with show_progress=False + with patch.object(config_space, 'unique_configurations', return_value=[]) as mock_unique_configs: + config_space.unique_configurations_by_composition(n_species=2, show_progress=False) + + call_kwargs = mock_unique_configs.call_args[1] + self.assertEqual(call_kwargs['show_progress'], False) + + @patch('bsym.configuration_space.generate_partitions') + @patch('bsym.configuration_space.unique_permutations') + def test_unique_configurations_by_composition_only_analyzes_canonical_compositions( + self, mock_unique_permutations, mock_generate_partitions): + """ + Test that symmetry analysis is only performed on canonical compositions. + + For a 2-site system with 2 species: + - Compositions: (2, 0), (1, 1), (0, 2) + - (2, 0) and (0, 2) are related by species exchange + - Only (2, 0) and (1, 1) should undergo symmetry analysis + - All three compositions should be present in the output + """ + mock_generate_partitions.return_value = [(2, 0), (1, 1)] + mock_unique_permutations.side_effect = [ + iter([(2, 0), (0, 2)]), # permutations of partition (2, 0) + iter([(1, 1)]) # permutations of partition (1, 1) + ] + + # Create mock configurations + mock_config_2_0 = Mock(spec=Configuration) + mock_config_2_0.vector = np.array([0, 0]) # Both sites are species 0 + mock_config_2_0.count = 1 + + mock_config_1_1 = Mock(spec=Configuration) + mock_config_1_1.vector = np.array([0, 1]) # One of each species + mock_config_1_1.count = 1 + + mock_config_0_2 = Mock(spec=Configuration) # Shouldn't be needed if working correctly + mock_config_0_2.vector = np.array([1, 1]) # Both sites are species 1 + mock_config_0_2.count = 1 + + config_space = ConfigurationSpace(objects=[1, 2]) + + # Mock unique_configurations to return different configs for different compositions + def mock_unique_configs_side_effect(site_distribution, **kwargs): + if site_distribution == {0: 2}: # composition (2, 0) + return [mock_config_2_0] + elif site_distribution == {0: 1, 1: 1}: # composition (1, 1) + return [mock_config_1_1] + elif site_distribution == {1: 2}: # composition (0, 2) - shouldn't be called + return [mock_config_0_2] + else: + return [] # Default fallback + + with patch.object(config_space, 'unique_configurations', + side_effect=mock_unique_configs_side_effect) as mock_unique_configs: + result = config_space.unique_configurations_by_composition(n_species=2) + + # Verify unique_configurations called exactly twice (not for (0, 2)) + self.assertEqual(mock_unique_configs.call_count, 2) + + # Verify calls for (2, 0) and (1, 1) + call_args_list = mock_unique_configs.call_args_list + called_site_distributions = [call[1]['site_distribution'] for call in call_args_list] + self.assertIn({0: 2}, called_site_distributions) + self.assertIn({0: 1, 1: 1}, called_site_distributions) + + # Verify (0, 2) was NOT analyzed (no site_distribution={1: 2}) + self.assertNotIn({1: 2}, called_site_distributions) + + # Verify all three compositions present in output + self.assertEqual(len(result), 3) + self.assertIn((2, 0), result) + self.assertIn((1, 1), result) + self.assertIn((0, 2), result) + + # Verify each composition returns a list + self.assertIsInstance(result[(2, 0)], list) + self.assertIsInstance(result[(1, 1)], list) + self.assertIsInstance(result[(0, 2)], list) + + # Verify (0, 2) has configurations (generated from (2, 0)) + self.assertEqual(len(result[(0, 2)]), len(result[(2, 0)])) + class ConfigurationSpaceModuleFunctionsTestCase( unittest.TestCase ): - def test_permutation_as_config_number( self ): - self.assertEqual( permutation_as_config_number( [ 1, 1, 0, 0, 1 ] ), 11001 ) + def test_permutation_as_config_number(self): + self.assertEqual( permutation_as_config_number([1, 1, 0, 0, 1]), 11001) - def test_colourings_generator( self ): - colourings = list( colourings_generator( [ 1, 0 ], dim=3 ) ) - expected_colourings = [ [1, 1, 1], - [0, 1, 1], [1, 0, 1], [1, 1, 0], - [0, 0, 1], [0, 1, 0], [1, 0, 0], - [0, 0, 0] ] + def test_colourings_generator(self): + colourings = list(colourings_generator([1, 0], dim=3)) + expected_colourings = [(1, 1, 1), + (0, 1, 1), (1, 0, 1), (1, 1, 0), + (0, 0, 1), (0, 1, 0), (1, 0, 0), + (0, 0, 0)] for c in colourings: - self.assertEqual( c in expected_colourings, True ) + self.assertEqual(c in expected_colourings, True) for ec in expected_colourings: - self.assertEqual( ec in colourings, True ) + self.assertEqual(ec in colourings, True) + + def test_apply_species_mapping_identity(self): + """Identity mapping should return equivalent configuration.""" + config = Configuration([0, 1, 0, 1]) + config.count = 3 + mapping = [0, 1] # No change + + result = apply_species_mapping(config, mapping) + + np.testing.assert_array_equal(result.vector, np.array([0, 1, 0, 1])) + self.assertEqual(result.count, 3) + self.assertIsNot(result, config) # Should be a new object + + def test_apply_species_mapping_binary_swap(self): + """Swap two species: 0↔1.""" + config = Configuration([0, 0, 1]) + config.count = 2 + mapping = [1, 0] # Swap species 0 and 1 + + result = apply_species_mapping(config, mapping) + + np.testing.assert_array_equal(result.vector, np.array([1, 1, 0])) + self.assertEqual(result.count, 2) + + def test_apply_species_mapping_ternary_rotation(self): + """Three-species rotation: 0→1, 1→2, 2→0.""" + config = Configuration([0, 1, 2, 0]) + config.count = 5 + mapping = [1, 2, 0] # Rotate: 0→1, 1→2, 2→0 + + result = apply_species_mapping(config, mapping) + + np.testing.assert_array_equal(result.vector, np.array([1, 2, 0, 1])) + self.assertEqual(result.count, 5) + + def test_apply_species_mapping_partial_swap(self): + """Swap first two of three species: 0↔1, keep 2.""" + config = Configuration([0, 1, 2, 1, 0]) + config.count = 10 + mapping = [1, 0, 2] # Swap 0↔1, keep 2 + + result = apply_species_mapping(config, mapping) + + np.testing.assert_array_equal(result.vector, np.array([1, 0, 2, 0, 1])) + self.assertEqual(result.count, 10) + + def test_apply_species_mapping_preserves_count(self): + """Degeneracy count should always be preserved.""" + config = Configuration([0, 1]) + config.count = 42 + mapping = [1, 0] + result = apply_species_mapping(config, mapping) + + self.assertEqual(result.count, 42) + + def test_enumerate_configurations_finds_unique_configs(self): + """Test that enumerate_configurations correctly identifies unique configurations.""" + s0 = SymmetryOperation.from_vector([1, 2, 3]) + s1 = SymmetryOperation.from_vector([2, 1, 3]) # Swap first two + sg = SymmetryGroup(symmetry_operations=[s0, s1]) + config_space = ConfigurationSpace(objects=[1, 2, 3], symmetry_group=sg) + + # [1,0,0] and [0,1,0] are equivalent under s1, should get 1 unique config + # [0,0,1] is different + permutations = iter([(1, 0, 0), (0, 1, 0), (0, 0, 1)]) + + unique_configs = config_space.enumerate_configurations(permutations) + + self.assertEqual(len(unique_configs), 2) # Two unique configs + + def test_enumerate_configurations_sets_correct_counts(self): + """Test that degeneracy counts are set correctly.""" + s0 = SymmetryOperation.from_vector([1, 2, 3]) + s1 = SymmetryOperation.from_vector([2, 1, 3]) + sg = SymmetryGroup(symmetry_operations=[s0, s1]) + config_space = ConfigurationSpace(objects=[1, 2, 3], symmetry_group=sg) + + permutations = iter([(1, 0, 0)]) + unique_configs = config_space.enumerate_configurations(permutations) + + # [1,0,0] has 2 equivalents: [1,0,0] and [0,1,0] + self.assertEqual(unique_configs[0].count, 2) + + if __name__ == '__main__': unittest.main() diff --git a/tests/unit_tests/test_interface_pymatgen.py b/tests/unit_tests/test_interface_pymatgen.py index bef7259..de9dfa2 100644 --- a/tests/unit_tests/test_interface_pymatgen.py +++ b/tests/unit_tests/test_interface_pymatgen.py @@ -4,45 +4,46 @@ from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Molecule, Structure from pymatgen.core.operations import SymmOp -from bsym.interface.pymatgen import ( unique_symmetry_operations_as_vectors_from_structure, - space_group_from_structure, - parse_site_distribution, - unique_structure_substitutions, - new_structure_from_substitution, - configuration_space_from_structure, - space_group_symbol_from_structure, - configuration_space_from_molecule, - structure_cartesian_coordinates_mapping, - molecule_cartesian_coordinates_mapping ) +from bsym.interface.pymatgen import (unique_symmetry_operations_as_vectors_from_structure, + space_group_from_structure, + parse_site_distribution, + unique_structure_substitutions, + new_structure_from_substitution, + configuration_space_from_structure, + space_group_symbol_from_structure, + configuration_space_from_molecule, + structure_cartesian_coordinates_mapping, + molecule_cartesian_coordinates_mapping, + unique_structure_substitutions_by_composition) from itertools import permutations from bsym import SymmetryOperation, Configuration, SpaceGroup, PointGroup, ConfigurationSpace -class TestPymatgenInterface( unittest.TestCase ): +class TestPymatgenInterface(unittest.TestCase): - def setUp( self ): + def setUp(self): # construct a pymatgen Structure instance using the site fractional coordinates # face-centered cubic lattice - coords = np.array( [ [ 0.0, 0.0, 0.0 ], - [ 0.5, 0.5, 0.0 ], - [ 0.0, 0.5, 0.5 ], - [ 0.5, 0.0, 0.5 ] ] ) - atom_list = [ 'Li' ] * len( coords ) - lattice = Lattice.from_parameters( a=3.0, b=3.0, c=3.0, alpha=90, beta=90, gamma=90 ) - self.structure = Structure( lattice, atom_list, coords ) + coords = np.array([[0.0, 0.0, 0.0], + [0.5, 0.5, 0.0], + [0.0, 0.5, 0.5], + [0.5, 0.0, 0.5]]) + atom_list = ['Li'] * len(coords) + lattice = Lattice.from_parameters(a=3.0, b=3.0, c=3.0, alpha=90, beta=90, gamma=90) + self.structure = Structure(lattice, atom_list, coords) # construct a pymatgen Molecule instance # square molecule (D4h) - m_coords = np.array( [ [ 0.0, 0.0, 0.0 ], - [ 1.0, 0.0, 0.0 ], - [ 0.0, 1.0, 0.0 ], - [ 1.0, 1.0, 0.0 ] ] ) - molecule = Molecule( atom_list, m_coords ) - molecule = Molecule( molecule.species, molecule.cart_coords - molecule.center_of_mass ) + m_coords = np.array([[0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [1.0, 1.0, 0.0]]) + molecule = Molecule(atom_list, m_coords) + molecule = Molecule(molecule.species, molecule.cart_coords - molecule.center_of_mass) self.molecule = molecule - def test_new_structure_from_substitution( self ): - substitution_index = [ 2,3 ] - new_species_list = [ 'Mg', 'Fe' ] + def test_new_structure_from_substitution(self): + substitution_index = [2,3] + new_species_list = ['Mg', 'Fe'] s_new = new_structure_from_substitution( self.structure, substitution_index, new_species_list ) self.assertEqual( s_new[2].species_string, 'Mg' ) self.assertEqual( s_new[3].species_string, 'Fe' ) @@ -59,28 +60,178 @@ def test_new_structure_from_substitution_raises_ValueError_with_invalid_index( s with self.assertRaises( ValueError ): new_structure_from_substitution( self.structure, substitution_index, new_species_list ) - def test_parse_site_distribution( self ): - site_distribution = { 'Mg': 1, 'Li': 3 } - n, d = parse_site_distribution( site_distribution ) + def test_parse_site_distribution(self): + site_distribution = {'Mg': 1, 'Li': 3} + n, d = parse_site_distribution(site_distribution) for k, v in n.items(): - self.assertEqual( site_distribution[ d[ k ] ], v ) + self.assertEqual(site_distribution[d[k]], v) - def test_structure_cartesian_coordinates_mapping( self ): - mock_symmop = Mock( spec=SymmOp ) - new_coords = np.array( [ [ 0.5, 0.5, 0.5 ] ] ) - mock_symmop.operate_multi = Mock( return_value=new_coords ) - self.structure.lattice.get_cartesian_coords = Mock( return_value=np.array( [ [ 2.0, 2.0, 2.0 ] ] ) ) - mapped_coords = structure_cartesian_coordinates_mapping( self.structure, mock_symmop ) - np.testing.assert_array_equal( mapped_coords, np.array( [ [ 2.0, 2.0, 2.0 ] ] ) ) - np.testing.assert_array_equal( mock_symmop.operate_multi.call_args[0][0], self.structure.frac_coords ) + def test_structure_cartesian_coordinates_mapping(self): + mock_symmop = Mock(spec=SymmOp) + new_coords = np.array([[0.5, 0.5, 0.5]]) + mock_symmop.operate_multi = Mock(return_value=new_coords) + self.structure.lattice.get_cartesian_coords = Mock(return_value=np.array([[2.0, 2.0, 2.0]])) + mapped_coords = structure_cartesian_coordinates_mapping(self.structure, mock_symmop) + np.testing.assert_array_equal(mapped_coords, np.array([[2.0, 2.0, 2.0]])) + np.testing.assert_array_equal(mock_symmop.operate_multi.call_args[0][0], self.structure.frac_coords) - def test_molecule_cartesian_coordinates_mapping( self ): - mock_symmop = Mock( spec=SymmOp ) - new_coords = np.array( [ [ 0.5, 0.5, 0,5 ] ] ) - mock_symmop.operate_multi = Mock( return_value=new_coords ) - mapped_coords = molecule_cartesian_coordinates_mapping( self.molecule, mock_symmop ) - np.testing.assert_array_equal( mapped_coords, new_coords ) - np.testing.assert_array_equal( mock_symmop.operate_multi.call_args[0][0], self.molecule.cart_coords ) + def test_molecule_cartesian_coordinates_mapping(self): + mock_symmop = Mock(spec=SymmOp) + new_coords = np.array([[0.5, 0.5, 0,5]]) + mock_symmop.operate_multi = Mock(return_value=new_coords) + mapped_coords = molecule_cartesian_coordinates_mapping(self.molecule, mock_symmop) + np.testing.assert_array_equal(mapped_coords, new_coords) + np.testing.assert_array_equal(mock_symmop.operate_multi.call_args[0][0], self.molecule.cart_coords) + + @patch('bsym.interface.pymatgen.configuration_space_from_structure') + @patch('bsym.interface.pymatgen.new_structure_from_substitution') + def test_unique_structure_substitutions_by_composition_calls_config_space_correctly( + self, mock_new_structure, mock_config_space_from_structure): + """Test that configuration space is created with correct parameters""" + mock_structure = Mock(spec=Structure) + mock_structure.indices_from_symbol = Mock(return_value=[0, 1, 2, 3]) + + mock_config_space = Mock(spec=ConfigurationSpace) + mock_config_space.unique_configurations_by_composition = Mock(return_value={}) + mock_config_space_from_structure.return_value = mock_config_space + + unique_structure_substitutions_by_composition( + mock_structure, + 'X', + ['Li', 'Na'], + atol=1e-6 + ) + + # Verify configuration_space_from_structure called with correct args + mock_config_space_from_structure.assert_called_once_with( + mock_structure, + subset=[0, 1, 2, 3], + atol=1e-6 + ) + + @patch('bsym.interface.pymatgen.configuration_space_from_structure') + def test_unique_structure_substitutions_by_composition_converts_bounds_to_indices( + self, mock_config_space_from_structure): + """Test that species name bounds are converted to numeric indices""" + mock_structure = Mock(spec=Structure) + mock_structure.indices_from_symbol = Mock(return_value=[0, 1, 2]) + + mock_config_space = Mock(spec=ConfigurationSpace) + mock_config_space.unique_configurations_by_composition = Mock(return_value={}) + mock_config_space_from_structure.return_value = mock_config_space + + bounds = {'Li': (1, 2), 'Na': (0, 2)} + + unique_structure_substitutions_by_composition( + mock_structure, + 'X', + ['Li', 'Na', 'Mg'], + bounds=bounds + ) + + # Verify bounds were converted: Li->0, Na->1 + call_kwargs = mock_config_space.unique_configurations_by_composition.call_args[1] + expected_bounds = {0: (1, 2), 1: (0, 2)} + self.assertEqual(call_kwargs['bounds'], expected_bounds) + + @patch('bsym.interface.pymatgen.configuration_space_from_structure') + def test_unique_structure_substitutions_by_composition_raises_error_for_invalid_species_in_bounds( + self, mock_config_space_from_structure): + """Test ValueError raised when bounds contain species not in species_list""" + mock_structure = Mock(spec=Structure) + mock_structure.indices_from_symbol = Mock(return_value=[0, 1, 2]) + + mock_config_space = Mock(spec=ConfigurationSpace) + mock_config_space_from_structure.return_value = mock_config_space + + bounds = {'K': (1, 2)} # K not in species_list + + with self.assertRaises(ValueError) as context: + unique_structure_substitutions_by_composition( + mock_structure, + 'X', + ['Li', 'Na'], + bounds=bounds + ) + + self.assertIn("'K'", str(context.exception)) + self.assertIn("not found in species_list", str(context.exception)) + + @patch('bsym.interface.pymatgen.configuration_space_from_structure') + @patch('bsym.interface.pymatgen.new_structure_from_substitution') + def test_unique_structure_substitutions_by_composition_maps_configs_to_structures( + self, mock_new_structure, mock_config_space_from_structure): + """Test that configurations are correctly mapped to structures with species""" + mock_structure = Mock(spec=Structure) + mock_structure.indices_from_symbol = Mock(return_value=[0, 1, 2]) + + # Create mock configurations + mock_config1 = Mock(spec=Configuration) + mock_config1.tolist = Mock(return_value=[0, 1, 0]) # Li, Na, Li + mock_config1.count = 3 + + mock_config2 = Mock(spec=Configuration) + mock_config2.tolist = Mock(return_value=[1, 1, 0]) # Na, Na, Li + mock_config2.count = 2 + + mock_config_space = Mock(spec=ConfigurationSpace) + mock_config_space.unique_configurations_by_composition = Mock( + return_value={(2, 1): [mock_config1, mock_config2]} + ) + mock_config_space_from_structure.return_value = mock_config_space + + mock_structure1 = Mock(spec=Structure) + mock_structure2 = Mock(spec=Structure) + mock_new_structure.side_effect = [mock_structure1, mock_structure2] + + results = unique_structure_substitutions_by_composition( + mock_structure, + 'X', + ['Li', 'Na'] + ) + + # Verify new_structure_from_substitution called with correct species + calls = mock_new_structure.call_args_list + self.assertEqual(len(calls), 2) + + # First call: [0, 1, 0] -> ['Li', 'Na', 'Li'] + self.assertEqual(calls[0][0][2], ['Li', 'Na', 'Li']) + + # Second call: [1, 1, 0] -> ['Na', 'Na', 'Li'] + self.assertEqual(calls[1][0][2], ['Na', 'Na', 'Li']) + + # Verify metadata was set + self.assertEqual(mock_structure1.number_of_equivalent_configurations, 3) + self.assertEqual(mock_structure2.number_of_equivalent_configurations, 2) + + # Verify results structure + self.assertEqual(len(results), 1) + self.assertIn((2, 1), results) + self.assertEqual(results[(2, 1)], [mock_structure1, mock_structure2]) + + @patch('bsym.interface.pymatgen.configuration_space_from_structure') + def test_unique_structure_substitutions_by_composition_passes_through_parameters( + self, mock_config_space_from_structure): + """Test that verbose and show_progress parameters are passed through""" + mock_structure = Mock(spec=Structure) + mock_structure.indices_from_symbol = Mock(return_value=[0, 1]) + + mock_config_space = Mock(spec=ConfigurationSpace) + mock_config_space.unique_configurations_by_composition = Mock(return_value={}) + mock_config_space_from_structure.return_value = mock_config_space + + unique_structure_substitutions_by_composition( + mock_structure, + 'X', + ['Li', 'Na'], + verbose=True, + show_progress='notebook' + ) + + call_kwargs = mock_config_space.unique_configurations_by_composition.call_args[1] + self.assertEqual(call_kwargs['verbose'], True) + self.assertEqual(call_kwargs['show_progress'], 'notebook') + if __name__ == '__main__': unittest.main() diff --git a/tests/unit_tests/test_partitions.py b/tests/unit_tests/test_partitions.py new file mode 100644 index 0000000..82d285b --- /dev/null +++ b/tests/unit_tests/test_partitions.py @@ -0,0 +1,169 @@ +# tests/unit_tests/test_partitions.py +import unittest +from bsym.partitions import generate_partitions +from bsym.partitions import compute_mapping_vector +from bsym.partitions import satisfies_bounds + + +class GeneratePartitionsTestCase(unittest.TestCase): + """Tests for generate_partitions function""" + + def test_generate_partitions_n4_k2(self): + """N=4, k=2 should give 3 partitions""" + result = list(generate_partitions(4, 2)) + expected = [ + (4, 0), + (3, 1), + (2, 2) + ] + self.assertEqual(result, expected) + + def test_generate_partitions_n4_k3(self): + """N=4, k=3 should give 4 partitions""" + result = list(generate_partitions(4, 3)) + expected = [ + (4, 0, 0), + (3, 1, 0), + (2, 2, 0), + (2, 1, 1) + ] + self.assertEqual(result, expected) + + def test_generate_partitions_n4_k4(self): + """N=4, k=4 should include uniform partition""" + result = list(generate_partitions(4, 4)) + expected = [ + (4, 0, 0, 0), + (3, 1, 0, 0), + (2, 2, 0, 0), + (2, 1, 1, 0), + (1, 1, 1, 1) + ] + self.assertEqual(result, expected) + + def test_partitions_are_tuples(self): + """Partitions should be returned as tuples""" + result = list(generate_partitions(3, 2)) + for partition in result: + self.assertIsInstance(partition, tuple) + + def test_partitions_have_correct_length(self): + """All partitions should have length k""" + result = list(generate_partitions(5, 3)) + for partition in result: + self.assertEqual(len(partition), 3) + + def test_partitions_sum_to_n(self): + """Each partition should sum to n""" + n, k = 6, 3 + result = list(generate_partitions(n, k)) + for partition in result: + self.assertEqual(sum(partition), n) + + def test_partitions_are_descending(self): + """Each partition should be sorted descending""" + result = list(generate_partitions(7, 4)) + for partition in result: + self.assertEqual(list(partition), sorted(partition, reverse=True)) + + def test_generate_partitions_n0_k2(self): + """N=0 should give single partition of zeros""" + result = list(generate_partitions(0, 2)) + expected = [(0, 0)] + self.assertEqual(result, expected) + + def test_generate_partitions_n1_k1(self): + """N=1, k=1 minimal case""" + result = list(generate_partitions(1, 1)) + expected = [(1,)] + self.assertEqual(result, expected) + + def test_generate_partitions_n_less_than_k(self): + """N