Ejemplo n.º 1
0
def don_water(donor, donor_hyds, acceptor, sp2_O_rp2, sp2_O_r2, sp2_O_theta,
        sp3_O_rp2, sp3_O_r2, sp3_O_theta, sp3_O_phi, sp3_N_rp2, sp3_N_r2,
        sp3_N_theta, sp3_N_upsilon, gen_rp2, gen_r2, gen_theta):
    if hbond.verbose:
        print("don_water")
    if len(donor_hyds) > 0:
        # hydrogens explicitly present, can immediately call don_theta_tau
        return don_theta_tau(donor, donor_hyds, acceptor, sp2_O_rp2,
            sp2_O_theta, sp3_O_rp2, sp3_O_theta, sp3_O_phi, sp3_N_rp2,
            sp3_N_theta, sp3_N_upsilon, gen_rp2, gen_theta)

    ap = acceptor._hb_coord
    dp = donor._hb_coord

    acc_type = acceptor.idatm_type
    if acc_type not in type_info:
        if hbond.verbose:
            print("Unknown acceptor type failure")
        return False

    geom = type_info[acc_type].geometry
    element = acceptor.element.name
    if element == 'O' and geom == planar:
        if hbond.verbose:
            print("planar O")
        sq = distance_squared(dp, ap)
        if sq > sp2_O_r2:
            if hbond.verbose:
                print("dist criteria failed (%g > %g)" % (sqrt(sq), sqrt(sp2_O_r2)))
            return False
    elif element == 'O' and geom == tetrahedral or element == 'N' and geom == planar:
        if hbond.verbose:
            print("planar N or tet O")
        sq = distance_squared(dp, ap)
        if sq > sp3_O_r2:
            if hbond.verbose:
                print("dist criteria failed (%g > %g)" % (sqrt(sq), sqrt(sp3_O_r2)))
            return False
    elif element == 'N' and geom == tetrahedral:
        if hbond.verbose:
            print("tet N")
        sq = distance_squared(dp, ap)
        if sq > sp3_N_r2:
            if hbond.verbose:
                print("dist criteria failed (%g > %g)" % (sqrt(sq), sqrt(sp3_N_r2)))
            return False
    else:
        if hbond.verbose:
            print("generic acceptor")
        sq = distance_squared(dp, ap)
        if sq > gen_r2:
            if hbond.verbose:
                print("dist criteria failed (%g > %g)" % (sqrt(sq), sqrt(gen_r2)))
            return False
    if hbond.verbose:
        print("dist criteria OK")

    return don_theta_tau(donor, donor_hyds, acceptor, sp2_O_rp2,
        sp2_O_theta, sp3_O_rp2, sp3_O_theta, sp3_O_phi, sp3_N_rp2,
        sp3_N_theta, sp3_N_upsilon, gen_rp2, gen_theta, is_water=True)
Ejemplo n.º 2
0
def test_upsion_tau_acceptor(donor, donor_hyds, acceptor, r2, upsilon_low, upsilon_high,
        theta, tau, tau_sym):
    dc = donor._hb_coord
    ac = acceptor._hb_coord

    d2 = distance_squared(dc, ac)
    if d2 > r2:
        if hbond.verbose:
            print("dist criteria failed (%g > %g)" % (sqrt(d2), sqrt(r2)))
        return False

    upsilon_high = 0 - upsilon_high
    heavys = [a for a in donor.neighbors if a.element.number > 1]
    if len(heavys) != 1:
        raise AtomTypeError("upsilon tau donor (%s) not bonded to"
            " exactly one heavy atom" % donor)
    ang = angle(heavys[0]._hb_coord, dc, ac)
    if ang < upsilon_low or ang > upsilon_high:
        if hbond.verbose:
            print("upsilon criteria failed (%g < %g or %g > %g)"
                % (ang, upsilon_low, ang, upsilon_high))
        return False
    if hbond.verbose:
        print("upsilon criteria OK (%g < %g < %g)" % (upsilon_low, ang, upsilon_high))

    dp = dc
    ap = ac

    if not test_theta(dp, donor_hyds, ap, theta):
        return False

    return test_tau(tau, tau_sym, donor, dp, ap)
