Esempio n. 1
0
def parse_symmetry(session, group, center=None, axis=None, molecule=None):

    # Handle products of symmetry groups.
    groups = group.split('*')
    from chimerax.geometry import Places
    ops = Places()
    for g in groups:
        ops = ops * group_symmetries(session, g, molecule)

    # Apply center and axis transformation.
    if center is not None or axis is not None:
        from chimerax.geometry import Place, vector_rotation, translation
        tf = Place()
        if center is not None and tuple(center) != (0, 0, 0):
            tf = translation([-c for c in center])
        if axis is not None and tuple(axis) != (0, 0, 1):
            tf = vector_rotation(axis, (0, 0, 1)) * tf
        if not tf.is_identity():
            ops = ops.transform_coordinates(tf)
    return ops
def unit_cell_and_sym_axes(session, unit_cell):
    cell = unit_cell.cell
    from chimerax.core.models import Model
    from chimerax.geometry import Places, Place
    from collections import defaultdict
    import numpy
    from math import sqrt
    from fractions import Fraction
    from chimerax.clipper.clipper_python import (Coord_frac, Coord_orth,
                                                 RTop_frac, Vec3_double as
                                                 Vec3, Symop, Mat33_double as
                                                 Mat33)

    axis_defs = defaultdict(lambda: list())

    m = Model('Unit cell and symmetry', session)

    # box_origin_frac = unit_cell.origin.coord_frac(unit_cell.grid)-Coord_frac([1,1,1])
    # box_origin_xyz = box_origin_frac.coord_orth(cell).as_numpy()
    box_size = unit_cell.grid.dim * 3
    syms = unit_cell.symops

    #syms = Places(place_array = unit_cell.all_symops_in_box(box_origin_xyz, box_size).all_matrices_orth(unit_cell.cell, '3x4'))

    #syms = Places(place_array = unit_cell.symops.all_matrices_orth(unit_cell.cell, '3x4'))
    identity = Mat33.identity()

    # Need to check all operators covering a 3x3 unit cell block to ensure we
    # get them all. Generate all possible length-3 combinations of -1, 0 and 1
    u = v = w = numpy.linspace(-1, 1, 3)
    frac_offsets = numpy.array(numpy.meshgrid(u, v, w)).T.reshape(-1, 3)
    frac_offsets = [Vec3(f) for f in frac_offsets]

    rot44 = numpy.identity(4)

    for sym_index, s in enumerate(syms):
        if s.rot.equals(identity, 1e-6):
            # No rotation, therefore no symmetry axis
            continue

        rot = s.rot
        trn = s.trn

        ss = Symop(s)

        so = ss.rtop_orth(cell)
        pr = Place(axes=so.rot.as_numpy().T)
        if pr.is_identity(tolerance=1e-5):
            continue
        central_axis, angle = pr.rotation_axis_and_angle()
        central_axis /= numpy.linalg.norm(central_axis)
        angle = abs(round(angle))
        if angle == 0:
            # Pure translation
            continue
        if angle not in (60, 90, 120, 180):
            err_str = (
                'This transform has a rotation angle of {} degrees, '
                'which is not compatible with crystallographic symmetry. '
                'In real crystals, only rotations yielding 2, 3, 4 or 6-fold '
                'symmetry are possible.').format(angle)
            raise RuntimeError(err_str)
        fold_symmetry = 360 // angle

        ca_frac = Coord_orth(100 * central_axis).coord_frac(cell).unit()

        #screw_component = Coord_orth(so.screw_translation).coord_frac(cell)
        # screw_component_orth = numpy.dot(so.trn.as_numpy(), central_axis) * central_axis
        # screw_component = Coord_orth(screw_component_orth).coord_frac(cell).lattice_copy_zero()
        #screw_component = Coord_frac((Vec3.dot(ss.trn, ca_frac) * ca_frac)).lattice_copy_zero()
        #print('Symop: {}, symmetry: {}, fractional_translation: {}, central_axis_orth: {}, central_axis_frac: {}, screw_component_frac: {}'.format(
        #    sym_index, fold_symmetry, ss.trn.as_numpy(), central_axis, ca_frac, screw_component))

        # screw_mag = numpy.linalg.norm(screw_component.as_numpy())
        # print('Screw magnitude before normalisation: {}'.format(screw_mag))
        # # if screw_mag > 1/12:
        # #     print('Non-zero screw translation vector: {} along axis: {} for fractional transform number {}: {}'.format(screw_component, central_axis, sym_index, s))
        # screw_mag = numpy.linalg.norm([float(Fraction(s).limit_denominator(12)) for s in screw_component.as_numpy()])*sqrt(3) # LCM of possible fractional screw translations 1/(2,3,4 or 6)
        # print('Screw magnitude after normalisation: {}'.format(screw_mag))

        for offset in frac_offsets:
            this_trn = trn + offset

            tf = RTop_frac(rot, this_trn).rtop_orth(unit_cell.cell)
            trn_orth = tf.trn.as_numpy()
            screw_orth = numpy.dot(trn_orth, central_axis) * central_axis
            perp_orth = trn_orth - screw_orth

            # Remove any whole-unit-cell translation component
            screw_frac = Coord_orth(screw_orth).coord_frac(
                cell).lattice_copy_zero().as_numpy()
            normalised_screw_frac = [
                Fraction(s).limit_denominator(12) for s in screw_frac
            ]
            normalised_screw_frac_float = numpy.array(
                [float(s) for s in normalised_screw_frac])
            screw_mag = numpy.linalg.norm(normalised_screw_frac_float)

            rot44[:3, :3] = tf.rot.as_numpy()
            rot44[:3, 3] = perp_orth

            origin, slope = rotation_axis(rot44)

            origin = Coord_orth(origin).coord_frac(cell).as_numpy()
            slope = Coord_orth(slope * 100).coord_frac(cell).as_numpy()

            axis_defs[(fold_symmetry, screw_mag)].append([origin, slope])

    plane_points, plane_normals = unit_cell_planes(unit_cell,
                                                   fractional_coords=True,
                                                   pad=0.001)
    plane_points = numpy.array([pp[0] for pp in plane_points])

    for (fold_symmetry, screw_component), axes in axis_defs.items():
        uvw0, uvw1 = plane_intersections_in_unit_cell(axes, plane_points,
                                                      plane_normals)
        if uvw0 is None:
            continue
        from chimerax.clipper.clipper_python import Coord_frac
        axyz0 = numpy.array([
            Coord_frac(uvw).coord_orth(unit_cell.cell).as_numpy()
            for uvw in uvw0
        ])
        axyz1 = numpy.array([
            Coord_frac(uvw).coord_orth(unit_cell.cell).as_numpy()
            for uvw in uvw1
        ])
        d = sym_axis_drawing(fold_symmetry, screw_component, axyz0, axyz1)
        if d is not None:
            m.add_drawing(d)

    bd = unit_cell_box_drawing(unit_cell_corners(unit_cell))
    m.add_drawing(bd)

    return m