Esempio n. 1
0
def correct_o_position(res, psi):
    n, ca, c, o = [res.find_atom(name) for name in ['N', 'CA', 'C', 'O']]
    from chimerax.geometry import rotation, dihedral
    d = dihedral(*[a.coord for a in (n, ca, c, o)])
    r = rotation(c.coord - ca.coord, psi + 180 - d, center=c.coord)
    from chimerax.atomic import Atoms
    Atoms([o]).transform(r)
Esempio n. 2
0
def log_torsion_command(bond_rotator):
    bond = bond_rotator.rotation.bond
    ms_atom = bond_rotator.moving_side
    fs_atom = bond.other_atom(ms_atom)
    ms_atom2 = _connected_atom(ms_atom, fs_atom)
    fs_atom2 = _connected_atom(fs_atom, ms_atom)
    if ms_atom2 is None or fs_atom2 is None:
        return  # No connected atom to define a torsion
    side = '' if bond.smaller_side is ms_atom else 'move large'
    from chimerax.geometry import dihedral
    torsion = dihedral(fs_atom2.scene_coord, fs_atom.scene_coord,
                       ms_atom.scene_coord, ms_atom2.scene_coord)
    res = ms_atom.residue
    if ms_atom2.residue is res and fs_atom.residue is res and fs_atom2.residue is res:
        # Use simpler atom spec for the common case of rotating a side chain.
        atom_specs = '%s@%s,%s,%s,%s' % (res.string(style='command'),
                                         ms_atom2.name, ms_atom.name,
                                         fs_atom.name, fs_atom2.name)
    else:
        atom_specs = '%s %s %s %s' % (
            fs_atom2.string(style='command'), fs_atom.string(style='command'),
            ms_atom.string(style='command'), ms_atom2.string(style='command'))
    cmd = 'torsion %s %.2f %s' % (atom_specs, torsion, side)
    ses = ms_atom.structure.session
    from chimerax.core.commands import run
    run(ses, cmd)
Esempio n. 3
0
def cmd_torsion(session, atoms, value=None, *, move="small"):
    """Wrapper called by command line."""
    if len(atoms) != 4:
        raise UserError(
            "Must specify exactly 4 atoms for 'torsion' command; you specified %d"
            % len(atoms))
    a1, a2, a3, a4 = atoms
    from chimerax.geometry import dihedral
    cur_torsion = dihedral(*[a.scene_coord for a in atoms])
    if value is None:
        session.logger.info(
            "Torsion angle for atoms %s %s %s %s is %g\N{DEGREE SIGN}" %
            (a1, a2.string(relative_to=a1), a3.string(relative_to=a2),
             a4.string(relative_to=a3), cur_torsion))
        return
    for nb, bond in zip(a2.neighbors, a2.bonds):
        if nb == a3:
            break
    else:
        raise UserError(
            "To set torsion, middle two atoms (%s %s) must be bonded;they aren't"
            % (a2, a3.string(relative_to=a2)))
    move_smaller = move == "small"
    mgr = session.bond_rotations
    from .manager import BondRotationError
    try:
        rotater = mgr.new_rotation(bond, move_smaller_side=move_smaller)
    except BondRotationError as e:
        raise UserError(str(e))

    rotater.angle += value - cur_torsion
    mgr.delete_rotation(rotater)
Esempio n. 4
0
def build_next_atom_from_geometry(residue, residue_anchor, template_anchor,
                                  template_new_atom):
    from chimerax.atomic import struct_edit
    from chimerax.geometry import distance, angle, dihedral
    r = residue
    m = r.structure
    tnext = template_new_atom
    if tnext is None:
        raise TypeError('Template does not contain an atom with that name!')
    tstub = template_anchor
    rstub = residue_anchor
    existing_rstub_neighbors = rstub.neighbors

    n1 = rstub
    n2 = n3 = None
    t_direct_neighbors = []
    r_direct_neighbors = []
    for a2 in tstub.neighbors:
        if a2.element.name != 'H':
            n2 = r.find_atom(a2.name)
            if n2:
                t_direct_neighbors.append(a2)
                r_direct_neighbors.append(n2)
    if len(t_direct_neighbors) > 1:
        a2, a3 = t_direct_neighbors[:2]
        n2, n3 = r_direct_neighbors[:2]
    else:
        a2 = t_direct_neighbors[0]
        n2 = r_direct_neighbors[0]
    if not n2:
        raise TypeError(
            'No n2 found - Not enough connected atoms to form a dihedral!')
    if not n3:
        for a3 in a2.neighbors:
            if a3 not in (a2, tstub) and a3.element.name != 'H':
                n3 = r.find_atom(a3.name)
                if n3:
                    break
        if not n3:
            raise TypeError(
                'No n3 found - Not enough connected atoms to form a dihedral!')

    # print('Building next atom {} from geometry of {}'.format(template_new_atom.name,
    #     ','.join([n.name for n in (n1, n2, n3)])))

    dist = distance(tnext.coord, tstub.coord)
    ang = angle(tnext.coord, tstub.coord, a2.coord)
    dihe = dihedral(tnext.coord, tstub.coord, a2.coord, a3.coord)
    # print('{}: {} {} {}'.format(next_atom_name, dist, ang, dihe))
    a = struct_edit.add_dihedral_atom(tnext.name, tnext.element, n1, n2, n3,
                                      dist, ang, dihe)

    a.occupancy = rstub.occupancy
    a.bfactor = rstub.bfactor
    return a