Ejemplo n.º 3
0
def _make_shared_data(session, protonation_models, in_isolation):
    from chimerax.geometry import distance_squared
    from chimerax.atom_search import AtomSearchTree
    # since adaptive search tree is static, it will not include
    # hydrogens added after this; they will have to be found by
    # looking off their heavy atoms
    global search_tree, _radii, _metals, ident_pos_models, _h_coloring, _solvent_atoms
    _radii = {}
    search_atoms = []
    metal_atoms = []
    # if we're adding hydrogens to unopen models, add those models to open models...
    pm_set = set(protonation_models)
    if in_isolation:
        models = pm_set
    else:
        from chimerax.atomic import AtomicStructure
        om_set = set(
            [m for m in session.models if isinstance(m, AtomicStructure)])
        models = om_set | pm_set
    # consider only one copy of identically-positioned models...
    ident_pos_models = {}
    for pm in protonation_models:
        for m in models:
            if m == pm:
                continue
            if pm.num_atoms != m.num_atoms:
                continue
            for a1, a2 in zip(pm.atoms[:3], m.atoms[:3]):
                if distance_squared(a1._addh_coord, a2._addh_coord) > 0.00001:
                    break
            else:
                ident_pos_models.setdefault(pm, set()).add(m)

    for m in models:
        if m not in ident_pos_models:
            ident_pos_models[m] = set()
        for a in m.atoms:
            search_atoms.append(a)
            _radii[a] = a.radius
            if a.element.is_metal:
                metal_atoms.append(a)
    from chimerax.atomic import Atom
    use_scene_coords = Atom._addh_coord == Atom.scene_coord
    search_tree = AtomSearchTree(search_atoms,
                                 sep_val=_tree_dist,
                                 scene_coords=use_scene_coords)
    _metals = AtomSearchTree(metal_atoms,
                             sep_val=max(_metal_dist, 1.0),
                             scene_coords=use_scene_coords)
    from weakref import WeakKeyDictionary
    _h_coloring = WeakKeyDictionary()
    _solvent_atoms = WeakKeyDictionary()
Ejemplo n.º 4
0
 def evaluate(self, pos):
     sum = 0.0
     n = 0
     from chimerax.geometry import distance_squared
     for coords in self._gather_coords(pos):
         for i, crd1 in enumerate(coords):
             for crd2 in coords[i + 1:]:
                 sum += distance_squared(crd1, crd2)
             n += (len(coords) * (len(coords) - 1)) // 2
     if n == 0:
         return None
     from math import sqrt
     return sqrt(sum / n)
Ejemplo n.º 5
0
def don_generic(donor, donor_hyds, acceptor, sp2_O_rp2, sp3_O_rp2, sp3_N_rp2,
    sp2_O_r2, sp3_O_r2, sp3_N_r2, gen_rp2, gen_r2, min_hyd_angle, min_bonded_angle):
    if hbond.verbose:
        print("don_generic")
    dc = donor._hb_coord
    ac = acceptor._hb_coord

    acc_type = acceptor.idatm_type
    if acc_type not in type_info:
        return False

    geom = type_info[acc_type].geometry
    element = acceptor.element.name
    if element == 'O' and geom == planar:
        if hbond.verbose:
            print("planar O")
        r2 = sp2_O_r2
        rp2 = sp2_O_rp2
    elif element == 'O' and geom == tetrahedral or element == 'N' and geom == planar:
        if hbond.verbose:
            print("planar N or tet O")
        r2 = sp3_O_r2
        rp2 = sp3_O_rp2
    elif element == 'N' and geom == tetrahedral:
        if hbond.verbose:
            print("tet N")
        r2 = sp3_N_r2
        rp2 = sp3_N_rp2
    else:
        if hbond.verbose:
            print("generic acceptor")
        if acceptor.element.name == "S":
            r2 = sulphur_compensate(gen_r2)
            min_bonded_angle = min_bonded_angle - 9
        r2 = gen_r2
        rp2 = gen_rp2

    ap = acceptor._hb_coord
    dp = donor._hb_coord
    if len(donor_hyds) == 0:
        d2 = distance_squared(dc, ac)
        if d2 > r2:
            if hbond.verbose:
                print("dist criteria failed (%g > %g)" % (sqrt(d2), sqrt(r2)))
            return False
    else:
        for hyd_pos in donor_hyds:
            if distance_squared(hyd_pos, ap) < rp2:
                break
        else:
            if hbond.verbose:
                print("hyd dist criteria failed (all >= %g)" % (sqrt(rp2)))
            return False

    if hbond.verbose:
        print("dist criteria OK")

    for bonded in donor.neighbors:
        if bonded.element.number <= 1:
            continue
        bp = bonded._hb_coord
        ang = angle(bp, dp, ap)
        if ang < min_bonded_angle:
            if hbond.verbose:
                print("bonded angle too sharp (%g < %g)" % (ang, min_bonded_angle))
            return False

    if len(donor_hyds) == 0:
        if hbond.verbose:
            print("No specific hydrogen positions; default accept")
        return True

    for hyd_pos in donor_hyds:
        ang = angle(dp, hyd_pos, ap)
        if ang >= min_hyd_angle:
            if hbond.verbose:
                print("hydrogen angle okay (%g >= %g)" % (ang, min_hyd_angle))
            return True
    if hbond.verbose:
        print("hydrogen angle(s) too sharp (< %g)" % min_hyd_angle)
    return False
