def _straight_initial(self): from numpy import mean, dot, newaxis from numpy.linalg import norm if len(self.coords) > len(self.IDEAL_COORDS): # "Normal" helices can be approximated by using all # coordinates on the assumption that the helix length # is sufficiently larger than the helix radius that # the biggest eigenvector will be the helical axis from numpy.linalg import svd from numpy import argmax centroid = mean(self.coords, axis=0) rel_coords = self.coords - centroid ignore, vals, vecs = svd(rel_coords) axis = vecs[argmax(vals)] else: from chimerax.geometry import align_points num_pts = len(self.coords) tf, rmsd = align_points(self.IDEAL_COORDS[:num_pts], self.coords) centroid = tf * self.IDEAL_PARAMS[0] axis = tf.transform_vector(self.IDEAL_PARAMS[1]) rel_coords = self.coords - centroid axis_pos = dot(rel_coords, axis)[:, newaxis] radial_vecs = rel_coords - axis * axis_pos radius = mean(norm(radial_vecs, axis=1)) return centroid, axis, radius
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'
def align_and_prune(xyz, ref_xyz, cutoff_distance, indices=None): import numpy if indices is None: indices = numpy.arange(len(xyz)) axyz, ref_axyz = xyz[indices], ref_xyz[indices] from chimerax.geometry import align_points p, rms = align_points(axyz, ref_axyz) dxyz = p * axyz - ref_axyz d2 = (dxyz * dxyz).sum(axis=1) cutoff2 = cutoff_distance * cutoff_distance i = d2.argsort() if d2[i[-1]] <= cutoff2: return p, rms, indices # cull 10% or... index1 = int(len(d2) * 0.9) # cull half the long pairings index2 = int(((d2 <= cutoff2).sum() + len(d2)) / 2) # whichever is fewer index = max(index1, index2) survivors = indices[i[:index]] if len(survivors) < 3: raise IterationError( "Alignment failed;" " pruning distances > %g left less than 3 atom pairs" % cutoff_distance) return align_and_prune(xyz, ref_xyz, cutoff_distance, survivors)
def minimize_link_lengths(mols, pbonds, iterations, frames, session): if len(mols) == 0: from chimerax.core.errors import UserError raise UserError('No structures specified for minimizing crosslinks.') mol_links, mol_pbonds = links_by_molecule(pbonds, mols) if len(mol_links) == 0: from chimerax.core.errors import UserError raise UserError('No pseudobonds between molecules.') if len(mols) == 1: iterations = min(1, iterations) if not frames is None: pos0 = dict((m, m.position) for m in mols) from numpy import array, float64 from chimerax.geometry import align_points for i in range(iterations): for m in mols: if m in mol_links: atom_pairs = mol_links[m] moving = array([a1.scene_coord for a1, a2 in atom_pairs], float64) fixed = array([a2.scene_coord for a1, a2 in atom_pairs], float64) tf, rms = align_points(moving, fixed) m.position = tf * m.position lengths = [pb.length for pb in mol_pbonds] lengths.sort(reverse=True) lentext = ', '.join('%.1f' % d for d in lengths) session.logger.info('%d crosslinks, lengths: %s' % (len(mol_pbonds), lentext)) if not frames is None: for m in mols: interpolate_position(m, pos0[m], m.position, frames, session.triggers)
def align_atoms(patoms, pto_atoms, xyz_to, cutoff_distance, atoms, to_atoms, move, log, report_matrix): xyz_from = patoms.scene_coords if cutoff_distance is None: from chimerax.geometry import align_points tf, rmsd = align_points(xyz_from, xyz_to) full_rmsd = rmsd matched_patoms, matched_pto_atoms = patoms, pto_atoms msg = 'RMSD between %d atom pairs is %.3f angstroms' % (len(patoms), rmsd) else: if cutoff_distance <= 0.0: raise UserError("Distance cutoff must be positive") tf, rmsd, indices = align_and_prune(xyz_from, xyz_to, cutoff_distance) np = len(indices) dxyz = tf * xyz_from - xyz_to d2 = (dxyz * dxyz).sum(axis=1) import math full_rmsd = math.sqrt(d2.sum() / len(d2)) matched_patoms, matched_pto_atoms = patoms[indices], pto_atoms[indices] msg = 'RMSD between %d pruned atom pairs is %.3f angstroms;' \ ' (across all %d pairs: %.3f)' % (np, rmsd, len(patoms), full_rmsd) if report_matrix: log.info(matrix_text(tf, atoms, to_atoms)) log.status(msg) log.info(msg) move_atoms(atoms, to_atoms, tf, move) return matched_patoms, matched_pto_atoms, rmsd, full_rmsd, tf
def show_residue_fit(session, residues, map, range = 2, last_pos = None, motion_frames = 20): '''Set camera to show first residue in list and display map in zone around given residues.''' res = residues[0] ratoms = res.atoms anames = tuple(ratoms.names) try: i = [anames.index(aname) for aname in ('N', 'CA', 'C')] except ValueError: return None # Missing backbone atom xyz = ratoms.filter(i).scene_coords # Align backbone to template backbone coords from chimerax.geometry import align_points, Place from numpy import array txyz = array([[ 12.83300018, 6.83900023, 6.73799992], [ 12.80800056, 7.87400055, 5.70799971], [ 11.91800022, 9.06700039, 5.9920001 ]]) p, rms = align_points(txyz, xyz) # Set camera view relative to template. c = session.main_view.camera cp = c.position if last_pos is None: tc = Place(((-0.46696,0.38225,-0.79739,-3.9125), (0.81905,-0.15294,-0.55296,-4.3407), (-0.33332,-0.91132,-0.24166,-1.4889))) if c.name == 'orthographic': c.field_width = 12 # Set orthographic field of view, Angstroms else: # Maintain same relative camera position to backbone. tc = last_pos.inverse() * cp # Smooth interpolation np = p*tc if motion_frames > 1: def interpolate_camera(session, f, cp=cp, np=np, center=np.inverse()*xyz[1], frames=motion_frames): c = session.main_view.camera p = np if f+1 == frames else cp.interpolate(np, center, frac = (f+1)/frames) c.position = p from chimerax.core.commands import motion motion.CallForNFrames(interpolate_camera, motion_frames, session) else: c.position = np from numpy import concatenate zone_points = concatenate([r.atoms.scene_coords for r in residues]) from chimerax.surface import zone for s in map.surfaces: zone.surface_zone(s, zone_points, range, auto_update = True) for r in residues: r.atoms.displays = True return p
def steady_transform(self, cset): tfc = self._steady_transforms if cset in tfc: return tfc[cset] atoms = self.steady_atoms coords = coordset_coords(atoms, cset, self.structure) if self._steady_coords is None: self._steady_coords = coords from chimerax.geometry import align_points tf = align_points(coords, self._steady_coords)[0] tfc[cset] = tf return tf
def _orient(self): gc = [] la = [] for g, (x, y) in zip(self.groups, self._layout_positions): if g.shown(): gc.append(g.centroid()) la.append((x, y, 0)) if len(gc) < 2: return from chimerax.geometry import align_points, translation from numpy import array, mean p, rms = align_points(array(gc), array(la)) ra = p.zero_translation() center = mean(gc, axis=0) v = self._session().main_view rc = v.camera.position.zero_translation() rot = translation(center) * rc * ra * translation(-center) v.move(rot)
def ncs_average_map(session, asu_map, full_map, reference_chain, ncs_chains): from chimerax.match_maker.match import ( defaults, align ) grid_points = asu_map.grid_points(asu_map.model_transform().inverse()) from chimerax.atomic import Residues, Atoms interpolated_maps = [] interpolated_maps.append(asu_map.data.matrix()) original_map_position = full_map.position dssp_cache={} for ncs in ncs_chains: score, s1, s2 = align(session, ncs, reference_chain, defaults['matrix'], 'nw', defaults['gap_open'], defaults['gap_extend'], dssp_cache) ref_residues = [] ncs_residues = [] for i, (mr, rr) in enumerate(zip(s1, s2)): if mr=='.' or rr=='.': continue ref_res = s1.residues[s1.gapped_to_ungapped(i)] match_res = s2.residues[s2.gapped_to_ungapped(i)] if not ref_res or not match_res: continue ref_residues.append(ref_res) ncs_residues.append(match_res) ref_residues = Residues(ref_residues) ncs_residues = Residues(ncs_residues) (ref_pa, ncs_pa) = paired_principal_atoms([ref_residues, ncs_residues]) from chimerax.geometry import align_points tf, rms = align_points(ncs_pa.coords, ref_pa.coords) #full_map.position = tf * full_map.position from chimerax.core.commands import run #run(session, "fitmap #{} in #{}".format(full_map.id_string, asu_map.id_string)) interpolated_maps.append( full_map.interpolated_values(grid_points, point_xform=tf.inverse()).reshape(asu_map.data.size)) #full_map.position = original_map_position asu_map.data.matrix()[:] = sum(interpolated_maps)/len(interpolated_maps) asu_map.data.values_changed()
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)
def optimized_placement(polygon, vertex_degree): lpairs = [] for l0 in polygon.edges: l0j = joined_edge(l0) if l0j: lpairs.append((l0, l0j)) from chimerax.geometry import Place, align_points if len(lpairs) == 0: tf = Place() elif len(lpairs) == 1: l0, l1 = lpairs[0] tf = edge_join_transform(l0, l1, vertex_degree) else: xyz0 = [] xyz1 = [] for l0, l1 in lpairs: xyz0.extend(edge_alignment_points(l0)) xyz1.extend(reversed(edge_alignment_points(l1))) from numpy import array tf, rms = align_points(array(xyz0), array(xyz1)) return tf
def __init__(self, residue_groups, method, coordset0, coordset1): # Get transform for each rigid segment t0 = time() calc_motion_parameters = segment_motion_methods[method] self.motion_parameters = motion_params = [] self.motion_transforms = motion_transforms = [] from .util import segment_alignment_atoms from chimerax.geometry import align_points from chimerax.atomic import Atoms for rList in residue_groups: aatoms = Atoms(segment_alignment_atoms(rList)) raindices = aatoms.coord_indices cList0 = coordset0[raindices] c0 = cList0.mean(axis = 0) cList1 = coordset1[raindices] c1 = cList1.mean(axis = 0) xform, rmsd = align_points(cList0, cList1) atom_indices = rList.atoms.coord_indices motion_transforms.append((atom_indices, xform)) axis, angle, center, shift = calc_motion_parameters(xform, c0, c1) motion_params.append((atom_indices, axis, angle, center, shift)) t1 = time() global stt stt += t1-t0
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
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
def add_amino_acid_residue(model, resname, prev_res=None, next_res=None, chain_id=None, number=None, center=None, insertion_code=' ', add_b_factor=0, occupancy=1, phi=-135, psi=135): session = model.session if (not chain_id or not number or center is None) and (not prev_res and not next_res): raise TypeError('If no anchor residues are specified, chain ID, ' 'number and center must be provided!') if prev_res and next_res: raise TypeError('Cannot specify both previous and next residues!') other_atom = None insertion_point = None import numpy if prev_res: ref_res = prev_res b_factor = prev_res.atoms[numpy.in1d( prev_res.atoms.names, ('N', 'CA', 'C', 'O', 'CB'))].bfactors.mean() + add_b_factor pri = model.residues.index(prev_res) if pri > 0 and pri < len(model.residues) - 1: insertion_point = model.residues[pri + 1] catom = prev_res.find_atom('C') for n in catom.neighbors: if n.residue != prev_res: raise TypeError( 'This residue already has another bonded to its ' 'C terminus!') if chain_id is not None: session.logger.warning( 'AddAA: chain_id argument is ignored when adding to an existing residue.' ) chain_id = prev_res.chain_id oxt = prev_res.find_atom('OXT') if oxt is not None: oxt.delete() elif next_res: ref_res = next_res b_factor = next_res.atoms[numpy.in1d( next_res.atoms.names, ('N', 'CA', 'C', 'O', 'CB'))].bfactors.mean() + add_b_factor insertion_point = next_res natom = next_res.find_atom('N') for n in natom.neighbors: if n.residue != next_res: raise TypeError( 'This residue already has another bonded to its ' 'N terminus!') if chain_id is not None: session.logger.warning( 'AddAA: chain_id argument is ignored when adding to an existing residue.' ) chain_id = next_res.chain_id for hname in ('H2', 'H3'): h = next_res.find_atom(hname) if h is not None: h.delete() h = next_res.find_atom('H1') if h is not None: h.name = 'H' if next_res.name == 'PRO': h = next_res.find_atom('H') if h: h.delete() if number is None: if prev_res: number = prev_res.number + 1 elif next_res: number = next_res.number - 1 from chimerax import mmcif tmpl = mmcif.find_template_residue(session, resname) from .place_ligand import new_residue_from_template import numpy # delete extraneous atoms r = new_residue_from_template(model, tmpl, chain_id, [0, 0, 0], number, insert_code=insertion_code, b_factor=b_factor, precedes=insertion_point) r.atoms[numpy.in1d(r.atoms.names, ['OXT', 'HXT', 'H2', 'H1', 'HN1', 'HN2'])].delete() # Translate and rotate residue to (roughly) match the desired position if not next_res and not prev_res: r.atoms.coords += numpy.array(center) - r.atoms.coords.mean(axis=0) b_factor = max(add_b_factor, 5) else: from chimerax.geometry import align_points from chimerax.atomic.struct_edit import add_bond if prev_res: correct_o_position(prev_res, psi) add_bond(r.find_atom('N'), prev_res.find_atom('C')) n_pos = _find_next_N_position(prev_res) ca_pos = _find_next_CA_position(n_pos, prev_res) c_pos = _find_next_C_position(ca_pos, n_pos, prev_res, phi) target_coords = numpy.array([n_pos, ca_pos, c_pos]) align_coords = numpy.array( [r.find_atom(a).coord for a in ['N', 'CA', 'C']]) elif next_res: add_bond(r.find_atom('C'), next_res.find_atom('N')) c_pos = _find_prev_C_position(next_res, psi) ca_pos = _find_prev_CA_position(c_pos, next_res) #o_pos = _find_prev_O_position(c_pos, next_res) n_pos = _find_prev_N_position(c_pos, ca_pos, next_res, psi) target_coords = numpy.array([c_pos, ca_pos, n_pos]) align_coords = numpy.array( [r.find_atom(a).coord for a in ['C', 'CA', 'N']]) tf = align_points(align_coords, target_coords)[0] r.atoms.coords = tf * r.atoms.coords correct_o_position(r, psi) if r.name in ('GLU', 'ASP'): fix_amino_acid_protonation_state(r) if r.name == 'PRO': r.atoms[r.atoms.names == 'H'].delete() r.atoms.bfactors = b_factor r.atoms.occupancies = occupancy from . import copy_atom_style_from copy_atom_style_from(session, r.atoms, ref_res) model.atoms.selecteds = False r.atoms.selecteds = True return r
def _new_camera_position(self, residue, block_spotlight=True): session = self.session r = residue from chimerax.atomic import Residue, Atoms pt = residue.polymer_type if pt == Residue.PT_NONE: # No preferred orientation ref_coords = None target_coords = r.atoms.coords centroid = target_coords.mean(axis=0) elif pt == Residue.PT_AMINO: ref_coords = self.peptide_ref_coords try: target_coords = Atoms( [r.find_atom(name) for name in ('N', 'CA', 'C')]).coords centroid = target_coords[1] except ValueError: # Either a key atom is missing, or this is a special residue # e.g. NH2 ref_coords = None target_coords = r.atoms.coords centroid = target_coords.mean(axis=0) elif pt == Residue.PT_NUCLEIC: ref_coords = self.nucleic_ref_coords try: target_coords = Atoms([ r.find_atom(name) for name in ("C2'", "C1'", "O4'") ]).coords centroid = target_coords[1] except ValueError: ref_coords = None target_coords = r.atoms.coords centroid = target_coords.mean(axis=0) c = session.main_view.camera cp = c.position old_cofr = session.main_view.center_of_rotation if ref_coords is not None: from chimerax.geometry import align_points, Place p, rms = align_points(ref_coords, target_coords) else: from chimerax.geometry import Place p = Place(origin=centroid) tc = self._camera_ref_pos(self.view_distance) np = p * tc new_cofr = centroid if c.name == 'orthographic': fw = c.field_width new_fw = self._view_distance * 2 def interpolate_camera(session, f, cp=cp, np=np, oc=old_cofr, nc=new_cofr, fw=fw, nfw=new_fw, vr=self._view_distance, center=np.inverse() * centroid, frames=self._interpolate_frames): frac = (f + 1) / frames v = session.main_view c = v.camera p = np if f + 1 == frames else cp.interpolate( np, center, frac=frac) cofr = oc + frac * (nc - oc) c.position = p vd = c.view_direction() cp = v.clip_planes ncm, fcm = _get_clip_distances(session) cp.set_clip_position('near', cofr - ncm * vr * vd, v) cp.set_clip_position('far', cofr + fcm * vr * vd, v) if c.name == 'orthographic': c.field_width = fw + frac * (nfw - fw) from chimerax.geometry import distance if distance(new_cofr, old_cofr) < self._view_distance: if block_spotlight: self._block_clipper_spotlights() from .delayed_reaction import call_after_n_events call_after_n_events(self.session.triggers, 'frame drawn', self._interpolate_frames, self._release_clipper_spotlights, []) from chimerax.core.commands import motion motion.CallForNFrames(interpolate_camera, self._interpolate_frames, session) else: interpolate_camera(session, 0, frames=1)