Esempio n. 5
0
def interpolate_dihedral(i0, i1, i2, i3, coords0, coords1, f, coord_set):
    """
        Computer coordinate of atom a0 by interpolating dihedral angle
        defined by atoms (a0, a1, a2, a3).
        """
    t0 = time()
    from chimerax.geometry import distance, angle, dihedral, dihedral_point
    c00 = coords0[i0]
    c01 = coords0[i1]
    c02 = coords0[i2]
    c03 = coords0[i3]
    length0 = distance(c00, c01)
    angle0 = angle(c00, c01, c02)
    dihed0 = dihedral(c00, c01, c02, c03)
    c10 = coords1[i0]
    c11 = coords1[i1]
    c12 = coords1[i2]
    c13 = coords1[i3]
    length1 = distance(c10, c11)
    angle1 = angle(c10, c11, c12)
    dihed1 = dihedral(c10, c11, c12, c13)
    length = length0 + (length1 - length0) * f
    angle = angle0 + (angle1 - angle0) * f
    ddihed = dihed1 - dihed0
    if ddihed > 180:
        ddihed -= 360
    elif ddihed < -180:
        ddihed += 360
    dihed = dihed0 + ddihed * f
    c1 = coord_set[i1, :]
    c2 = coord_set[i2, :]
    c3 = coord_set[i3, :]
    t2 = time()
    c0 = dihedral_point(c1, c2, c3, length, angle, dihed)
    t3 = time()
    coord_set[i0:] = c0
    t1 = time()
    global iit, dpt
    iit += t1 - t0
    dpt += t3 - t2
Esempio n. 6
0
def flip_if_necessary(residue, chi_atom_names):
    if residue.is_missing_heavy_template_atoms():
        return
    chis = [[*chi_atom_names[:3], chi_atom_names[3][i]] for i in range(2)]
    dihedrals = []
    from chimerax.geometry import dihedral
    for chi_atom_names in chis:
        atoms = [residue.find_atom(name) for name in chi_atom_names]
        dihedrals.append(dihedral(*[a.coord for a in atoms]))
    if abs(dihedrals[1]) < abs(dihedrals[0]):
        if swap_equivalent_atoms(residue):
            correct_dihedral_restraint(residue)
            correct_position_and_distance_restraints(residue)
            return True
    return False
Esempio n. 7
0
def form_dihedral(res_bud, real1, tmpl_res, a, b, pos=None, dihed=None):
    from chimerax.atomic.struct_edit import add_atom, add_dihedral_atom
    res = res_bud.residue
    if pos:
        return add_atom(a.name, a.element, res, pos, info_from=real1)
    # use neighbors of res_bud rather than real1 to avoid clashes with
    # other res_bud neighbors in case bond to real1 neighbor freely rotates
    inres = [
        nb for nb in res_bud.neighbors if nb != real1 and nb.residue == res
    ]
    if len(inres) < 1:
        inres = [x for x in res.atoms if x not in [res_bud, real1]]
    if real1.residue != res or len(inres) < 1:
        raise AssertionError(
            "Can't form in-residue dihedral for %s of residue %s" %
            (res_bud, res))
    if dihed:
        real1 = res.find_atom("C1'")
        real2 = res.find_atom("O4'")
    else:
        real2 = inres[0]
    xyz0, xyz1, xyz2 = [
        tmpl_res.find_atom(a.name).coord for a in (res_bud, real1, real2)
    ]

    xyz = a.coord
    blen = b.length
    from chimerax.geometry import angle, dihedral
    ang = angle(xyz, xyz0, xyz1)
    if dihed is None:
        dihed = dihedral(xyz, xyz0, xyz1, xyz2)
    return add_dihedral_atom(a.name,
                             a.element,
                             res_bud,
                             real1,
                             real2,
                             blen,
                             ang,
                             dihed,
                             info_from=real1)
