예제 #1
0
def pdb_mtrix_matrices(pdb_headers, add_identity=True, given=False):

    h = pdb_headers
    have_matrix = ('MTRIX1' in h and 'MTRIX2' in h and 'MTRIX3' in h)
    if not have_matrix:
        if add_identity:
            from chimerax.geometry import identity
            return [identity()]
        else:
            return []

    row1_list = h['MTRIX1']
    row2_list = h['MTRIX2']
    row3_list = h['MTRIX3']
    if len(row1_list) != len(row2_list) or len(row2_list) != len(row3_list):
        if add_identity:
            from chimerax.geometry import identity
            return [identity()]
        else:
            return []

    row_triples = zip(row1_list, row2_list, row3_list)

    mlist = []
    from chimerax.geometry import Place
    for row_triple in row_triples:
        matrix = []
        for line in row_triple:
            try:
                mrow = [
                    float(f) for f in (line[10:20], line[20:30], line[30:40],
                                       line[45:55])
                ]
            except ValueError:
                break
            mgiven = (len(line) >= 60 and line[59] == '1')
            if (mgiven and given) or (not mgiven and not given):
                matrix.append(mrow)
        if len(matrix) == 3:
            mlist.append(Place(matrix))

    if add_identity:
        if len([m for m in mlist if m.is_identity()]) == 0:
            # Often there is no MTRIX identity entry
            from chimerax.geometry import identity
            mlist.append(identity())

    return Places(mlist)
예제 #2
0
def extrusion_transforms(path, tangents, yaxis=None):

    from chimerax.geometry import identity, vector_rotation, translation
    tflist = []
    if yaxis is None:
        # Make xy planes for coordinate frames at each path point not rotate
        # from one point to next.
        tf = identity()
        n0 = (0, 0, 1)
        for p1, n1 in zip(path, tangents):
            tf = vector_rotation(n0, n1) * tf
            tflist.append(translation(p1) * tf)
            n0 = n1
    else:
        # Make y-axis of coordinate frames at each point align with yaxis.
        from chimerax.geometry import normalize_vector, cross_product, Place
        for p, t in zip(path, tangents):
            za = t
            xa = normalize_vector(cross_product(yaxis, za))
            ya = cross_product(za, xa)
            tf = Place(
                ((xa[0], ya[0], za[0], p[0]), (xa[1], ya[1], za[1], p[1]),
                 (xa[2], ya[2], za[2], p[2])))
            tflist.append(tf)
    return tflist
예제 #3
0
 def draw(self, renderer, draw_pass):
     # TODO: Overlay drawing sets projection to identity which will defeat supersample
     #       image capture using pixel shifts that go into the projection matrix from the camera.
     r = renderer
     # Enable backface culling if discs have spaces between them so far side of
     # globe is not visible.
     r.enable_backface_culling(True)
     # TODO: Enabling depth test causes globe to vanish if silhouette edges turned on.  Why?
     # TODO: Maybe overlay drawing should always use a cleared depth buffer, so
     #       overlay cannot be obscured by real models?
     # Enable depth test so overlapped discs don't look like fish scales dependent
     # on order of drawing of discs.
     r.enable_depth_test(True)
     ox, oy, oz = self.offset
     w, h = r.render_size()
     whmax = max(w,h)
     sx, sy = h/whmax, w/whmax
     r.set_projection_matrix(((sx, 0, 0, 0), (0, sy, 0, 0), (0, 0, 1, 0),
                              (ox, oy, oz, 1)))
     rot = self.session.main_view.camera.position.zero_translation()
     r.set_view_matrix(rot.inverse())
     Drawing.draw(self, renderer, draw_pass)
     # Restore drawing settings so other overlays get expected state.
     from chimerax.geometry import identity
     r.set_view_matrix(identity())
     r.set_projection_matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0),
                              (0, 0, 0, 1)))
     r.enable_depth_test(False)
     r.enable_backface_culling(False)
예제 #4
0
def motion_to_maximum(points,
                      point_weights,
                      volume,
                      max_steps=2000,
                      ijk_step_size_min=0.01,
                      ijk_step_size_max=0.5,
                      optimize_translation=True,
                      optimize_rotation=True,
                      metric='sum product',
                      symmetries=[],
                      request_stop_cb=None):
    '''
    Use gradient ascent to rigidly rotate and shift a set of points within a
    density map to maximize the average interpolated density value at the point
    positions.  Returns a Place object giving the motion of the points to the
    local maximum. The point coordinates are not modified.
    Optionally the correlation value or correlation about mean can
    be optimized.  Copies of the point set can be included in the optimization
    by specifying a list of Place objects for the symmetries argument.
    '''

    from chimerax.geometry import identity
    data_array, xyz_to_ijk_transform = \
        volume.matrix_and_transform(identity(), subregion = None, step = 1)
    move_tf, stats = \
             locate_maximum(points, point_weights,
                            data_array, xyz_to_ijk_transform,
                            max_steps, ijk_step_size_min, ijk_step_size_max,
                            optimize_translation, optimize_rotation,
                            metric, None, symmetries, request_stop_cb)
    return move_tf, stats