Ejemplo n.º 6
0
def don_theta_tau(donor, donor_hyds, acceptor, sp2_O_rp2, sp2_O_theta, sp3_O_rp2, sp3_O_theta,
        sp3_O_phi, sp3_N_rp2, sp3_N_theta, sp3_N_upsilon, gen_rp2, gen_theta, is_water=False):
        # 'is_water' only for hydrogenless water
    if hbond.verbose:
        print("don_theta_tau")
    if len(donor_hyds) == 0 and not is_water:
        if hbond.verbose:
            print("No hydrogens; default failure")
        return False
    ap = acceptor._hb_coord
    dp = donor._hb_coord

    acc_type = acceptor.idatm_type
    if acc_type not in type_info:
        if hbond.verbose:
            print("Unknown acceptor type failure")
        return False

    geom = type_info[acc_type].geometry
    element = acceptor.element.name
    if element == 'O' and geom == planar:
        if hbond.verbose:
            print("planar O")
        for hyd_pos in donor_hyds:
            if distance_squared(hyd_pos, ap) <= sp2_O_rp2:
                break
        else:
            if not is_water:
                if hbond.verbose:
                    print("dist criteria failed (all > %g)"% sqrt(sp2_O_rp2))
                return False
        theta = sp2_O_theta
    elif element == 'O' and geom == tetrahedral or element == 'N' and geom == planar:
        if hbond.verbose:
            print("planar N or tet O")
        for hyd_pos in donor_hyds:
            if distance_squared(hyd_pos, ap) <= sp3_O_rp2:
                break
        else:
            if not is_water:
                if hbond.verbose:
                    print("dist criteria failed (all > %g)" % sqrt(sp3_O_rp2))
                return False
        theta = sp3_O_theta

        # only test phi for acceptors with two bonded atoms
        if acceptor.num_bonds == 2:
            if hbond.verbose:
                print("testing donor phi")
            bonded = acceptor.neighbors
            phi_plane, base_pos = get_phi_plane_params(acceptor, bonded[0], bonded[1])
            if not test_phi(donor._hb_coord, ap, base_pos, phi_plane, sp3_O_phi):
                return False

    elif element == 'N' and geom == tetrahedral:
        if hbond.verbose:
            print("tet N")
        for hyd_pos in donor_hyds:
            if distance_squared(hyd_pos, ap) <= sp3_N_rp2:
                break
        else:
            if not is_water:
                if hbond.verbose:
                    print("dist criteria failed (all > %g)" % sqrt(sp3_N_rp2))
                return False
        theta = sp3_N_theta

        # test upsilon against lone pair directions
        bonded_pos = []
        for bonded in acceptor.neighbors:
            bonded_pos.append(bonded._hb_coord)
        lp_pos = bond_positions(ap, geom, 1.0, bonded_pos)
        if len(lp_pos) > 0:
            # fixed lone pair positions
            for lp in bond_positions(ap, geom, 1.0, bonded_pos):
                # invert position so that we are measuring angles correctly
                ang = angle(dp, ap, ap - (lp - ap))
                if ang > sp3_N_upsilon:
                    if hbond.verbose:
                        print("acceptor upsilon okay (%g > %g)" % (ang, sp3_N_upsilon))
                    break
            else:
                if hbond.verbose:
                    print("all acceptor upsilons failed (< %g)" % sp3_N_upsilon)
                return False
        # else: indefinite lone pair positions; default okay
    else:
        if hbond.verbose:
            print("generic acceptor")
        if acceptor.element.name == "S":
            gen_rp2 = sulphur_compensate(gen_rp2)
        for hyd_pos in donor_hyds:
            if distance_squared(hyd_pos, ap) <= gen_rp2:
                break
        else:
            if hbond.verbose:
                print("dist criteria failed (all > %g)" % sqrt(gen_rp2))
            return False
        theta = gen_theta
    if hbond.verbose:
        print("dist criteria OK")

    return test_theta(dp, donor_hyds, ap, theta)