Esempio n. 8
0
def post_add(session, fake_n, fake_c):
    # fix up non-"true" terminal residues (terminal simply because
    # next residue is missing)
    for fn in fake_n:
        n = fn.find_atom("N")
        ca = fn.find_atom("CA")
        c = fn.find_atom("C")
        if not n or not ca or not c:
            continue
        dihed = None
        for cnb in c.neighbors:
            if cnb.name == "N":
                pn = cnb
                break
        else:
            dihed = 0.0
        if dihed is None:
            pr = pn.residue
            pc = pr.find_atom("C")
            pca = pr.find_atom("CA")
            if pr.name == "PRO":
                ph = pr.find_atom("CD")
            else:
                ph = pr.find_atom("H")
            if not pc or not pca or not ph:
                dihed = 0.0
        add_nh = True
        for nb in n.neighbors:
            if nb.element.number == 1:
                if nb.name == "H":
                    add_nh = False
                    if fn.name == "PRO":
                        nb.structure.delete_atom(nb)
                else:
                    nb.structure.delete_atom(nb)
        if fn.name == "PRO":
            n.idatm_type = "Npl"
            continue
        if add_nh:
            if dihed is None:
                from chimerax.geometry import dihedral
                dihed = dihedral(pc.coord, pca.coord, pn.coord, ph.coord)
            session.logger.info("Adding 'H' to %s" % str(fn))
            from chimerax.atomic.struct_edit import add_dihedral_atom
            h = add_dihedral_atom("H",
                                  "H",
                                  n,
                                  ca,
                                  c,
                                  1.01,
                                  120.0,
                                  dihed,
                                  bonded=True)
            h.color = determine_h_color(n)
        # also need to set N's IDATM type, because if we leave it as
        # N3+ then the residue will be identified by AddCharge as
        # terminal and there will be no charge for the H atom
        n.idatm_type = "Npl"

    for fc in fake_c:
        c = fc.find_atom("C")
        if not c:
            continue
        for nb in c.neighbors:
            if nb.element.number == 1:
                session.logger.info(
                    "%s is not terminus, removing H atom from 'C'" % str(fc))
                nb.structure.delete_atom(nb)
        # the N proton may have been named 'HN'; fix that
        hn = fc.find_atom("HN")
        if not hn:
            continue
        n = hn.neighbors[0]
        h = add_atom("H",
                     "H",
                     fc,
                     hn.coord,
                     serial_number=hn.serial_number,
                     bonded_to=n)
        h.color = determine_h_color(n)
        h.hide = n.hide
        fc.structure.delete_atom(hn)