예제 #5
0
def atoms_outside_contour(atoms, volume=None):

    if volume is None:
        from chimerax.map import active_volume
        volume = active_volume()
    points = atom_coordinates(atoms)
    from chimerax.geometry import identity
    poc, clevel = points_outside_contour(points, identity(), volume)
    return poc, clevel
예제 #6
0
 def initial_camera_view(self, pad=0.05, set_pivot=True):
     '''Set the camera position to show all displayed drawings,
     looking down the z axis.'''
     b = self.drawing_bounds()
     if b is None:
         return
     c = self.camera
     from chimerax.geometry import identity
     c.position = identity()
     c.view_all(b, window_size=self.window_size, pad=pad)
     if set_pivot:
         self._center_of_rotation = cr = b.center()
         self._update_center_of_rotation = True
예제 #7
0
def _contacting_transforms(structures, transforms, distance):

    from numpy import concatenate, float32
    points = concatenate([s.atoms.scene_coords
                          for s in structures]).astype(float32)
    from chimerax.geometry import identity, find_close_points_sets, Places
    ident = identity().matrix.astype(float32)
    orig_points = [(points, ident)]
    tfnear = Places([
        tf for tf in transforms if len(
            find_close_points_sets(orig_points, [(
                points, tf.matrix.astype(float32))], distance)[0][0]) > 0
    ])
    return tfnear
예제 #8
0
def map_fitting_points(v, envelope, local_coords=False, include_zeros=False):

    from chimerax.geometry import identity
    point_to_scene_transform = None if local_coords else identity()
    from . import fitmap as F
    try:
        points, point_weights = F.map_points_and_weights(
            v, envelope, point_to_scene_transform, include_zeros=include_zeros)
    except (MemoryError, ValueError) as e:
        raise UserError('Out of memory, too many points in %s' % v.name)

    if len(points) == 0:
        msg = ('No grid points above map threshold.'
               if envelope else 'Map has no non-zero values.')
        raise UserError(msg)

    return points, point_weights
예제 #9
0
def average_map_value_at_atom_positions(atoms, volume=None):

    if volume is None:
        from chimerax.map import active_volume
        volume = active_volume()

    points = atom_coordinates(atoms)

    if volume is None or len(points) == 0:
        return 0, len(points)

    from chimerax.geometry import identity
    data_array, xyz_to_ijk_transform = \
        volume.matrix_and_transform(identity(), subregion = None, step = 1)

    amv, npts = average_map_value(points, xyz_to_ijk_transform, data_array)
    return amv, npts
예제 #10
0
 def __init__(self, session, filename):
     from chimerax.core import generic3d
     self.model = generic3d.Generic3DModel(filename, session)
     self.session = session
     self.shapes = []
     # parse input
     self.warned = set()
     self.lineno = 0
     self.transforms = [identity()]
     self.pure = [True]  # True if corresponding transform has pure rotation
     self.cur_color = [1.0, 1.0, 1.0, 1.0]
     self.cur_transparency = 0
     self.cur_pos = array([0.0, 0.0, 0.0])
     self.cur_pos_is_move = True  # set whenever cur_pos is set
     self.cur_char_pos = array([0.0, 0.0, 0.0])
     self.cur_font = ['SANS', 12, 'PLAIN']
     self.cur_description = None
     self.cur_atoms = None
     self.num_objects = 0
     self.LINE_RADIUS = 0.08
예제 #11
0
    def __init__(self,
                 name,
                 hkls,
                 vals,
                 dim_scale=2.0,
                 highlight_negatives=True):
        super().__init__(name)
        from chimerax.surface.shapes import sphere_geometry2
        self.set_geometry(*sphere_geometry2(80))
        import numpy
        from chimerax.geometry import Places, Place, identity
        id_axes = identity().axes()
        abs_vals = numpy.abs(vals)
        import numpy
        place_array = numpy.zeros((len(hkls), 3, 4))
        place_array[:, :, 3] = hkls * dim_scale
        place_array[:, :, :3] = id_axes
        for i in range(3):
            # Volume scales with value, so radius scales with cube root of value
            place_array[:, i, i] *= abs_vals**(1 / 3)

        # positions =[Place(origin=hkl*dim_scale, axes=id_axes*max(rval, 0))
        #     for (hkl, rval) in zip(hkls, vals)
        # ]
        if highlight_negatives:
            import numpy
            negatives = numpy.argwhere(vals < 0).flatten()
            place_array[negatives, :, :3] = id_axes
            # for ni in negatives:
            #     positions[ni] = Place(origin=positions[ni].origin(), axes=id_axes)

        positions = Places(place_array=place_array)

        self.set_positions(Places(positions))
        from chimerax.core.colors import BuiltinColormaps
        cmap = BuiltinColormaps['rdylbu'].linear_range(-1, 1)
        colors = cmap.interpolated_rgba8(vals)
        if highlight_negatives:
            colors[vals < 0] = [255, 0, 0, 255]
        self.set_colors(colors)