Ejemplo n.º 7
0
def cmd_define_plane(session,
                     atoms,
                     *,
                     thickness=defaults["plane_thickness"],
                     padding=0.0,
                     color=None,
                     radius=None,
                     name="plane"):
    """Wrapper to be called by command line.

       Use chimerax.axes_planes.plane for other programming applications.
    """
    from chimerax.core.errors import UserError

    from chimerax.atomic import AtomicStructure, concatenate, Structure
    if atoms is None:
        structures_atoms = [
            m.atoms for m in session.models if isinstance(m, AtomicStructure)
        ]
        if structures_atoms:
            atoms = concatenate(structures_atoms)
        else:
            raise UserError("Atom specifier selects no atoms")
    if len(atoms) < 3:
        raise UserError("Must specify at least 3 atoms to define a plane")

    structures = atoms.unique_structures
    if len(structures) > 1:
        crds = atoms.scene_coords
    else:
        crds = atoms.coords

    from chimerax.geometry import Plane, distance_squared
    plane = Plane(crds)

    if radius is None:
        max_sq_dist = None
        origin = plane.origin
        for crd in crds:
            projected = plane.nearest(crd)
            sq_dist = distance_squared(origin, projected)
            if max_sq_dist is None or sq_dist > max_sq_dist:
                max_sq_dist = sq_dist
        from math import sqrt
        radius = sqrt(max_sq_dist)

    if color is None:
        from chimerax.atomic.colors import element_color, predominant_color
        color = predominant_color(atoms)
        if color is None:
            color = element_color(a.element.number)
    else:
        color = color.uint8x4()

    plane_model = PlaneModel(session, name, plane, thickness, radius + padding,
                             color)
    if len(structures) > 1:
        session.models.add([plane_model])
    else:
        structures[0].add([plane_model])
    session.logger.info("Plane %s' placed at %s with normal %s" %
                        (name, plane.origin, plane.normal))
    return plane_model
