Exemplo n.º 1
0
def make_fmet_from_met(session, residue):
    err_string = "make_fmet_from_met() is only applicable to N-terminal methionine residues!"
    if residue.name != 'MET':
        raise TypeError(err_string)
    n = residue.find_atom('N')
    if n is None:
        raise TypeError('Missing N atom! Is your residue complete?')
    for nb in n.neighbors:
        if nb.residue != residue:
            raise TypeError(err_string)
        if nb.element.name == 'H':
            nb.delete()
    from chimerax.mmcif import find_template_residue
    import numpy
    fme = find_template_residue(session, 'FME')
    ref_atoms = ('C', 'CA', 'N')
    r_coords = numpy.array([residue.find_atom(a).coord for a in ref_atoms])
    t_coords = numpy.array([fme.find_atom(a).coord for a in ref_atoms])
    from chimerax.geometry import align_points
    tf, rms = align_points(t_coords, r_coords)
    from chimerax.atomic.struct_edit import add_atom
    atom_pairs = (('N', 'H'), ('N', 'CN'), ('CN', 'O1'), ('CN', 'HCN'))
    for (bname, aname) in atom_pairs:
        ta = fme.find_atom(aname)
        add_atom(aname,
                 ta.element,
                 residue,
                 tf * ta.coord,
                 bonded_to=residue.find_atom(bname))
    residue.name = 'FME'
Exemplo n.º 2
0
def add_hydrogen_to_atom(atom, coord, name=None):
    '''
    Add a single hydrogen atom to the given heavy atom, at the given coordinate.
    Simple-minded tool, taking no notice of chemistry or geometry.
    '''
    from chimerax.atomic import Element
    r = atom.residue
    s = atom.structure
    if name is not None:
        if name in r.atoms.names:
            raise TypeError('This atom name is already taken!')
    else:
        existing_names = [
            a.name for a in atom.neighbors if a.element.name == 'H'
        ]
        if len(existing_names):
            last_digits = [
                int(n[-1]) for n in existing_names if n[-1].isdigit()
            ]
            if len(last_digits):
                num = max(last_digits) + 1
            else:
                num = 1
        else:
            num = 1

        name = 'H' + atom.name[1:] + str(num)

    from chimerax.atomic.struct_edit import add_atom
    na = add_atom(name, Element.get_element('H'), r, coord, bonded_to=atom)
    return na
Exemplo n.º 3
0
def place_helium(structure, res_name, position=None):
    '''If position is None, place in the center of view'''
    max_existing = 0
    for r in structure.residues:
        if r.chain_id == "het" and r.number > max_existing:
            max_existing = r.number
    res = structure.new_residue(res_name, "het", max_existing + 1)
    if position is None:
        if len(structure.session.models) == 0:
            position = (0.0, 0.0, 0.0)
        else:
            #view = structure.session.view
            #n, f = view.near_far_distances(view.camera, None)
            #position = view.camera.position.origin() + (n+f) * view.camera.view_direction() / 2

            # apparently the commented-out code above is equivalent to...
            position = structure.session.main_view.center_of_rotation

    from numpy import array
    position = array(position)
    from chimerax.atomic.struct_edit import add_atom
    helium = Element.get_element("He")
    a = add_atom("He", helium, res, position)
    from chimerax.atomic.colors import element_color
    a.color = element_color(helium.number)
    a.draw_mode = a.BALL_STYLE
    return a
Exemplo n.º 4
0
def new_hydrogen(parent_atom, h_num, total_hydrogens, naming_schema, pos,
                 parent_type_info, alt_loc):
    global _serial, _metals
    nearby_metals = _metals.search(pos,
                                   _metal_dist) if _metal_dist > 0.0 else []
    for metal in nearby_metals:
        if metal.structure != parent_atom.structure:
            continue
        metal_pos = metal.coord
        parent_pos = parent_atom.coord
        if metal_clash(metal_pos, pos, parent_pos, parent_atom,
                       parent_type_info):
            return
    # determine added H color before actually adding it...
    h_color = determine_h_color(parent_atom)
    new_h = add_atom(_h_name(parent_atom, h_num, total_hydrogens,
                             naming_schema),
                     "H",
                     parent_atom.residue,
                     pos,
                     serial_number=_serial,
                     bonded_to=parent_atom,
                     alt_loc=alt_loc)
    _serial = new_h.serial_number + 1
    new_h.color = h_color
    new_h.hide = parent_atom.hide
    return new_h
Exemplo n.º 5
0
def place_metal_at_coord(model,
                         chain_id,
                         residue_number,
                         residue_name,
                         atom_name,
                         coord,
                         element_name=None,
                         bfactor=20):
    '''
    Create a new residue encompassing a single metal ion in the current model,
    and place it at the given coordinate.
    '''
    if element_name is None:
        element_name = atom_name.title()
    from chimerax.atomic import Element
    from chimerax.atomic.struct_edit import add_atom
    e = Element.get_element(element_name)
    r = model.new_residue(residue_name, chain_id, residue_number)
    add_atom(atom_name, e, r, coord, bfactor=bfactor)
    return r