예제 #12
0
def extend_crystal_map(volarray, ijk_cell_size, ijk_symmetries, out_array,
                       out_ijk_to_vol_ijk_transform):

    ksz, jsz, isz = out_array.shape
    otf = out_ijk_to_vol_ijk_transform
    from chimerax.geometry import identity
    itf = identity()
    nsym = len(ijk_symmetries)
    from numpy import empty, float32, delete as delete_array_elements
    values = empty((nsym, ), float32)
    nnc = 0  # Number of grid points not covered by symmetry
    dmax = None  # Maximum value discrepancy for multiple overlaps
    from chimerax.map_data import interpolate_volume_data
    for k in range(ksz):
        for j in range(jsz):
            for i in range(isz):
                vijk = otf * (i, j, k)
                vlist = []
                for sym in ijk_symmetries:
                    svijk = sym * vijk
                    scijk = [p % s for p, s in zip(svijk, ijk_cell_size)
                             ]  # Wrap to unit cell.
                    vlist.append(scijk)
                values[:] = 0
                outside = interpolate_volume_data(vlist, itf, volarray,
                                                  'linear', values)[1]
                n = len(values) - len(outside)
                if n >= 1:
                    out_array[k, j, i] = float(values.sum()) / n
                    if n >= 2:
                        # Record maximum value discrepancy for multiple overlaps
                        vinside = delete_array_elements(values, outside)
                        d = vinside.max() - vinside.min()
                        if dmax is None or d > dmax:
                            dmax = d
                else:
                    nnc += 1  # Count grid points not covered by symmetry.

    return nnc, dmax
예제 #13
0
def fit_search(models, points, point_weights, volume, n,
               rotations = True, shifts = True, radius = None,
               angle_tolerance = 6, shift_tolerance = 3,
               asymmetric_unit = True,
               minimum_points_in_contour = 0.5,
               metric = 'sum product',
               optimize_translation = True, optimize_rotation = True,
               max_steps = 2000,
               ijk_step_size_min = 0.01, ijk_step_size_max = 0.5,
               request_stop_cb = None):

    bounds = volume.surface_bounds()
    if bounds is None:
        xyz_min, xyz_max = volume.xyz_bounds(step = 1)
        from chimerax.geometry import Bounds
        bounds = Bounds(xyz_min, xyz_max)

    asym_center_f = (.75,.55,.55)
    asym_center = tuple(x0 + (x1-x0)*f
                        for x0, x1, f in zip(bounds.xyz_min, bounds.xyz_max, asym_center_f)) 

    from chimerax.geometry import translation, identity
    center = points.mean(axis=0)
    ctf = translation(-center)
    ijk_to_xyz_tf = volume.matrix_indices_to_xyz_transform(step = 1)
    xyz_to_ijk_tf = ijk_to_xyz_tf.inverse()
    data_array = volume.matrix(step = 1)
    vtfinv = volume.position.inverse()
    mtv_list = [vtfinv * m.position for m in models]

    flist = []
    outside = 0
    from math import pi
    from chimerax.geometry import bins
    b = bins.Binned_Transforms(angle_tolerance*pi/180, shift_tolerance, center)
    fo = {}
    from .fitmap import locate_maximum
    for i in range(n):
        if request_stop_cb and request_stop_cb('Fit %d of %d' % (i+1,n)):
            break
        shift = ((random_translation(bounds) if radius is None
                  else random_translation_step(center, radius)) if shifts
                  else translation(center))
        rot = random_rotation() if rotations else identity()
        tf = shift * rot * ctf
        optimize = True
        max_opt = 2
        while optimize and max_opt > 0:
            p_to_ijk_tf = xyz_to_ijk_tf * tf
            move_tf, stats = \
              locate_maximum(points, point_weights, data_array, p_to_ijk_tf,
                             max_steps, ijk_step_size_min, ijk_step_size_max,
                             optimize_translation, optimize_rotation,
                             metric, request_stop_cb = None)
            ptf = tf * move_tf
            optimize = False
            max_opt -= 1
            if asymmetric_unit:
                atf = unique_symmetry_position(ptf, center, asym_center,
                                               volume.data.symmetries)
                if not atf is ptf:
                    ptf = tf = atf
                    optimize = not b.close_transforms(ptf)
        close = b.close_transforms(ptf)
        if len(close) == 0:
            transforms = [ptf * mtv for mtv in mtv_list]
            stats['hits'] = 1
            f = Fit(models, transforms, volume, stats)
            f.ptf = ptf
            flist.append(f)
            b.add_transform(ptf)
            fo[id(ptf)] = f
        else:
            s = fo[id(close[0])].stats
            s['hits'] += 1

    # Filter out solutions with too many points outside volume contour.
    fflist = [f for f in flist if (in_contour(f.ptf, points, volume, f.stats)
                                   >= minimum_points_in_contour)]
    fset = set(fflist)
    outside = sum([f.stats['hits'] for f in flist if not f in fset])
    
    # Sort fits by correlation, then average map value.
    fflist.sort(key = fit_order)
    fflist.reverse()    # Best first

    return fflist, outside