Ejemplo n.º 8
0
def cmd_hbonds(session,
               atoms,
               intra_model=True,
               inter_model=True,
               relax=True,
               dist_slop=rec_dist_slop,
               angle_slop=rec_angle_slop,
               two_colors=False,
               restrict="any",
               radius=AtomicStructure.default_hbond_radius,
               save_file=None,
               batch=False,
               inter_submodel=False,
               make_pseudobonds=True,
               retain_current=False,
               reveal=False,
               naming_style=None,
               log=False,
               cache_DA=None,
               color=AtomicStructure.default_hbond_color,
               slop_color=BuiltinColors["dark orange"],
               show_dist=False,
               intra_res=True,
               intra_mol=True,
               dashes=None,
               salt_only=False,
               name="hydrogen bonds",
               coordsets=True,
               select=False):
    """Wrapper to be called by command line.

       Use hbonds.find_hbonds for other programming applications.
    """

    if atoms is None:
        from chimerax.atomic import concatenate
        structures_atoms = [
            m.atoms for m in session.models if isinstance(m, AtomicStructure)
        ]
        if structures_atoms:
            atoms = concatenate(structures_atoms)
        else:
            atoms = Atoms()

    from chimerax.core.errors import UserError
    if not atoms and not batch:
        raise UserError("Atom specifier selects no atoms")

    bond_color = color

    if restrict == "both":
        donors = acceptors = atoms
        structures = atoms.unique_structures
    elif restrict in ["cross", "any"]:
        donors = acceptors = None
        if inter_model:
            structures = [
                m for m in session.models if isinstance(m, AtomicStructure)
            ]
        else:
            structures = atoms.unique_structures
    else:  # another Atoms collection
        if not restrict and not batch:
            raise UserError("'restrict' atom specifier selects no atoms")
        combined = atoms | restrict
        donors = acceptors = combined
        structures = combined.unique_structures

    if not relax:
        dist_slop = angle_slop = 0.0

    base_kw = {
        'inter_model': inter_model,
        'intra_model': intra_model,
        'donors': donors,
        'acceptors': acceptors,
        'inter_submodel': inter_submodel,
        'cache_da': cache_DA
    }

    doing_coordsets = coordsets and len(
        structures) == 1 and structures[0].num_coordsets > 1
    if doing_coordsets:
        hb_func = find_coordset_hbonds
        struct_info = structures[0]
    else:
        hb_func = find_hbonds
        struct_info = structures

    result = hb_func(session,
                     struct_info,
                     dist_slop=dist_slop,
                     angle_slop=angle_slop,
                     **base_kw)
    if doing_coordsets:
        hb_lists = result
    else:
        hb_lists = [result]
    for hbonds in hb_lists:
        # filter on salt bridges first, since we need access to all H-bonds in order
        # to assess which histidines should be considered salt-bridge donors
        if salt_only:
            sb_donors, sb_acceptors = salt_preprocess(hbonds)
            hbonds[:] = [
                hb for hb in hbonds
                if hb[0] in sb_donors and hb[1] in sb_acceptors
            ]
        hbonds[:] = restrict_hbonds(hbonds, atoms, restrict)
        if not intra_mol:
            mol_num = 0
            mol_map = {}
            for s in structures:
                for m in s.molecules:
                    mol_num += 1
                    for a in m:
                        mol_map[a] = mol_num
            hbonds[:] = [
                hb for hb in hbonds if mol_map[hb[0]] != mol_map[hb[1]]
            ]
        if not intra_res:
            hbonds[:] = [hb for hb in hbonds if hb[0].residue != hb[1].residue]

    if doing_coordsets:
        cs_ids = structures[0].coordset_ids
        output_info = (inter_model, intra_model, relax, dist_slop, angle_slop,
                       structures, hb_lists, cs_ids)
    else:
        output_info = (inter_model, intra_model, relax, dist_slop, angle_slop,
                       structures, result, None)
    if log:
        import io
        buffer = io.StringIO()
        buffer.write("<pre>")
        _file_output(buffer, output_info, naming_style)
        buffer.write("</pre>")
        session.logger.info(buffer.getvalue(), is_html=True)
    if save_file is not None:
        _file_output(save_file, output_info, naming_style)

    if doing_coordsets:
        session.logger.status("%d hydrogen bonds found in %d coordsets" %
                              (sum([len(hbs)
                                    for hbs in hb_lists]), len(cs_ids)),
                              log=True,
                              blank_after=120)
    else:
        session.logger.status("%d hydrogen bonds found" % len(result),
                              log=True,
                              blank_after=120)

    if select:
        if doing_coordsets:
            structure = structures[0]
            for i, cs_id in enumerate(structure.coordset_ids):
                if structure.active_coordset_id == cs_id:
                    break
            hb_list = hb_lists[i]
        else:
            hb_list = hb_lists[0]
            cs_id = None
        session.selection.clear()
        for d, a in hb_list:
            if cs_id is None:
                acc_coord = a.scene_coord
            else:
                acc_coord = a.get_coordset_coord(cs_id)
            dist, hyd = donor_hyd(d, cs_id, acc_coord)
            if hyd is None:
                d.selected = True
            else:
                hyd.selected = True
            a.selected = True

    if not make_pseudobonds:
        return hb_lists if doing_coordsets else hb_lists[0]

    if two_colors:
        # color relaxed constraints differently
        precise_result = hb_func(session, struct_info, **base_kw)
        if doing_coordsets:
            precise_lists = precise_result
        else:
            precise_lists = [precise_result]
        for precise in precise_lists:
            precise[:] = restrict_hbonds(precise, atoms, restrict)
            if not intra_mol:
                precise[:] = [
                    hb for hb in precise if mol_map[hb[0]] != mol_map[hb[1]]
                ]
            if not intra_res:
                precise[:] = [
                    hb for hb in precise if hb[0].residue != hb[1].residue
                ]
            if salt_only:
                precise[:] = [
                    hb for hb in precise
                    if hb[0] in sb_donors and hb[1] in sb_acceptors
                ]
        # give another opportunity to read the result...
        if doing_coordsets:
            session.logger.status(
                "%d strict hydrogen bonds found in %d coordsets" %
                (sum([len(hbs) for hbs in precise_lists]), len(cs_ids)),
                log=True,
                blank_after=120)
        else:
            session.logger.status("%d strict hydrogen bonds found" %
                                  len(precise_result),
                                  log=True,
                                  blank_after=120)

    # a true inter-model computation should be placed in a global group, otherwise
    # into individual per-structure groups
    submodels = False
    m_ids = set()
    for s in structures:
        m_id = s.id[:-1] if len(s.id) > 1 else s.id
        if m_id in m_ids:
            submodels = True
        m_ids.add(m_id)
    global_comp = (inter_model and len(m_ids) > 1) or (submodels
                                                       and inter_submodel)
    if global_comp:
        # global comp nukes per-structure groups it covers if intra-model also
        if intra_model and not retain_current:
            closures = []
            for s in structures:
                pbg = s.pseudobond_group(name, create_type=None)
                if pbg:
                    closures.append(pbg)
            if closures:
                session.models.close(closures)
        hb_info = [(result, session.pb_manager.get_group(name))]
    elif doing_coordsets:
        hb_info = [(result,
                    structures[0].pseudobond_group(name,
                                                   create_type="coordset"))]
    else:
        per_structure = {s: [] for s in structures}
        for hb in result:
            per_structure[hb[0].structure].append(hb)
        hb_info = [(hbs, s.pseudobond_group(name, create_type="coordset"))
                   for s, hbs in per_structure.items()]

    for grp_hbonds, pbg in hb_info:
        if not retain_current:
            pbg.clear()
            pbg.color = bond_color.uint8x4()
            pbg.radius = radius
            pbg.dashes = dashes if dashes is not None else AtomicStructure.default_hbond_dashes
        else:
            if dashes is not None:
                pbg.dashes = dashes
        if not doing_coordsets:
            grp_hbonds = [grp_hbonds]

        for i, cs_hbonds in enumerate(grp_hbonds):
            pre_existing = {}
            if doing_coordsets:
                cs_id = cs_ids[i]
                pbg_pseudobonds = pbg.get_pseudobonds(cs_id)
            else:
                pbg_pseudobonds = pbg.pseudobonds
            if two_colors:
                precise = set(precise_lists[i])
            if retain_current:
                for pb in pbg_pseudobonds:
                    pre_existing[pb.atoms] = pb

            from chimerax.geometry import distance_squared
            for don, acc in cs_hbonds:
                nearest = None
                heavy_don = don
                for h in [x for x in don.neighbors if x.element.number == 1]:
                    sqdist = distance_squared(h.scene_coord, acc.scene_coord)
                    if nearest is None or sqdist < nsqdist:
                        nearest = h
                        nsqdist = sqdist
                if nearest is not None:
                    don = nearest
                if (don, acc) in pre_existing:
                    pb = pre_existing[(don, acc)]
                else:
                    if doing_coordsets:
                        pb = pbg.new_pseudobond(don, acc, cs_id)
                    else:
                        pb = pbg.new_pseudobond(don, acc)
                if two_colors:
                    if (heavy_don, acc) in precise:
                        color = bond_color
                    else:
                        color = slop_color
                else:
                    color = bond_color
                rgba = pb.color
                rgba[:3] = color.uint8x4()[:3]  # preserve transparency
                pb.color = rgba
                pb.radius = radius
                if reveal:
                    for end in [don, acc]:
                        if end.display:
                            continue
                        res_atoms = end.residue.atoms
                        if end.is_side_chain:
                            res_atoms.filter(res_atoms.is_side_chains ==
                                             True).displays = True
                        elif end.is_backbone():
                            res_atoms.filter(res_atoms.is_backbones() ==
                                             True).displays = True
                        else:
                            res_atoms.displays = True
        if pbg.id is None:
            session.models.add([pbg])

        if show_dist:
            session.pb_dist_monitor.add_group(pbg)
        else:
            session.pb_dist_monitor.remove_group(pbg)
    return hb_lists if doing_coordsets else hb_lists[0]