Exemplo n.º 6
0
def cmd_centroid(session, atoms=None, *, mass_weighting=False, name="centroid", color=None, radius=2.0):
    """Wrapper to be called by command line.

       Use chimerax.centroids.centroid 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")

    structures = atoms.unique_structures
    if len(structures) > 1:
        crds = atoms.scene_coords
    else:
        crds = atoms.coords
    if mass_weighting:
        masses = atoms.elements.masses
        avg_mass = masses.sum() / len(masses)
        import numpy
        weights = masses[:, numpy.newaxis] / avg_mass
    else:
        weights = None
    xyz = centroid(crds, weights=weights)
    s = Structure(session, name=name)
    r = s.new_residue('centroid', 'centroid', 1)
    from chimerax.atomic.struct_edit import add_atom
    a = add_atom('cent', 'C', r, xyz)
    if color:
        a.color = color.uint8x4()
    else:
        from chimerax.atomic.colors import element_color, predominant_color
        color = predominant_color(atoms)
        if color is None:
            a.color = element_color(a.element.number)
        else:
            a.color = color
    a.radius = radius
    if len(structures) > 1:
        session.models.add([s])
    else:
        structures[0].add([s])

    session.logger.info("Centroid '%s' placed at %s" % (name, xyz))
    return a
Exemplo n.º 7
0
def complete_terminal_carboxylate(session, cter):
    from chimerax.atomic.bond_geom import bond_positions
    from chimerax.atomic.struct_edit import add_atom
    from chimerax.atomic import Element
    if cter.find_atom("OXT"):
        return
    c = cter.find_atom("C")
    if c:
        if c.num_bonds != 2:
            return
        loc = bond_positions(c.coord, 3, 1.229,
                             [n.coord for n in c.neighbors])[0]
        oxt = add_atom("OXT", Element.get_element("O"), cter, loc, bonded_to=c)
        from chimerax.atomic.colors import element_color
        if c.color == element_color(c.element.number):
            oxt.color = element_color(oxt.element.number)
        else:
            oxt.color = c.color
        session.logger.info("Missing OXT added to C-terminal residue %s" %
                            str(cter))
Exemplo n.º 8
0
def new_residue_from_template(model,
                              template,
                              chain_id,
                              center,
                              residue_number=None,
                              insert_code=' ',
                              b_factor=50,
                              precedes=None):
    '''
    Create a new residue based on a template, and add it to the model.
    '''
    if residue_number is None:
        if chain_id in model.residues.chain_ids:
            residue_number = suggest_new_residue_number_for_ligand(
                model, chain_id)
        else:
            residue_number = 0
    import numpy
    from chimerax.atomic import Atom
    t_coords = numpy.array([a.coord for a in template.atoms])
    t_center = t_coords.mean(axis=0)
    t_coords += numpy.array(center) - t_center
    tatom_to_atom = {}
    r = model.new_residue(template.name,
                          chain_id,
                          residue_number,
                          insert=insert_code,
                          precedes=precedes)
    from chimerax.atomic.struct_edit import add_bond, add_atom
    for i, ta in enumerate(template.atoms):
        a = tatom_to_atom[ta] = add_atom(ta.name,
                                         ta.element,
                                         r,
                                         t_coords[i],
                                         bfactor=b_factor)
        for tn in ta.neighbors:
            n = tatom_to_atom.get(tn, None)
            if n is not None:
                add_bond(a, n)
    return r
Exemplo n.º 9
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)
Exemplo n.º 10
0
def build_next_atom_from_coords(residue, found_neighbors, template_new_atom):
    # print('Building next atom {} from aligned coords of {}'.format(template_new_atom.name,
    #     ','.join([f[0].name for f in found_neighbors])))
    bonded_to = [f[0] for f in found_neighbors]
    m = residue.structure
    n = len(found_neighbors)
    found = set(f[0] for f in found_neighbors)
    while len(found_neighbors) < 3:
        for ra, ta in found_neighbors:
            for tn in ta.neighbors:
                rn = residue.find_atom(tn.name)
                if rn and rn not in found:
                    found_neighbors.append((rn, tn))
                    found.add(rn)
        if len(found_neighbors) <= n:
            residue.session.logger.warning(
                "Couldn't find more than two neighbor atoms. Falling back to simple geometry-based addition."
            )
            return build_next_atom_from_geometry(residue, *found_neighbors[0],
                                                 template_new_atom)
        n = len(found_neighbors)
    from chimerax.geometry import align_points
    import numpy
    ra_coords = numpy.array([f[0].coord for f in found_neighbors])
    ta_coords = numpy.array([f[1].coord for f in found_neighbors])
    bfactor = numpy.mean([f[0].bfactor for f in found_neighbors])
    occupancy = numpy.mean([f[0].occupancy for f in found_neighbors])
    tf, rms = align_points(ta_coords, ra_coords)
    from chimerax.atomic.struct_edit import add_atom
    ta = template_new_atom
    a = add_atom(ta.name,
                 ta.element,
                 residue,
                 tf * ta.coord,
                 occupancy=occupancy,
                 bfactor=bfactor)
    from chimerax.atomic.struct_edit import add_bond
    for b in bonded_to:
        add_bond(a, b)
Exemplo n.º 11
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)
Exemplo n.º 12
0
def use_rotamer(session, res, rots, retain=False, log=False, bfactor=None):
    """Takes a Residue instance and either a list or dictionary of rotamers (as returned by get_rotamers,
       i.e. with backbone already matched) and swaps the Residue's side chain with the given rotamers.

       If the rotamers are a dictionary, then the keys should match the alt locs of the CA atom, and
       the corresponding rotamer will be used for that alt loc.  If the alt locs are a list, if the list
       has only one rotamer then that rotamer will be used for each CA alt loc.  If the list has multiple
       rotamers, then the CA must have only one alt loc (namely ' ') and all the rotamers will be attached,
       using different alt loc characters for each.

       If 'retain' is True, existing side chains will be retained.  If 'bfactor' is None, then the
       current highest existing bfactor in the residue will be used.
    """
    N = res.find_atom("N")
    CA = res.find_atom("CA")
    C = res.find_atom("C")
    if not N or not C or not CA:
        raise LimitationError(
            "N, CA, or C missing from %s: needed for side-chain pruning algorithm"
            % res)
    import string
    alt_locs = string.ascii_uppercase + string.ascii_lowercase + string.digits + string.punctuation
    if retain and CA.alt_locs:
        raise LimitationError(
            "Cannot retain side chains if multiple CA alt locs")
    ca_alt_locs = [' '] if not CA.alt_locs else CA.alt_locs
    if not isinstance(rots, dict):
        # reformat as dictionary
        if CA.alt_locs and len(rots) > 1:
            raise LimitationError(
                "Cannot add multiple rotamers to multi-position backbone")
        retained_alt_locs = side_chain_locs(res) if retain else []
        num_retained = len(retained_alt_locs)
        if len(rots) + num_retained > len(alt_locs):
            raise LimitationError("Don't have enough unique alternate "
                                  "location characters to place %d rotamers." %
                                  len(rots))
        if len(rots) + num_retained > 1:
            rots = {
                loc: rot
                for loc, rot in zip(
                    [c for c in alt_locs
                     if c not in retained_alt_locs][:len(rots)], rots)
            }
        else:
            rots = {alt_loc: rots[0] for alt_loc in ca_alt_locs}
    swap_type = list(rots.values())[0].residues[0].name
    if retain and res.name != swap_type:
        raise LimitationError(
            "Cannot retain side chains if rotamers are a different residue type"
        )
    rot_anchors = {}
    for rot in rots.values():
        rot_res = rot.residues[0]
        rot_N, rot_CA = rot_res.find_atom("N"), rot_res.find_atom("CA")
        if not rot_N or not rot_CA:
            raise LimitationError(
                "N or CA missing from rotamer: cannot matchup with original residue"
            )
        rot_anchors[rot] = (rot_N, rot_CA)
    color_by_element = N.color != CA.color
    if color_by_element:
        carbon_color = CA.color
    else:
        uniform_color = N.color
    # prune old side chain
    bfactor = bfactor_for_res(res, bfactor)
    if not retain:
        res_atoms = res.atoms
        side_atoms = res_atoms.filter(res_atoms.is_side_onlys)
        serials = {a.name: a.serial_number for a in side_atoms}
        side_atoms.delete()
    else:
        serials = {}
    # for proline, also prune amide hydrogens
    if swap_type == "PRO":
        for nnb in N.neighbors[:]:
            if nnb.element.number == 1:
                N.structure.delete_atom(nnb)

    tot_prob = sum([r.rotamer_prob for r in rots.values()])
    with CA.suppress_alt_loc_change_notifications():
        res.name = swap_type
        from chimerax.atomic.struct_edit import add_atom, add_bond
        for alt_loc, rot in rots.items():
            if CA.alt_locs:
                CA.alt_loc = alt_loc
            if log:
                extra = " using alt loc %s" % alt_loc if alt_loc != ' ' else ""
                session.logger.info(
                    "Applying %s rotamer (chi angles: %s) to %s%s" %
                    (rot_res.name, " ".join(["%.1f" % c
                                             for c in rot.chis]), res, extra))
            # add new side chain
            rot_N, rot_CA = rot_anchors[rot]
            visited = set([N, CA, C])
            sprouts = [rot_CA]
            while sprouts:
                sprout = sprouts.pop()
                built_sprout = res.find_atom(sprout.name)
                for nb in sprout.neighbors:
                    built_nb = res.find_atom(nb.name)
                    if tot_prob == 0.0:
                        # some rotamers in Dunbrack are zero prob!
                        occupancy = 1.0 / len(rots)
                    else:
                        occupancy = rot.rotamer_prob / tot_prob
                    if not built_nb:
                        serial = serials.get(nb.name, None)
                        built_nb = add_atom(nb.name,
                                            nb.element,
                                            res,
                                            nb.coord,
                                            serial_number=serial,
                                            bonded_to=built_sprout,
                                            alt_loc=alt_loc)
                        built_nb.occupancy = occupancy
                        built_nb.bfactor = bfactor
                        if color_by_element:
                            if built_nb.element.name == "C":
                                built_nb.color = carbon_color
                            else:
                                from chimerax.atomic.colors import element_color
                                built_nb.color = element_color(
                                    built_nb.element.number)
                        else:
                            built_nb.color = uniform_color
                    elif built_nb not in visited:
                        built_nb.set_alt_loc(alt_loc, True)
                        built_nb.coord = nb.coord
                        built_nb.occupancy = occupancy
                        built_nb.bfactor = bfactor
                    if built_nb not in visited:
                        sprouts.append(nb)
                        visited.add(built_nb)
                    if built_nb not in built_sprout.neighbors:
                        add_bond(built_sprout, built_nb)
Exemplo n.º 13
0
def get_rotamers(session,
                 res,
                 phi=None,
                 psi=None,
                 cis=False,
                 res_type=None,
                 rot_lib="Dunbrack",
                 log=False):
    """Takes a Residue instance and optionally phi/psi angles (if different from the Residue), residue
       type (e.g. "TYR"), and/or rotamer library name.  Returns a list of AtomicStructure instances (sublass of
       AtomicStructure).  The AtomicStructure are each a single residue (a rotamer) and are in descending
       probability order.  Each has an attribute "rotamer_prob" for the probability and "chis" for the
       chi angles.
    """
    res_type = res_type or res.name
    if res_type == "ALA" or res_type == "GLY":
        raise NoResidueRotamersError("No rotamers for %s" % res_type)

    if not isinstance(rot_lib, RotamerLibrary):
        rot_lib = session.rotamers.library(rot_lib)

    # check that the residue has the n/c/ca atoms needed to position the rotamer
    # and to ensure that it is an amino acid
    from chimerax.atomic import Residue
    match_atoms = {}
    for bb_name in Residue.aa_min_backbone_names:
        match_atoms[bb_name] = a = res.find_atom(bb_name)
        if a is None:
            raise LimitationError("%s missing from %s; needed to position CB" %
                                  (bb_name, res))
    match_atoms["CB"] = res.find_atom("CB")
    if not phi and not psi:
        phi, psi = res.phi, res.psi
        omega = res.omega
        cis = False if omega is None or abs(omega) > 90 else True
        if log:

            def _info(ang):
                if ang is None:
                    return "none"
                return "%.1f" % ang

            if match_atoms["CA"].alt_locs:
                al_info = " (alt loc %s)" % match_atoms["CA"].alt_loc
            else:
                al_info = ""
            session.logger.info("%s%s: phi %s, psi %s %s" %
                                (res, al_info, _info(phi), _info(psi),
                                 "cis" if cis else "trans"))
    session.logger.status("Retrieving rotamers from %s library" %
                          rot_lib.display_name)
    res_template_func = rot_lib.res_template_func
    params = rot_lib.rotamer_params(res_type, phi, psi, cis=cis)
    session.logger.status("Rotamers retrieved from %s library" %
                          rot_lib.display_name)

    mapped_res_type = rot_lib.res_name_mapping.get(res_type, res_type)
    template = rot_lib.res_template_func(mapped_res_type)
    tmpl_N = template.find_atom("N")
    tmpl_CA = template.find_atom("CA")
    tmpl_C = template.find_atom("C")
    tmpl_CB = template.find_atom("CB")
    if match_atoms['CB']:
        res_match_atoms, tmpl_match_atoms = [
            match_atoms[x] for x in ("C", "CA", "CB")
        ], [tmpl_C, tmpl_CA, tmpl_CB]
    else:
        res_match_atoms, tmpl_match_atoms = [
            match_atoms[x] for x in ("N", "CA", "C")
        ], [tmpl_N, tmpl_CA, tmpl_C]
    from chimerax.geometry import align_points
    from numpy import array
    xform, rmsd = align_points(array([fa.coord for fa in tmpl_match_atoms]),
                               array([ta.coord for ta in res_match_atoms]))
    n_coord = xform * tmpl_N.coord
    ca_coord = xform * tmpl_CA.coord
    cb_coord = xform * tmpl_CB.coord
    info = Residue.chi_info[mapped_res_type]
    bond_cache = {}
    angle_cache = {}
    from chimerax.atomic.struct_edit import add_atom, add_dihedral_atom, add_bond
    structs = []
    middles = {}
    ends = {}
    for i, rp in enumerate(params):
        s = AtomicStructure(session, name="rotamer %d" % (i + 1))
        structs.append(s)
        r = s.new_residue(mapped_res_type, 'A', 1)
        registerer = "swap_res get_rotamers"
        AtomicStructure.register_attr(session,
                                      "rotamer_prob",
                                      registerer,
                                      attr_type=float)
        s.rotamer_prob = rp.p
        AtomicStructure.register_attr(session, "chis", registerer)
        s.chis = rp.chis
        rot_N = add_atom("N", tmpl_N.element, r, n_coord)
        rot_CA = add_atom("CA", tmpl_CA.element, r, ca_coord, bonded_to=rot_N)
        rot_CB = add_atom("CB", tmpl_CB.element, r, cb_coord, bonded_to=rot_CA)
        todo = []
        for j, chi in enumerate(rp.chis):
            n3, n2, n1, new = info[j]
            b_len, angle = _len_angle(new, n1, n2, template, bond_cache,
                                      angle_cache)
            n3 = r.find_atom(n3)
            n2 = r.find_atom(n2)
            n1 = r.find_atom(n1)
            new = template.find_atom(new)
            a = add_dihedral_atom(new.name,
                                  new.element,
                                  n1,
                                  n2,
                                  n3,
                                  b_len,
                                  angle,
                                  chi,
                                  bonded=True)
            todo.append(a)
            middles[n1] = [a, n1, n2]
            ends[a] = [a, n1, n2]

        # if there are any heavy non-backbone atoms bonded to template
        # N and they haven't been added by the above (which is the
        # case for Richardson proline parameters) place them now
        for tnnb in tmpl_N.neighbors:
            if r.find_atom(tnnb.name) or tnnb.element.number == 1:
                continue
            tnnb_coord = xform * tnnb.coord
            add_atom(tnnb.name, tnnb.element, r, tnnb_coord, bonded_to=rot_N)

        # fill out bonds and remaining heavy atoms
        from chimerax.geometry import distance, align_points
        done = set([rot_N, rot_CA])
        while todo:
            a = todo.pop(0)
            if a in done:
                continue
            tmpl_A = template.find_atom(a.name)
            for bonded, bond in zip(tmpl_A.neighbors, tmpl_A.bonds):
                if bonded.element.number == 1:
                    continue
                rbonded = r.find_atom(bonded.name)
                if rbonded is None:
                    # use middles if possible...
                    try:
                        p1, p2, p3 = middles[a]
                        conn = p3
                    except KeyError:
                        p1, p2, p3 = ends[a]
                        conn = p2
                    t1 = template.find_atom(p1.name)
                    t2 = template.find_atom(p2.name)
                    t3 = template.find_atom(p3.name)
                    xform = align_points(
                        array([t.coord for t in [t1, t2, t3]]),
                        array([p.coord for p in [p1, p2, p3]]))[0]
                    pos = xform * template.find_atom(bonded.name).coord
                    rbonded = add_atom(bonded.name,
                                       bonded.element,
                                       r,
                                       pos,
                                       bonded_to=a)
                    middles[a] = [rbonded, a, conn]
                    ends[rbonded] = [rbonded, a, conn]
                if a not in rbonded.neighbors:
                    add_bond(a, rbonded)
                if rbonded not in done:
                    todo.append(rbonded)
            done.add(a)
    return structs
Exemplo n.º 14
0
def _read_block(session, stream, line_number):
    # XYZ files are stored in blocks, with each block representing
    # a set of atoms.  This function reads a single block
    # and builds a ChimeraX AtomStructure instance containing
    # the atoms listed in the block.

    # First line should be an integer count of the number of
    # atoms in the block.
    count_line = stream.readline()
    if not count_line:
        # Reached EOF, normal termination condition
        return None, line_number
    line_number += 1
    try:
        count = int(count_line)
    except ValueError:
        session.logger.error("line %d: atom count missing" % line_number)
        return None, line_number

    # Create the AtomicStructure instance for atoms in this block.
    # All atoms in the structure are placed in one residue
    # since XYZ format does not partition atoms into groups.
    from chimerax.atomic import AtomicStructure
    from numpy import array, float64
    s = AtomicStructure(session)
    residue = s.new_residue("UNK", 'A', 1)

    # XYZ format supplies the atom element type only, but
    # ChimeraX keeps track of both the element type and
    # a unique name for each atom.  To construct the unique
    # atom name, the # 'element_count' dictionary is used
    # to track the number of atoms of each element type so far,
    # and the current count is used to build unique atom names.
    element_count = {}

    # Next line is a comment line
    s.comment = stream.readline().strip()
    line_number += 1

    # import convenience function for adding atoms
    from chimerax.atomic.struct_edit import add_atom

    # There should be "count" lines of atoms.
    for n in range(count):
        atom_line = stream.readline()
        if not atom_line:
            session.logger.error("line %d: atom data missing" % line_number)
            return None, line_number
        line_number += 1

        # Extract available data
        parts = atom_line.split()
        if len(parts) != 4:
            session.logger.error("line %d: atom data malformatted" %
                                 line_number)
            return None, line_number

        # Convert to required parameters for creating atom.
        # Since XYZ format only required atom element, we
        # create a unique atom name by putting a number after
        # the element name.
        xyz = [float(v) for v in parts[1:]]
        element = parts[0]
        n = element_count.get(element, 0) + 1
        name = element + str(n)
        element_count[element] = n

        # Create atom in AtomicStructure instance 's',
        # set its coordinates, and add to residue
        atom = add_atom(name, element, residue, array(xyz, dtype=float64))

    # Use AtomicStructure method to add bonds based on interatomic distances
    s.connect_structure()

    # Return AtomicStructure instance and current line number
    return s, line_number
Exemplo n.º 15
0
def read_sdf(session, stream, file_name):

    path = stream.name if hasattr(stream, 'name') else None

    structures = []
    nonblank = False
    state = "init"
    from chimerax.core.errors import UserError
    from chimerax.atomic.struct_edit import add_atom
    from chimerax.atomic import AtomicStructure, Element, Bond, Atom, AtomicStructure
    from numpy import array
    Bond.register_attr(session, "order", "SDF format", attr_type=float)
    Atom.register_attr(session, "charge", "SDF format", attr_type=float)
    AtomicStructure.register_attr(session, "charge_model", "SDF format", attr_type=str)
    try:
        for l in stream:
            line = l.strip()
            nonblank = nonblank or line
            if state == "init":
                state = "post header 1"
                mol_name = line
            elif state == "post header 1":
                state = "post header 2"
            elif state == "post header 2":
                state = "counts"
            elif state == "counts":
                if not line:
                    break
                state = "atoms"
                serial = 1
                anums = {}
                atoms = []
                try:
                    num_atoms = int(l[:3].strip())
                    num_bonds = int(l[3:6].strip())
                except ValueError:
                    raise UserError("Atom/bond counts line of MOL/SDF file '%s' is botched" % file_name)
                from chimerax.atomic.structure import is_informative_name
                name = mol_name if is_informative_name(mol_name) else file_name
                s = AtomicStructure(session, name=name)
                structures.append(s)
                r = s.new_residue("UNL", " ", 1)
            elif state == "atoms":
                num_atoms -= 1
                if num_atoms == 0:
                    if num_bonds:
                        state = "bonds"
                    else:
                        state = "properties"
                try:
                    x = float(l[:10].strip())
                    y = float(l[10:20].strip())
                    z = float(l[20:30].strip())
                    elem = l[31:34].strip()
                except ValueError:
                    s.delete()
                    raise UserError("Atom line of MOL/SDF file '%s' is not x y z element...: '%s'"
                        % (file_name, l))
                element = Element.get_element(elem)
                if element.number == 0:
                    # lone pair of somesuch
                    atoms.append(None)
                    continue
                anum = anums.get(element.name, 0) + 1
                anums[element.name] = anum
                a = add_atom("%s%d" % (element.name, anum), element, r, array([x,y,z]), serial_number=serial)
                serial += 1
                atoms.append(a)
            elif state == "bonds":
                num_bonds -= 1
                if num_bonds == 0:
                    state = "properties"
                try:
                    a1_index = int(l[:3].strip())
                    a2_index = int(l[3:6].strip())
                    order = float(l[6:9].strip())
                except ValueError:
                    raise UserError("Bond line of MOL/SDF file '%s' is not a1 a2 order...: '%s'"
                        % (file_name, 1))
                a1 = atoms[a1_index-1]
                a2 = atoms[a2_index-1]
                if not a1 or not a2:
                    continue
                s.new_bond(a1, a2).order = order
            elif state == "properties":
                if not s.atoms:
                    raise UserError("No atoms found for compound '%s' in MOL/SDF file '%s'" % (name, file_name))
                if line.split() == ["M", "END"]:
                    state = "data"
                    reading_data = None
            elif state == "data":
                if line == "$$$$":
                    nonblank = False
                    state = "init"
                elif reading_data == "charges":
                    data_item = line.strip()
                    if data_item:
                        try:
                            data.append(float(data_item))
                        except ValueError:
                            try:
                                index, charge = data_item.split()
                                index = int(index) - 1
                                charge = float(charge)
                            except ValueError:
                                raise UserError("Charge data (%s) in %s data is not either a floating-point"
                                    " number or an atom index and a floating-point number" % (data_item,
                                    orig_data_name))
                            else:
                                if not indexed_charges:
                                    # for indexed charges, the first thing is a count
                                    data.pop()
                                    indexed_charges = True
                                data.append((index, charge))
                    else:
                        if not indexed_charges and len(atoms) != len(data):
                            raise UserError("Number of charges (%d) in %s data not equal to number of atoms"
                                " (%d)" % (len(data), orig_data_name, len(atoms)))
                        if indexed_charges:
                            for a in atoms:
                                # charge defaults to 0.0, so don't need to set non-indexed
                                for index, charge in data:
                                    atoms[index].charge = charge
                        else:
                            for a, charge in zip(atoms, data):
                                a.charge = charge
                        if "mmff94" in data_name:
                            s.charge_model = "MMFF94"
                        reading_data = None
                elif reading_data == "cid":
                    data_item = line.strip()
                    if data_item:
                        try:
                            cid = int(data_item)
                        except ValueError:
                            raise UserError("PubChem CID (%s) is %s data is not an integer" % (data_item,
                                orid_data_name))
                        s.name = "pubchem:%d" % cid
                        s.prefix_html_title = False
                        s.get_html_title = lambda *args, cid=cid: 'PubChem entry <a href="https://pubchem.ncbi.nlm.nih.gov/compound/%d">%d</a>' % (cid, cid)
                        s.has_formatted_metadata = lambda *args: False
                        reading_data = None
                elif line.startswith('>'):
                    try:
                        lp = line.index('<')
                        rp = line[lp+1:].index('>') + lp + 1
                    except (IndexError, ValueError):
                        continue
                    orig_data_name = line[lp+1:rp]
                    data_name = orig_data_name.lower()
                    if data_name.endswith("charges") and "partial" in data_name:
                        reading_data = "charges"
                        indexed_charges = False
                        data = []
                    elif data_name == "pubchem_compound_cid":
                        reading_data = "cid"
    except BaseException:
        for s in structures:
            s.delete()
        raise
    finally:
        stream.close()

    if nonblank and state not in ["data", "init"]:
        if structures:
            session.logger.warning("Extraneous text after final $$$$ in MOL/SDF file '%s'" % file_name)
        else:
            raise UserError("Unexpected end of file (parser state: %s) in MOL/SDF file '%s'"
                % (state, file_name))

    return structures, ""
Exemplo n.º 16
0
def create_merged_residue(residues, base_residue=None, new_name=None):
    if base_residue is None:
        base_residue = residues[0]
    if base_residue not in residues:
        raise TypeError('base_residue must be a member of residues!')
    if new_name is None:
        new_name = base_residue.name
    from chimerax.atomic import Residues
    from chimerax.atomic.struct_edit import add_atom, add_bond
    residues = Residues(residues)
    seen = set()
    for r in residues:
        for n in r.neighbors:
            seen.add(n)
    if not all([r in seen for r in residues]):
        raise TypeError(
            'All residues must be connected through covalent bonds!')
    atoms = residues.atoms
    other_residues = residues.subtract(Residues([base_residue]))
    bonds = atoms.intra_bonds
    external_bonds = []
    for r in seen:
        if r not in other_residues:
            for n in r.neighbors:
                if n in residues:
                    external_bonds.extend(r.bonds_between(n))
    next_atom_number = highest_atom_number(base_residue.atoms) + 1

    atom_map = {}
    other_atoms = other_residues.atoms
    # Do heavy atoms first, so we can assign hydrogen names based on them
    other_heavy_atoms = other_atoms[other_atoms.element_names != 'H']
    for a in other_heavy_atoms:
        name = a.element.name + str(next_atom_number)
        next_atom_number += 1
        atom_map[a] = add_atom(name,
                               a.element,
                               base_residue,
                               a.coord,
                               occupancy=a.occupancy,
                               bfactor=a.bfactor)
    # Add the hydrogens
    for a, na in atom_map.items():
        base_number = na.name[1:]
        i = 1
        for n in a.neighbors:
            if n.element.name == 'H':
                name = 'H' + base_number + str(i)
                i += 1
                add_atom(name,
                         n.element,
                         base_residue,
                         n.coord,
                         bonded_to=na,
                         occupancy=n.occupancy,
                         bfactor=n.bfactor)
    # Add all internal bonds
    for a, na in atom_map.items():
        for n in a.neighbors:
            nn = atom_map.get(n, None)
            if nn is not None and nn not in na.neighbors:
                add_bond(na, nn)
    # Now we just need to add the bonds between base_residue and the merged portion, and any
    # other bonds between the merged portion and the wider model. To avoid valence errors we'll need
    # to first delete the existing bonds.
    for b in external_bonds:
        atoms = b.atoms
        if atoms[1] in atom_map.keys():
            atoms = atoms[::-1]
        na = atom_map[atoms[0]]
        a = atoms[1]
        b.delete()
        add_bond(na, a)
    other_residues.delete()
    base_residue.name = new_name
Exemplo n.º 17
0
def place_peptide(structure,
                  sequence,
                  phi_psis,
                  *,
                  position=None,
                  rot_lib=None,
                  chain_id=None):
    """
    Place a peptide sequence.

    *structure* is an AtomicStructure to add the peptide to.

    *sequence* contains the sequence as a string.

    *phi_psis* is a list of phi/psi tuples, one per residue.

    *position* is either an array or sequence specifying an xyz world coordinate position or None.
    If None, the peptide is positioned at the center of the view.

    *rot_lib* is the name of the rotamer library to use to position the side chains.
    session.rotamers.library_names lists installed rotamer library names.  If None, a default
    library will be used.

    *chain_id* is the desired chain ID for the peptide.  If None, earliest alphabetical chain ID
    not already in use in the structure will be used (upper case taking precedence over lower
    case).

    returns a Residues collection of the added residues
    """

    if not sequence:
        raise PeptideError("No sequence supplied")
    sequence = sequence.upper()
    if not sequence.isupper():
        raise PeptideError("Sequence contains non-alphabetic characters")
    from chimerax.atomic import Sequence
    for c in sequence:
        try:
            r3 = Sequence.protein1to3[c]
        except KeyError:
            raise PeptideError("Unrecognized protein 1-letter code: %s" % c)
        if r3[-1] == 'X':
            raise PeptideError(
                "Peptide sequence cannot contain ambiguity codes (i.e. '%s')" %
                c)

    if len(sequence) != len(phi_psis):
        raise PeptideError("Number of phi/psis not equal to sequence length")

    session = structure.session

    open_models = session.models[:]
    if len(open_models) == 0:
        need_focus = True
    elif len(open_models) == 1:
        if open_models[0] == structure:
            need_focus = not structure.atoms
        else:
            need_focus = False
    else:
        need_focus = False

    if position is None:
        position = session.main_view.center_of_rotation
    from numpy import array
    position = array(position)

    prev = [None] * 3
    pos = 1
    from chimerax.atomic.struct_edit import DIST_N_C, DIST_CA_N, DIST_C_CA, DIST_C_O, \
        find_pt, add_atom, add_dihedral_atom
    serial_number = None
    residues = []
    prev_psi = 0
    if chain_id is None:
        chain_id = unused_chain_id(structure)
    from numpy import array
    for c, phi_psi in zip(sequence, phi_psis):
        phi, psi = phi_psi
        while structure.find_residue(chain_id, pos):
            pos += 1
        r = structure.new_residue(Sequence.protein1to3[c], chain_id, pos)
        residues.append(r)
        for backbone, dist, angle, dihed in [('N', DIST_N_C, 116.6, prev_psi),
                                             ('CA', DIST_CA_N, 121.9, 180.0),
                                             ('C', DIST_C_CA, 110.1, phi)]:
            if prev[0] is None:
                pt = array([0.0, 0.0, 0.0])
            elif prev[1] is None:
                pt = array([dist, 0.0, 0.0])
            elif prev[2] is None:
                pt = find_pt(prev[0].coord, prev[1].coord,
                             array([0.0, 1.0, 0.0]), dist, angle, 0.0)
            else:
                pt = find_pt(prev[0].coord, prev[1].coord, prev[2].coord, dist,
                             angle, dihed)
            a = add_atom(backbone,
                         backbone[0],
                         r,
                         pt,
                         serial_number=serial_number,
                         bonded_to=prev[0])
            serial_number = a.serial_number + 1
            prev = [a] + prev[:2]
        o = add_dihedral_atom("O",
                              "O",
                              prev[0],
                              prev[1],
                              prev[2],
                              DIST_C_O,
                              120.4,
                              180 + psi,
                              bonded=True)
        prev_psi = psi
    # C terminus O/OXT at different angle than mainchain O
    structure.delete_atom(o)
    add_dihedral_atom("O",
                      "O",
                      prev[0],
                      prev[1],
                      prev[2],
                      DIST_C_O,
                      117.0,
                      180 + psi,
                      bonded=True)
    add_dihedral_atom("OXT",
                      "O",
                      prev[0],
                      prev[1],
                      prev[2],
                      DIST_C_O,
                      117.0,
                      psi,
                      bonded=True)

    from chimerax.atomic import Residues
    residues = Residues(residues)

    from chimerax.swap_res import swap_aa
    # swap_aa is capable of swapping all residues in one call, but need to process one by one
    # since side-chain clashes are only calculated against pre-existing side chains
    kw = {}
    if rot_lib:
        kw['rot_lib'] = rot_lib
    for r in residues:
        swap_aa(session, [r], "same", criteria="cp", log=False, **kw)

    # find peptide center
    atoms = residues.atoms
    coords = atoms.coords
    center = coords.mean(0)
    correction = position - center
    atoms.coords = coords - correction

    from chimerax.std_commands.dssp import compute_ss
    compute_ss(session, structure)

    if need_focus:
        from chimerax.core.commands import run
        run(session, "view")
    return residues