예제 #14
0
def write_mol2(session,
               file_name,
               *,
               models=None,
               atoms=None,
               status=None,
               anchor=None,
               rel_model=None,
               sybyl_hyd_naming=True,
               combine_models=False,
               skip_atoms=None,
               res_num=False,
               gaff_type=False,
               gaff_fail_error=None):
    """Write a Mol2 file.

    Parameters
    ----------

    file_name : str, or file object open for writing
        Output file.

    models : a list/tuple/set of models (:py:class:`~chimerax.atomic.Structure`s)
        or a single :py:class:`~chimerax.atomic.Structure`
        The structure(s) to write out. If None (and 'atoms' is also None) then
        write out all structures.

    atoms : an :py:class:`~chimerax.atomic.Atoms` collection or None.  If not None,
        then 'models' must be None.

    status : function or None
        If not None, a function that takes a string -- used to report the progress of the write.

    anchor : :py:class:`~chimerax.atomic.Atoms` collection
        Atoms (and their implied internal bonds) that should be written out to the
        @SET section of the file as the rigid framework for flexible ligand docking.

    rel_model : Model whose coordinate system the coordinates should be written out reletive to,
        i.e. take the output atoms' coordinates and apply the inverse of the rel_model's transform.

    sybyl_hyd_naming : bool
        Controls whether hydrogen names should be "Sybyl-like" or "PDB-like" -- e.g.  HG21 vs. 1HG2.

    combine_models : bool
        Controls whether multiple structures will be combined into a single @MOLECULE
        section (value: True) or each given its own section (value: False).

    skip_atoms : list/set of :py:class:`~chimerax.atomic.Atom`s or an :py:class:`~chimerax.atomic.Atoms` collection or None
       Atoms to not output

    res_num : bool
        Controls whether residue sequence numbers are included in the substructure name.
        Since Sybyl Mol2 files include them, this defaults to True.

    gaff_type : bool
       If 'gaff_type' is True, outout GAFF atom types instead of Sybyl atom types.
       `gaff_fail_error`, if specified, is the type of error to throw (e.g. UserError)
       if there is no gaff_type attribute for an atom, otherwise throw the standard AttributeError.
    """

    if status:
        status("Writing Mol2 file %s" % file_name)

    from chimerax import io
    f = io.open_output(file_name, "utf-8")

    sort_key_func = serial_sort_key = lambda a, ri={}: write_mol2_sort_key(
        a, res_indices=ri)

    from chimerax.atomic import Structure, Atoms, Residue

    class JPBGroup:
        def __init__(self, atoms):
            atom_set = set(atoms)
            pbs = []
            for s in atoms.unique_structures:
                pbg = s.pbg_map.get(s.PBG_METAL_COORDINATION, None)
                if not pbg:
                    continue
                for pb in pbg.pseudobonds:
                    if pb.atoms[0] in atom_set and pb.atoms[1] in atom_set:
                        pbs.append(pb)
            self._pbs = pbs

        @property
        def pseudobonds(self):
            return self._pbs

    if models is None:
        if atoms is None:
            structures = session.models.list(type=Structure)
        else:
            structures = atoms
    else:
        if atoms is None:
            if isinstance(models, Structure):
                structures = [models]
            else:
                structures = [m for m in models if isinstance(m, Structure)]
        else:
            raise ValueError(
                "Cannot specify both 'models' and 'atoms' keywords")

    if isinstance(structures, Atoms):

        class Jumbo:
            def __init__(self, atoms):
                self.atoms = atoms
                self.residues = atoms.unique_residues
                self.bonds = atoms.intra_bonds
                self.name = "(selection)"
                self.pbg_map = {
                    Structure.PBG_METAL_COORDINATION: JPBGroup(atoms)
                }

        structures = [Jumbo(structures)]
        sort_key_func = lambda a: (a.structure.id, ) + serial_sort_key(a)
        combine_models = False

    # transform...
    if rel_model is None:
        from chimerax.geometry import identity
        xform = identity()
    else:
        xform = rel_model.scene_position.inverse()

    # need to find amide moieties since Sybyl has an explicit amide type
    if status:
        status("Finding amides")
    from chimerax.chem_group import find_group
    amides = find_group("amide", structures)
    amide_Ns = set([amide[2] for amide in amides])
    amide_CNs = set([amide[0] for amide in amides])
    amide_CNs.update(amide_Ns)
    amide_Os = set([amide[1] for amide in amides])

    substructure_names = None
    if combine_models and len(structures) > 1:
        # create a fictitious jumbo model
        class Jumbo:
            def __init__(self, structures):
                self.name = structures[0].name + " (combined)"
                from chimerax.atomic import concatenate
                self.atoms = concatenate([s.atoms for s in structures])
                self.bonds = concatenate([s.bonds for s in structures])
                self.residues = concatenate([s.residues for s in structures])
                self.pbg_map = {
                    Structure.PBG_METAL_COORDINATION: JPBGroup(self.atoms)
                }
                # if combining single-residue structures,
                # can be more informative to use model name
                # instead of residue type for substructure
                if len(structures) == len(self.residues):
                    rnames = self.residues.names
                    if len(set(rnames)) < len(rnames):
                        snames = [s.name for s in structures]
                        if len(set(snames)) == len(snames):
                            self.substructure_names = dict(
                                zip(self.residues, snames))

        structures = [Jumbo(structures)]
        if hasattr(structures[-1], 'substructure_names'):
            substructure_names = structures[-1].substructure_names
            delattr(structures[-1], 'substructure_names')
        sort_key_func = lambda a: (a.structure.id, ) + serial_sort(a)

    # write out structures
    for struct in structures:
        if hasattr(struct, 'mol2_comments'):
            for m2c in struct.mol2_comments:
                print(m2c, file=f)
        if hasattr(struct, 'solvent_info'):
            print(struct.solvent_info, file=f)

        # molecule section header
        print("%s" % MOLECULE_HEADER, file=f)

        # molecule name
        print("%s" % struct.name, file=f)

        atoms = list(struct.atoms)
        bonds = list(struct.bonds)
        # add metal-coordination bonds
        coord_grp = struct.pbg_map.get(Structure.PBG_METAL_COORDINATION, None)
        if coord_grp:
            bonds.extend(list(coord_grp.pseudobonds))
        if skip_atoms:
            skip_atoms = set(skip_atoms)
            atoms = [a for a in atoms if a not in skip_atoms]
            bonds = [
                b for b in bonds if b.atoms[0] not in skip_atoms
                and b.atoms[1] not in skip_atoms
            ]
        residues = struct.residues

        # Put the atoms in the order we want for output
        if status:
            status("Putting atoms in input order")
        atoms.sort(key=sort_key_func)

        # if anchor is not None, then there will be two entries in
        # the @SET section of the file...
        if anchor:
            sets = 2
        else:
            sets = 0
        # number of entries for various sections...
        print("%d %d %d 0 %d" % (len(atoms), len(bonds), len(residues), sets),
              file=f)

        # type of molecule
        if hasattr(struct, "mol2_type"):
            mtype = struct.mol2_type
        else:
            mtype = "SMALL"
            from chimerax.atomic import Sequence
            for r in struct.residues:
                if Sequence.protein3to1(r.name) != 'X':
                    mtype = "PROTEIN"
                    break
                if Sequence.nucleic3to1(r.name) != 'X':
                    mtype = "NUCLEIC_ACID"
                    break
        print(mtype, file=f)

        # indicate type of charge information
        if hasattr(struct, 'charge_model'):
            print(struct.charge_model, file=f)
        else:
            print("NO_CHARGES", file=f)

        if hasattr(struct, 'mol2_comment'):
            print("\n%s" % struct.mol2_comment, file=f)
        else:
            print("\n", file=f)

        if status:
            status("writing atoms")
        # atom section header
        print("%s" % ATOM_HEADER, file=f)

        # make a dictionary of residue indices so that we can do quick look ups
        res_indices = {}
        for i, r in enumerate(residues):
            res_indices[r] = i + 1
        for i, atom in enumerate(atoms):
            # atom ID, starting from 1
            print("%7d" % (i + 1), end=" ", file=f)

            # atom name, possibly rearranged if it's a hydrogen
            if sybyl_hyd_naming and not atom.name[0].isalpha():
                atom_name = atom.name[1:] + atom.name[0]
            else:
                atom_name = atom.name
            print("%-8s" % atom_name, end=" ", file=f)

            # use correct relative coordinate position
            coord = xform * atom.scene_coord
            print("%9.4f %9.4f %9.4f" % tuple(coord), end=" ", file=f)

            # atom type
            if gaff_type:
                try:
                    atom_type = atom.gaff_type
                except AttributeError:
                    if not gaff_fail_error:
                        raise
                    raise gaff_fail_error(
                        "%s has no Amber/GAFF type assigned.\n"
                        "Use the AddCharge tool to assign Amber/GAFF types." %
                        atom)
            elif hasattr(atom, 'mol2_type'):
                atom_type = atom.mol2_type
            elif atom in amide_Ns:
                atom_type = "N.am"
            elif atom.structure_category == "solvent" \
            and atom.residue.name in Residue.water_res_names:
                if atom.element.name == "O":
                    atom_type = "O.t3p"
                else:
                    atom_type = "H.t3p"
            elif atom.element.name == "N" and len(
                [r for r in atom.rings() if r.aromatic]) > 0:
                atom_type = "N.ar"
            elif atom.idatm_type == "C2" and len(
                [nb for nb in atom.neighbors if nb.idatm_type == "Ng+"]) > 2:
                atom_type = "C.cat"
            elif sulfur_oxygen(atom):
                atom_type = "O.2"
            else:
                try:
                    atom_type = chimera_to_sybyl[atom.idatm_type]
                except KeyError:
                    session.logger.warning(
                        "Atom whose IDATM type has no equivalent"
                        " Sybyl type: %s (type: %s)" % (atom, atom.idatm_type))
                    atom_type = str(atom.element)
            print("%-5s" % atom_type, end=" ", file=f)

            # residue-related info
            res = atom.residue

            # residue index
            print("%5d" % res_indices[res], end=" ", file=f)

            # substructure identifier and charge
            if hasattr(atom, 'charge') and atom.charge is not None:
                charge = atom.charge
            else:
                charge = 0.0
            if substructure_names:
                rname = substructure_names[res]
            elif res_num:
                rname = "%3s%-5d" % (res.name, res.number)
            else:
                rname = "%3s" % res.name
            print("%s %9.4f" % (rname, charge), file=f)

        if status:
            status("writing bonds")
        # bond section header
        print("%s" % BOND_HEADER, file=f)

        # make an atom-index dictionary to speed lookups
        atom_indices = {}
        for i, a in enumerate(atoms):
            atom_indices[a] = i + 1
        for i, bond in enumerate(bonds):
            a1, a2 = bond.atoms

            # ID
            print("%6d" % (i + 1), end=" ", file=f)

            # atom IDs
            print("%4d %4d" % (atom_indices[a1], atom_indices[a2]),
                  end=" ",
                  file=f)

            # bond order; give it our best shot...
            if hasattr(bond, 'mol2_type'):
                print(bond.mol2_type, file=f)
                continue
            amide_A1 = a1 in amide_CNs
            amide_A2 = a2 in amide_CNs
            if amide_A1 and amide_A2:
                print("am", file=f)
                continue
            if amide_A1 or amide_A2:
                if a1 in amide_Os or a2 in amide_Os:
                    print("2", file=f)
                else:
                    print("1", file=f)
                continue

            aromatic = False
            # 'bond' might be a metal-coordination bond so do a test for rings
            if hasattr(bond, 'rings'):
                for ring in bond.rings():
                    if ring.aromatic:
                        aromatic = True
                        break
            if aromatic:
                print("ar", file=f)
                continue

            try:
                geom1 = idatm_info[a1.idatm_type].geometry
            except KeyError:
                print("1", file=f)
                continue
            try:
                geom2 = idatm_info[a2.idatm_type].geometry
            except KeyError:
                print("1", file=f)
                continue
            # sulfone/sulfoxide is classically depicted as double-
            # bonded despite the high dipolar character of the
            # bond making it have single-bond character.  For
            # output, use the classical values.
            if sulfur_oxygen(a1) or sulfur_oxygen(a2):
                print("2", file=f)
                continue
            if geom1 not in [2, 3] or geom2 not in [2, 3]:
                print("1", file=f)
                continue
            # if either endpoint atom is in an aromatic ring and
            # the bond isn't, it's a single bond...
            for endp in [a1, a2]:
                aromatic = False
                for ring in endp.rings():
                    if ring.aromatic:
                        aromatic = True
                        break
                if aromatic:
                    break
            else:
                # neither endpoint in aromatic ring
                if geom1 == 2 and geom2 == 2:
                    print("3", file=f)
                else:
                    print("2", file=f)
                continue
            print("1", file=f)

        if status:
            status("writing residues")
        # residue section header
        print("%s" % SUBSTR_HEADER, file=f)

        for i, res in enumerate(residues):
            # residue id field
            print("%6d" % (i + 1), end=" ", file=f)

            # residue name field
            if substructure_names:
                rname = substructure_names[res]
            elif res_num:
                rname = "%3s%-4d" % (res.name, res.number)
            else:
                rname = "%3s" % res.name
            print(rname, end=" ", file=f)

            # ID of the root atom of the residue
            chain_atom = res.principal_atom
            if chain_atom is None:
                # if writing out a selection, not all residue atoms
                # might be in atom_indices...
                for chain_atom in res.atoms:
                    if chain_atom in atom_indices:
                        break
            print("%5d" % atom_indices[chain_atom], end=" ", file=f)

            print("RESIDUE           4", end=" ", file=f)

            # Sybyl seems to use chain 'A' when chain ID is blank,
            # so run with that
            chain_id = res.chain_id
            if not chain_id.strip():
                chain_id = 'A'
            print("%-4s  %3s" % (chain_id, res.name), end=" ", file=f)

            # number of out-of-substructure bonds
            cross_res_bonds = 0
            for a in res.atoms:
                for nb in a.neighbors:
                    if nb.residue != res:
                        cross_res_bonds += 1
            print("%5d" % cross_res_bonds, end="", file=f)
            # print "ROOT" if first or only residue of a chain
            if not res.chain or res.chain.existing_residues[0] == res:
                print(" ROOT", file=f)
            else:
                print(file=f)

        # write flexible ligand docking info
        if anchor:
            if status:
                status("writing anchor info")
            print("%s" % SET_HEADER, file=f)
            atom_indices = {}
            for i, a in enumerate(atoms):
                atom_indices[a] = i + 1
            bond_indices = {}
            for i, b in enumerate(bonds):
                bond_indices[b] = i + 1
            print(
                "ANCHOR          STATIC     ATOMS    <user>   **** Anchor Atom Set",
                file=f)
            print(len(anchor), end=" ", file=f)
            for a in anchor:
                if a in atom_indices:
                    print(atom_indices[a], end=" ", file=f)
            print(file=f)

            print(
                "RIGID           STATIC     BONDS    <user>   **** Rigid Bond Set",
                file=f)
            bonds = anchor.intra_bonds
            print(len(bonds), end=" ", file=f)
            for b in bonds:
                if b in bond_indices:
                    print(bond_indices[b], end=" ", file=f)
            print(file=f)

    if file_name != f:
        f.close()

    if status:
        status("Wrote Mol2 file %s" % file_name)