Ejemplo n.º 9
0
def _alt_loc_add_hydrogens(atom, alt_loc_atom, bonding_info, naming_schema,
                           total_hydrogens, idatm_type, invert, coordinations):
    from .cmd import new_hydrogen, find_nearest, roomiest, find_rotamer_nearest, add_altloc_hyds
    from .util import bond_with_H_length
    away = away2 = planar = None
    geom = bonding_info.geometry
    substs = bonding_info.substituents
    needed = substs - atom.num_explicit_bonds
    if needed <= 0:
        return
    added = None
    if alt_loc_atom is None:
        alt_locs = [atom.alt_loc]
    else:
        alt_locs = alt_loc_atom.alt_locs
        # move current alt_loc to end of list to minimize
        # the number of times we have to change alt locs
        cur_alt_loc = alt_loc_atom.alt_loc
        alt_locs.remove(cur_alt_loc)
        alt_locs.append(cur_alt_loc)
    alt_loc_info = []
    for alt_loc in alt_locs:
        if alt_loc_atom:
            alt_loc_atom.alt_loc = alt_loc
            occupancy = alt_loc_atom.occupancy
        else:
            occupancy = 1.0
        at_pos = atom._addh_coord
        exclude = coordinations + list(atom.neighbors)
        if geom == 3:
            if atom.num_bonds == 1:
                bonded = atom.neighbors[0]
                grand_bonded = list(bonded.neighbors)
                grand_bonded.remove(atom)
                if len(grand_bonded) < 3:
                    planar = [a._addh_coord for a in grand_bonded]
        if geom == 4 and atom.num_bonds == 0:
            away, d, natom = find_nearest(at_pos, atom, exclude, 3.5)
            if away is not None:
                away2, d2, natom2 = find_rotamer_nearest(
                    at_pos, idatm_type[atom], atom, natom, 3.5)
        elif geom == 4 and len(coordinations) + atom.num_bonds == 1:
            away, d, natom = find_rotamer_nearest(
                at_pos, idatm_type[atom], atom,
                (list(atom.neighbors) + coordinations)[0], 3.5)
        else:
            away, d, natom = find_nearest(at_pos, atom, exclude, 3.5)

        bonded_pos = []
        for bonded in atom.neighbors:
            bonded_pos.append(bonded._addh_coord)

        if coordinations:
            toward = coordinations[0]._addh_coord
            away2 = away
            away = None
        else:
            toward = None
        from chimerax.atomic.bond_geom import bond_positions
        from chimerax.geometry import distance_squared
        positions = bond_positions(at_pos,
                                   geom,
                                   bond_with_H_length(atom, geom),
                                   bonded_pos,
                                   toward=toward,
                                   coplanar=planar,
                                   away=away,
                                   away2=away2)
        if coordinations:
            coord_pos = None
            for pos in positions:
                d = distance_squared(pos, toward)
                if coord_pos is None or d < lowest:
                    coord_pos = pos
                    lowest = d
            positions.remove(coord_pos)
        if len(positions) > needed:
            positions = roomiest(positions, atom, 3.5, bonding_info)[:needed]
        alt_loc_info.append((alt_loc, occupancy, positions))
    # delay adding Hs until all positions computed so that neighbors, etc. correct
    # for later alt locs
    add_altloc_hyds(atom, alt_loc_info, invert, bonding_info, total_hydrogens,
                    naming_schema)
