Source code for flowws_unit_cell.CenterSpaceGroup

from collections import Counter
import flowws
from flowws import Argument as Arg
import numpy as np
import plato
import spglib

def identify_spacegroup(spg_cell, max_iterations=200, minimum_distance=.9):
    """This function aims to identify the best spacegroup
    based on allowing some amount of deviation. Accepts a spglib
    compatible cell tuple as the argument."""

    # Note that while precision to spglib is in cartesian distance,
    # input positions are fractional coordinates
    prec = 5*minimum_distance
    precs = []
    grps = []
    grp = 'None'
    highest_symmetry_group = 'None'
    max_group = 0

    # Try a range of precisions and record the determined spacegroups
    counter = 0
    while grp is None or grp.split()[-1] not in ['(1)', '(2)']:
        counter += 1
        if counter > max_iterations:
            break
        grp = spglib.get_spacegroup(spg_cell, symprec=prec)
        grps.append(grp)
        precs.append(prec)
        prec /= 2

        if grp is not None:
            group_num = int(grp.split()[-1].replace('(', '').replace(')', ''))
            if group_num > max_group:
                max_group = group_num
                highest_symmetry_group = grp

    if all(g is None for g in grps):
        raise ValueError("No symmetry groups found!")
    # One measure of best group is the highest symmetry group
    highest_symmetry_group_prec = precs[::-1][grps[::-1].index(highest_symmetry_group)]

    # An alternative measure is the most commonly occurring group
    counts = Counter(grps)
    if None in counts:
        del counts[None]
    most_common_group = counts.most_common(1)[0][0]
    most_common_group_prec = precs[::-1][grps[::-1].index(most_common_group)]

    return {'common': (most_common_group, most_common_group_prec),
            'highest': (highest_symmetry_group, highest_symmetry_group_prec),
            'histogram': counts}

def matrix_to_box(bm):
    """Convert a box matrix into a box object"""
    bm = np.asarray(bm)
    Lx, Ly, Lz = np.diag(bm).flatten().tolist()
    xy = bm[0, 1]/Ly
    xz = bm[0, 2]/Lz
    yz = bm[1, 2]/Lz
    box = (Lx, Ly, Lz, xy, xz, yz)

    return box

def standardize_cell(spg_cell, best_prec):
    """Convert cell into its standard crystallographic representation"""
    dataset = spglib.get_symmetry_dataset(spg_cell, best_prec)
    box = matrix_to_box(dataset['std_lattice'].T)
    positions = dataset['std_positions']

    return (box, positions, dataset['std_types'])

[docs]@flowws.add_stage_arguments class CenterSpaceGroup(flowws.Stage): """Attempt to automatically detect the space group of the system and center it accordingly.""" ARGS = [ Arg('minimum_distance', '-d', float, .9, help='Precision with which to merge points transformed by space group transformations'), Arg('use_types', '-t', bool, True, help='Use type information when centering the unit cell'), ]
[docs] def run(self, scope, storage): """Detect the space group and center the system.""" box = scope['box'] boxmat = plato.math.box_to_matrix(box) fractions = plato.math.make_fractions(box, scope['position']) types = scope['type'] if not self.arguments['use_types']: types = np.zeros_like(types) spglib_cell = (boxmat, fractions, types) try: self.spg_info = identify_spacegroup( spglib_cell, max_iterations=64, minimum_distance=self.arguments['minimum_distance']) except ValueError: # didn't work; don't display things return (box, fractions, types) = standardize_cell( spglib_cell, self.spg_info['common'][1]) scope['box'] = box scope['position'] = plato.math.fractions_to_coordinates(box, fractions) scope['type'] = types scope.setdefault('visuals', []).append(self)
def draw_matplotlib(self, figure): ax = figure.add_subplot() histogram = self.spg_info['histogram'] keys = list(histogram) counts = [histogram[k] for k in keys] xs = np.arange(len(keys)) ax.bar(xs, counts) ax.set_title('Space group prevalence') ax.set_xticks(xs) ax.set_xticklabels(keys) ax.set_ylabel('Count')