예제 #15
0
def coordinate_system_transform(from_cs, to_cs):

    global cst
    if cst:
        return cst[(from_cs, to_cs)]

    transform = cst

    e = icosahedron_edge_length()  # Triangle edge length of unit icosahedron.

    from math import sqrt
    s25 = e / 2  # Sin/Cos for angle between 2-fold and 5-fold axis
    c25 = sqrt(1 - s25 * s25)
    s35 = e / sqrt(3)  # Sin/Cos for angle between 3-fold and 5-fold axis
    c35 = sqrt(1 - s35 * s35)

    from chimerax.geometry import Place
    transform[('2n5', '222')] = Place(
        ((1, 0, 0, 0), (0, c25, -s25, 0), (0, s25, c25, 0)))
    transform[('2n5', '2n3')] = Place(
        ((1, 0, 0, 0), (0, c35, s35, 0), (0, -s35, c35, 0)))

    # Axes permutations.
    # 90 degree rotation about z:
    transform[('222', '222r')] = Place(
        ((0, 1, 0, 0), (-1, 0, 0, 0), (0, 0, 1, 0)))
    # 180 degree rotation about y:
    transform[('2n3', '2n3r')] = \
        transform[('2n5', '2n5r')] = Place(((-1, 0, 0, 0),
                                           (0, 1, 0, 0),
                                           (0, 0, -1, 0)))
    # 180 degree rotation about x:
    transform[('n25', 'n25r')] = Place(
        ((1, 0, 0, 0), (0, -1, 0, 0), (0, 0, -1, 0)))
    # x <-> y and z -> -z:
    transform[('n25', '2n5')] = Place(
        ((0, 1, 0, 0), (1, 0, 0, 0), (0, 0, -1, 0)))

    # Extend to all pairs of transforms.
    tlist = []
    while len(transform) > len(tlist):

        tlist = transform.keys()

        # Add inverse transforms
        for f, t in tlist:
            if not (t, f) in transform:
                transform[(t, f)] = transform[(f, t)].inverse()

        # Use transitivity
        for f1, t1 in tlist:
            for f2, t2 in tlist:
                if f2 == t1 and f1 != t2 and not (f1, t2) in transform:
                    transform[(f1, t2)] = transform[(f2, t2)] \
                        * transform[(f1, t1)]

    from chimerax.geometry import identity
    i = identity()
    for s in coordinate_system_names:
        transform[(s, s)] = i

    return transform[(from_cs, to_cs)]
