Source code for bsym.interface.pymatgen

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 bsym import SpaceGroup, SymmetryOperation, ConfigurationSpace, PointGroup
from copy import copy
from functools import partial
import numpy as np

[docs]def structure_cartesian_coordinates_mapping( structure, symmop ): """ 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. Returns (np.array): The mapped Cartesian coordinates. """ return structure.lattice.get_cartesian_coords( symmop.operate_multi( structure.frac_coords ) )
[docs]def molecule_cartesian_coordinates_mapping( molecule, symmop ): """ 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. Returns (np.array): The mapped Cartesian coordinates. """ return symmop.operate_multi( molecule.cart_coords )
[docs]def structure_mapping_list( new_structure, mapping_structure, atol ): """ Gives the index mapping between two pymatgen ``Structure`` objects. Args: new_structure (``Structure``): mapping_structure (``Structure``): Returns: 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 )
[docs]def molecule_mapping_list( new_molecule, mapping_molecule, atol ): """ Gives the index mapping between two pymatgen ``Molecule`` objects. Args: new_structure (``Molecule``): mapping_structure (``Molecule``): Returns: 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 )
[docs]def unique_symmetry_operations_as_vectors_from_structure( structure, verbose=False, subset=None, atol=1e-5 ): """ 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. Returns: (list[list]): 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 verbose: print( "The space group for this structure is {}".format( 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 ) if verbose: print( "The point group for this structure is {}".format( symmetry_analyzer.get_pointgroup()) ) else: raise ValueError( 'structure argument should be a Structure or Molecule object' ) symmetry_operations = symmetry_analyzer.get_symmetry_operations() mappings = [] 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 ) 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 ) ) ] if new_mapping not in mappings: mappings.append( new_mapping ) return mappings
[docs]def space_group_symbol_from_structure( structure ): """ Returns the symbol for the space group defined by this structure. Args: structure (pymatgen ``Structure``): The input structure. Returns: (str): The space group symbol. """ symmetry_analyzer = SpacegroupAnalyzer( structure ) symbol = symmetry_analyzer.get_space_group_symbol() return symbol
[docs]def space_group_from_structure( structure, subset=None, atol=1e-5 ): """ 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. Returns: 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 )
[docs]def point_group_from_molecule( molecule, subset=None, atol=1e-5 ): """ 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. Returns: 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 )
[docs]def configuration_space_from_structure( structure, subset=None, atol=1e-5 ): """ 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. Returns: 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 ) if subset is None: subset = list( range( 1, len( structure )+1 ) ) config_space = ConfigurationSpace( objects=subset, symmetry_group=space_group ) return config_space
[docs]def configuration_space_from_molecule( molecule, subset=None, atol=1e-5 ): """ 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. Returns: 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 ) if subset is None: subset = list( range( 1, len( molecule )+1 ) ) config_space = ConfigurationSpace( objects=subset, symmetry_group=point_group ) return config_space
[docs]def unique_structure_substitutions( structure, to_substitute, site_distribution, verbose=False, atol=1e-5, show_progress=False ): """ 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. Returns: (list[Structure]): 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. 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 the `full_configuration_degeneracy` attribute. If the parent structure is a standard Pymatgen Structure object, `number_of_equivalent_configurations` and `full_configuration_degeneracy` will be equal. .. _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 ) 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 else: 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
[docs]def parse_site_distribution( site_distribution ): """ Converts a site distribution using species labels into one using integer labels. Args: site_distribution (dict): 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' }` """ numeric_site_distribution = {} numeric_site_mapping = {} 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
[docs]def new_structure_from_substitution( parent_structure, site_substitution_index, new_species_list ): """ 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. Returns: (``Structure``): The new pymatgen ``Structure``. 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 s = parent_structure.copy() for i, spec in zip( site_substitution_index, new_species_list ): s[i] = spec return s