Source code for httk.external.pyspglib_ext

#
#    The high-throughput toolkit (httk)
#    Copyright (C) 2012-2015 Rickard Armiento
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
  pyspglib external module
"""

import os, sys

from httk.core import citation
citation.add_ext_citation('spglib / pyspglib', "(Author list to be added)")

from httk import config
from httk.external.command import Command
from httk.external.subimport import submodule_import_external
from httk.core.vectors import FracVector
from httk.atomistic import Structure, Spacegroup
from httk.atomistic.data.periodictable import atomic_symbol
import httk

try:
    pyspglib_path = config.get('paths', 'pyspglib')
except Exception:
    pyspglib_path = None

try:
    spglib_path = config.get('paths', 'spglib')
except Exception:
    spglib_path = None

pyspg_major_verion = None
pyspg_minor_verion = None

[docs] def ensure_pyspg_is_imported(): if pyspglib_path == "False" and spglib_path == "False": raise Exception("httk.external.ase_glue: module ase_glue imported, but ase is disabled in configuration file.") if pyspg_major_version is None: raise Exception("httk.external.pyspglib_ext imported, but could not access pyspg.")
try: if pyspglib_path is not None and spglib_path is not None: # Import spglib from pyspglib, with some magic to enable use of pyspglib.Atoms for the pyspglib ASE atoms emulation class pyspglib = submodule_import_external(os.path.join(pyspglib_path, 'lib', 'python'), 'pyspglib') from pyspglib import spglib atoms = submodule_import_external(os.path.join(spglib_path, 'python', 'ase', 'test'), 'atoms') spglib.Atoms = atoms.Atoms del atoms # Lets not pollute the namespace pyspg_major_version = spglib.__version__.split('.')[0] pyspg_minor_version = spglib.__version__.split('.')[1] else: try: external = config.get('general', 'allow_system_libs') except Exception: external = 'yes' if external == 'yes': # Note: this type of import will miss the spglib 'fake' atom object which is a problem. Probably should # not use this type of import. # NOTE: The 'fake' spglib Atoms object has become obsolete, so I have disabled # the warning below, because the warning causes tests to fail. import spglib # sys.stderr.write('WARNING: spglib imported in httk.external without any path given in httk.cfg, this means no spglib.Atoms object exists.\n') pyspg_major_version = spglib.__version__.split('.')[0] pyspg_minor_version = spglib.__version__.split('.')[1] else: pass except Exception: pass
[docs] def structure_to_spglib_atoms(struct): ensure_pyspg_is_imported() symbols = [] for i in range(len(struct.coordgroups)): name = httk.htdata.periodictable.atomic_symbol(struct.assignments[i]) symbols += [name]*struct.counts[i] print("SYMBOLS", symbols, struct.N) cell = struct.cell.to_floats() scaled_positions = struct.coords atoms = spglib.Atoms(symbols=symbols, cell=cell, scaled_positions=scaled_positions, pbc=True) return atoms
[docs] def analysis(struct, symprec=1e-5): ensure_pyspg_is_imported() atoms = structure_to_spglib_atoms(struct) val = spglib.get_spacegroup(atoms) print("Spacegroup is:", val) val = spglib.refine_cell(atoms, symprec=symprec) print("Primitive", val)
[docs] def primitive(struct, symprec=1e-5): ensure_pyspg_is_imported() atoms = structure_to_spglib_atoms(struct) prim = spglib.refine_cell(atoms, symprec=symprec) sg = spglib.get_spacegroup(atoms) struct = httk.iface.spglib_if.spglib_out_to_struct(prim) struct.comment = "Spacegroup: "+sg return struct
[docs] def struct_process_with_spglig(struct, symprec=1e-5): from httk.external.numpy_ext import numpy as np ensure_pyspg_is_imported() basis = struct.uc_basis.to_floats() coords = struct.uc_reduced_coords.to_floats() counts = struct.uc_counts symbols_int = [] index = 0 for a, count in zip(struct.assignments, counts): symbols_int += [a.atomic_number]*count index += 1 cell = (basis, coords, symbols_int) dataset = spglib.get_symmetry_dataset(cell, symprec=symprec) # print(dataset) hall_symbol = dataset['hall'] spacegroupnumber = dataset['number'] spacegroup = Spacegroup.create(hall_symbol=hall_symbol, spacegroupnumber=spacegroupnumber) rc_basis = dataset['std_lattice'].tolist() rc_pos = dataset['std_positions'].tolist() rc_reduced_occupationscoords = [] rc_occupancies = [] multiplicities = [] wyckoff_symbols = [] for ind in set(dataset['equivalent_atoms']): atom = symbols_int[ind] equiv_orbs_where = np.argwhere(dataset['equivalent_atoms'] == ind).flatten() prim_index = dataset['mapping_to_primitive'][ind] std_index = int(np.argwhere(dataset['std_mapping_to_primitive'] == prim_index).flatten()[0]) rc_reduced_occupationscoords.append(rc_pos[std_index]) rc_occupancies.append({'atom': atom, 'ratio': FracVector(1,1)}) multiplicities.append(len(equiv_orbs_where)) wyckoff_symbols.append(dataset['wyckoffs'][ind]) # Multiplicities have to be "normalized" if the input cell was a supercell # containing multiple primitive/standard conventional cells, or if # the input cell was the primitive unit cell, i.e. smaller than the # conventional cell. if sum(multiplicities) > len(dataset['std_mapping_to_primitive']): assert sum(multiplicities) % len(dataset['std_mapping_to_primitive']) == 0 multiplier = sum(multiplicities) // len(dataset['std_mapping_to_primitive']) for i in range(len(multiplicities)): multiplicities[i] //= multiplier elif sum(multiplicities) < len(dataset['std_mapping_to_primitive']): assert len(dataset['std_mapping_to_primitive']) % sum(multiplicities) == 0 multiplier = len(dataset['std_mapping_to_primitive']) // sum(multiplicities) for i in range(len(multiplicities)): multiplicities[i] *= multiplier assert sum(multiplicities) == len(dataset['std_mapping_to_primitive']) newstruct = Structure.create( spacegroup=spacegroup, rc_basis=rc_basis, rc_reduced_occupationscoords=rc_reduced_occupationscoords, rc_occupancies=rc_occupancies, wyckoff_symbols=wyckoff_symbols, multiplicities=multiplicities, ) newstruct.add_tags(struct.get_tags()) newstruct.add_refs(struct.get_refs()) return newstruct
[docs] def uc_reduced_coordgroups_process_with_spglib_TODO(coordgroup, cell, get_wyckoff=False): """ siteutils.py calls this function as uc_reduced_coordgroups_process_with_isotropy(coordgroups, cell, spacegroup, get_wyckoff=True), while structureutils.py calls it as uc_reduced_coordgroups_process_with_isotropy(coordgroups, basis). Mismatch between number of required arguments, not sure what to do. """ pass