예제 #16
0
def fit_sequence(models,
                 volume,
                 steps,
                 subtract_maps=[],
                 envelope=True,
                 include_zeros=False,
                 metric='overlap',
                 optimize_translation=True,
                 optimize_rotation=True,
                 max_steps=2000,
                 ijk_step_size_min=0.01,
                 ijk_step_size_max=0.5,
                 request_stop_cb=None,
                 log=None):

    from chimerax.geometry import identity
    data_array, xyz_to_ijk_transform = \
        volume.matrix_and_transform(identity(), subregion = None, step = 1)

    size = tuple(data_array.shape[::-1])
    from numpy import float32, multiply, add, array
    from chimerax.map_data import grid_indices
    grid_points = grid_indices(size, float32)
    xyz_to_ijk_transform.inverse().transform_points(grid_points, in_place=True)
    grid_points_to_scene_transform = identity()

    # Make float32 copy for subtracting interpolated molecule maps.
    d = array(data_array, float32)

    from chimerax.atomic import Atoms
    from .move import position_history as h
    h.record_position(models, Atoms(), volume)

    fits = {}
    from . import fitmap as F
    from chimerax.map.volume import minimum_rms_scale
    from .search import Fit
    for s in range(steps):
        mi = s % len(models)
        v = models[mi]
        if request_stop_cb and request_stop_cb('Fitting %s in %s, step %d' %
                                               (v.name, volume.name, s + 1)):
            return
        # Subtract off other maps.
        d[:] = data_array
        for m in models + subtract_maps:
            if not m is v:
                values = m.interpolated_values(grid_points,
                                               grid_points_to_scene_transform,
                                               subregion=None,
                                               step=None)
                level = m.minimum_surface_level
                scale = minimum_rms_scale(values, data_array.ravel(), level)
                multiply(values, scale, values)
                add(d, values.reshape(d.shape), d)
        # Fit in difference map.
        points, point_weights = F.map_points_and_weights(
            v, envelope, include_zeros=include_zeros)
        move_tf, stats = F.locate_maximum(points,
                                          point_weights,
                                          d,
                                          xyz_to_ijk_transform,
                                          max_steps,
                                          ijk_step_size_min,
                                          ijk_step_size_max,
                                          optimize_translation,
                                          optimize_rotation,
                                          metric,
                                          request_stop_cb=None)
        v.position = move_tf * v.position
        fits[v] = Fit([v], None, volume, stats)
        if log:
            log.info(F.map_fit_message(v, volume, stats))

    h.record_position(models, Atoms(), volume)

    flist = [fits[m] for m in models if m in fits]
    return flist