Esempio n. 9
0
def template_swap_res(res, res_type, *, preserve=False, bfactor=None):
    """change 'res' into type 'res_type'"""

    fixed, buds, start, end = get_res_info(res)

    if res_type == "HIS":
        res_type = "HIP"
    if res_type in ["A", "C", "G", "T"
                    ] and res.name in ["DA", "DC", "DT", "DG"]:
        res_type = "D" + res_type
    from chimerax.atomic import TmplResidue, Atom
    tmpl_res = TmplResidue.get_template(res_type, start=start, end=end)
    if not tmpl_res:
        raise TemplateError("No connectivity template for residue '%s'" %
                            res_type)
    # sanity check:  does the template have the bud atoms?
    for bud in buds:
        if tmpl_res.find_atom(bud) is None:
            raise TemplateError("New residue type (%s) not compatible with"
                                " starting residue type (%s)" %
                                (res_type, res.name))
    color_by_element = False
    uniform_color = res.find_atom(buds[0]).color
    het = res.find_atom("N") or res.find_atom("O4'")
    if het:
        carbon = res.find_atom("CA") or res.find_atom("C4'")
        if carbon:
            color_by_element = het.color != carbon.color
            if color_by_element:
                carbon_color = carbon.color
            else:
                uniform_color = het.color

    bfactor = bfactor_for_res(res, bfactor)

    if preserve:
        if "CA" in fixed and res_type not in ['GLY', 'ALA']:
            raise TemplateSwapError(
                "'preserve' keyword not yet implemented for amino acids")
        a1 = res.find_atom("O4'")
        a2 = res.find_atom("C1'")
        if not a1 or not a2:
            preserve_pos = None
        else:
            dihed_names = {"N9": ["C4", "C8"], "N1": ["C2", "C6"]}
            a3 = res.find_atom("N9") or res.find_atom("N1")
            if a3:
                if a2 not in a3.neighbors:
                    preserve_pos = None
                else:
                    preserve_pos = a3.coord
            else:
                preserve_pos = None
        if preserve_pos:
            p1, p2, p3 = [a.coord for a in (a1, a2, a3)]
            preserved_pos = False
            prev_name, alt_name = dihed_names[a3.name]
            a4 = res.find_atom(prev_name)
            if a4 and a3 in a4.neighbors:
                p4 = a4.coord
                from chimerax.geometry import dihedral
                preserve_dihed = dihedral(p1, p2, p3, p4)
            else:
                preserve_dihed = None
        else:
            preserve_dihed = None

    # prune non-backbone atoms
    for a in res.atoms:
        if a.name not in fixed:
            a.structure.delete_atom(a)

    # add new sidechain
    new_atoms = []
    xf = None
    from chimerax.atomic.struct_edit import add_bond
    while len(buds) > 0:
        bud = buds.pop()
        tmpl_bud = tmpl_res.find_atom(bud)
        res_bud = res.find_atom(bud)

        try:
            info = Atom.idatm_info_map[tmpl_bud.idatm_type]
            geom = info.geometry
            subs = info.substituents
        except KeyError:
            raise AssertionError(
                "Can't determine atom type information for atom %s of residue %s"
                % (bud, res))

        # use .coord rather than .scene_coord:  we want to set the new atom's coord,
        # to which the proper xform will then be applied
        for a, b in zip(tmpl_bud.neighbors, tmpl_bud.bonds):
            if a.element.number == 1:
                # don't add hydrogens
                continue
            if res.find_atom(a.name):
                res_bonder = res.find_atom(a.name)
                if res_bonder not in res_bud.neighbors:
                    add_bond(a, res_bonder)
                continue

            new_atom = None
            num_bonded = len(res_bud.bonds)
            if num_bonded >= subs:
                raise AssertionError(
                    "Too many atoms bonded to %s of residue %s" % (bud, res))
            if num_bonded == 0:
                raise AssertionError(
                    "Atom %s of residue %s has no neighbors after pruning?!?" %
                    (bud, res))
            # since fused ring systems may have distorted bond angles, always use dihedral placement
            real1 = res_bud.neighbors[0]
            kw = {}
            if preserve:
                if preserve_pos and not preserved_pos:
                    kw['pos'] = preserve_pos
                    preserved_pos = True
                    preserved_name = a.name
                elif preserve_dihed is not None:
                    prev_name, alt_name = dihed_names[preserved_name]
                    if a.name == prev_name:
                        kw['dihed'] = preserve_dihed
                    elif a.name == alt_name:
                        kw['dihed'] = preserve_dihed + 180.0
            if not kw and xf is not None:
                kw['pos'] = xf * a.coord

            new_atom = form_dihedral(res_bud, real1, tmpl_res, a, b, **kw)
            new_atom.draw_mode = res_bud.draw_mode
            new_atom.bfactor = bfactor
            if color_by_element:
                if new_atom.element.name == "C":
                    new_atom.color = carbon_color
                else:
                    from chimerax.atomic.colors import element_color
                    new_atom.color = element_color(new_atom.element.number)
            else:
                new_atom.color = uniform_color
            new_atoms.append(new_atom)

            for bonded in a.neighbors:
                bond_atom = res.find_atom(bonded.name)
                if not bond_atom:
                    continue
                add_bond(new_atom, bond_atom)
            buds.append(new_atom.name)

        # once we've placed 3 side chain atoms, we use superpositioning to
        # place the remainder of the side chain, since dihedrals will
        # likely distort ring closures if 'preserve' is true
        if buds and not xf and len(new_atoms) >= 3:
            placed_positions = []
            tmpl_positions = []
            for na in new_atoms:
                placed_positions.append(na.coord)
                tmpl_positions.append(tmpl_res.find_atom(na.name).coord)
            import numpy
            from chimerax.geometry import align_points
            xf = align_points(numpy.array(tmpl_positions),
                              numpy.array(placed_positions))[0]

    res.name = res_type