Ejemplo n.º 10
0
def make_ladder(nd, residues, params):
    """generate links between residues that are hydrogen bonded together"""
    # returns set of residues whose bases are drawn as rungs and
    # and have their atoms hidden
    all_shapes = []

    # Create list of atoms from residues for donors and acceptors
    mol = residues[0].structure

    # make a set for quick inclusion test
    residue_set = set(residues)

    pbg = mol.pseudobond_group(mol.PBG_HYDROGEN_BONDS, create_type=None)
    if not pbg:
        bonds = ()
    else:
        bonds = (p.atoms for p in pbg.pseudobonds)

    # only make one rung between residues even if there is more than one
    # h-bond
    depict_bonds = {}
    for a0, a1 in bonds:
        r0 = a0.residue
        r1 = a1.residue
        if r0 not in residue_set or r1 not in residue_set:
            continue
        non_base = (BackboneRiboseRE.match(a0.name),
                    BackboneRiboseRE.match(a1.name))
        if params.skip_nonbase_Hbonds and any(non_base):
            continue
        if r0.connects_to(r1):
            # skip covalently bonded residues
            continue
        if r1 < r0:
            r0, r1 = r1, r0
            non_base = (non_base[1], non_base[0])
        c3p0 = _c3pos(r0)
        if not c3p0:
            continue
        c3p1 = _c3pos(r1)
        if not c3p1:
            continue
        if params.rung_radius and not any(non_base):
            radius = params.rung_radius
        # elif r0.ribbon_display and r1.ribbon_display:
        #     mgr = mol.ribbon_xs_mgr
        #     radius = min(mgr.scale_nucleic)
        else:
            # TODO: radius = a0.structure.stickScale \
            #     * chimera.Molecule.DefaultBondRadius
            radius = a0.structure.bond_radius
        key = (r0, r1)
        if key in depict_bonds:
            prev_radius = depict_bonds[key][2]
            if prev_radius >= radius:
                continue
        depict_bonds[key] = (c3p0, c3p1, radius, non_base)

    matched_residues = set()
    if not params.stubs_only:
        for (r0, r1), (c3p0, c3p1, radius, non_base) in depict_bonds.items():
            a0 = r0.find_atom("C2")
            a1 = r1.find_atom("C2")
            r0color = r0.ring_color
            r1color = r1.ring_color
            # choose mid-point to make purine larger
            try:
                is_purine0 = standard_bases[nucleic3to1(
                    r0.name)]['tag'] == PURINE
                is_purine1 = standard_bases[nucleic3to1(
                    r1.name)]['tag'] == PURINE
            except KeyError:
                is_purine0 = False
                is_purine1 = False
            if any(non_base) or is_purine0 == is_purine1:
                mid = 0.5
            elif is_purine0:
                mid = purine_pyrimidine_ratio
            else:
                mid = 1.0 - purine_pyrimidine_ratio
            midpt = c3p0[1] + mid * (c3p1[1] - c3p0[1])
            va, na, ta = get_cylinder(radius, c3p0[1], midpt, top=False)
            all_shapes.append(
                AtomicShapeInfo(va, na, ta, r0color, r0.atoms, str(r0)))
            va, na, ta = get_cylinder(radius, c3p1[1], midpt, top=False)
            all_shapes.append(
                AtomicShapeInfo(va, na, ta, r1color, r1.atoms, str(r1)))
            if not non_base[0]:
                matched_residues.add(r0)
            if not non_base[1]:
                matched_residues.add(r1)

    if not params.show_stubs:
        if params.hide:
            return all_shapes, matched_residues
        return all_shapes, ()
    # draw stubs for unmatched nucleotide residues
    for r in residues:
        if r in matched_residues:
            continue
        c3p = _c3pos(r)
        if not c3p:
            continue
        ep0 = c3p[1]
        a = r.find_atom("C2")
        color = r.ring_color
        ep1 = None
        name = nucleic3to1(r.name)
        if name not in standard_bases:
            continue
        is_purine = standard_bases[name]['tag'] == PURINE
        if is_purine:
            a = r.find_atom('N1')
            if a:
                ep1 = a.coord
        else:
            # pyrimidine
            a = r.find_atom('N3')
            if a:
                ep1 = a.coord
        if ep1 is None:
            # find farthest atom from C3'
            dist_atom = (0, None)
            for a in r.atoms:
                dist = distance_squared(ep0, a.coord)
                if dist > dist_atom[0]:
                    dist_atom = (dist, a)
            ep1 = dist_atom[1].coord
        va, na, ta = get_cylinder(params.rung_radius, ep0, ep1)
        all_shapes.append(AtomicShapeInfo(va, na, ta, color, r.atoms, str(r)))
        # make exposed end rounded (TODO: use a hemisphere)
        va, na, ta = get_sphere(params.rung_radius, ep1)
        all_shapes.append(AtomicShapeInfo(va, na, ta, color, r.atoms, str(r)))
        matched_residues.add(r)
    if params.hide:
        return all_shapes, matched_residues
    return all_shapes, ()