예제 #17
0
def locate_maximum(points,
                   point_weights,
                   data_array,
                   xyz_to_ijk_transform,
                   max_steps=2000,
                   ijk_step_size_min=0.01,
                   ijk_step_size_max=0.5,
                   optimize_translation=True,
                   optimize_rotation=True,
                   metric='sum product',
                   rotation_center=None,
                   symmetries=[],
                   request_stop_cb=None):

    segment_steps = 4
    cut_step_size_threshold = .25
    step_cut_factor = .5
    step_grow_factor = 1.2

    ijk_step_size = ijk_step_size_max

    np = len(points)

    from time import time
    report_interval = 1.0  # seconds
    next_report_time = time() + report_interval

    if metric == 'correlation about mean':
        from numpy import sum, float64
        wm = sum(point_weights, dtype=float64) / len(point_weights)
        point_wts = point_weights.copy()
        point_wts -= wm
    else:
        point_wts = point_weights

    from chimerax.geometry import identity
    move_tf = identity()

    if rotation_center is None:
        from numpy import sum, float64
        rotation_center = rc = sum(points, axis=0, dtype=float64) / np

    syminv = [s.inverse() for s in symmetries]

    # Use temporary value and gradient arrays instead of reallocating each step.
    from numpy import empty, float32
    values = empty((np, ), float32)
    gradients = empty(points.shape, float32)

    step = 0
    while step < max_steps and ijk_step_size > ijk_step_size_min:
        xyz_to_ijk_tf = xyz_to_ijk_transform * move_tf
        rc = (rotation_center if len(symmetries) == 0 else move_tf.inverse() *
              rotation_center)
        seg_tf = step_to_maximum(points,
                                 point_wts,
                                 data_array,
                                 xyz_to_ijk_tf,
                                 segment_steps,
                                 ijk_step_size,
                                 optimize_translation,
                                 optimize_rotation,
                                 rc,
                                 metric,
                                 syminv=syminv,
                                 values=values,
                                 gradients=gradients)
        step += segment_steps
        mm = maximum_ijk_motion(points, xyz_to_ijk_tf, seg_tf)
        mmcut = cut_step_size_threshold * segment_steps * ijk_step_size
        if mm < mmcut:
            ijk_step_size *= step_cut_factor
        else:
            ijk_step_size = min(ijk_step_size * step_grow_factor,
                                ijk_step_size_max)
        move_tf = move_tf * seg_tf
        if request_stop_cb and time() >= next_report_time:
            next_report_time = time() + report_interval
            shift, angle = move_tf.shift_and_angle(rc)
            if request_stop_cb('%d steps, shift %.3g, rotation %.3g degrees' %
                               (step, shift, angle)):
                break

    # Record statistics of optimization.
    shift, angle = move_tf.shift_and_angle(rc)
    axis, axis_point, angle, axis_shift = move_tf.axis_center_angle_shift()
    xyz_to_ijk_tf = xyz_to_ijk_transform * move_tf
    stats = {
        'shift': shift,
        'axis': axis,
        'axis point': axis_point,
        'angle': angle,
        'axis shift': axis_shift,
        'steps': step,
        'points': np,
        'transform': move_tf
    }

    amv, npts = average_map_value(points,
                                  xyz_to_ijk_tf,
                                  data_array,
                                  syminv=syminv,
                                  values=values)
    stats['average map value'] = amv
    stats['points in map'] = npts  # Excludes out-of-bounds points
    stats['symmetries'] = len(symmetries)
    if not point_weights is None:
        map_values = volume_values(points,
                                   xyz_to_ijk_tf,
                                   data_array,
                                   syminv=syminv,
                                   values=values)
        olap, cor, corm = overlap_and_correlation(point_weights, map_values)
        stats['overlap'] = olap
        stats['correlation'] = cor
        stats['correlation about mean'] = corm

    return move_tf, stats