def pdb_mtrix_matrices(pdb_headers, add_identity=True, given=False): h = pdb_headers have_matrix = ('MTRIX1' in h and 'MTRIX2' in h and 'MTRIX3' in h) if not have_matrix: if add_identity: from chimerax.geometry import identity return [identity()] else: return [] row1_list = h['MTRIX1'] row2_list = h['MTRIX2'] row3_list = h['MTRIX3'] if len(row1_list) != len(row2_list) or len(row2_list) != len(row3_list): if add_identity: from chimerax.geometry import identity return [identity()] else: return [] row_triples = zip(row1_list, row2_list, row3_list) mlist = [] from chimerax.geometry import Place for row_triple in row_triples: matrix = [] for line in row_triple: try: mrow = [ float(f) for f in (line[10:20], line[20:30], line[30:40], line[45:55]) ] except ValueError: break mgiven = (len(line) >= 60 and line[59] == '1') if (mgiven and given) or (not mgiven and not given): matrix.append(mrow) if len(matrix) == 3: mlist.append(Place(matrix)) if add_identity: if len([m for m in mlist if m.is_identity()]) == 0: # Often there is no MTRIX identity entry from chimerax.geometry import identity mlist.append(identity()) return Places(mlist)
def extrusion_transforms(path, tangents, yaxis=None): from chimerax.geometry import identity, vector_rotation, translation tflist = [] if yaxis is None: # Make xy planes for coordinate frames at each path point not rotate # from one point to next. tf = identity() n0 = (0, 0, 1) for p1, n1 in zip(path, tangents): tf = vector_rotation(n0, n1) * tf tflist.append(translation(p1) * tf) n0 = n1 else: # Make y-axis of coordinate frames at each point align with yaxis. from chimerax.geometry import normalize_vector, cross_product, Place for p, t in zip(path, tangents): za = t xa = normalize_vector(cross_product(yaxis, za)) ya = cross_product(za, xa) tf = Place( ((xa[0], ya[0], za[0], p[0]), (xa[1], ya[1], za[1], p[1]), (xa[2], ya[2], za[2], p[2]))) tflist.append(tf) return tflist
def draw(self, renderer, draw_pass): # TODO: Overlay drawing sets projection to identity which will defeat supersample # image capture using pixel shifts that go into the projection matrix from the camera. r = renderer # Enable backface culling if discs have spaces between them so far side of # globe is not visible. r.enable_backface_culling(True) # TODO: Enabling depth test causes globe to vanish if silhouette edges turned on. Why? # TODO: Maybe overlay drawing should always use a cleared depth buffer, so # overlay cannot be obscured by real models? # Enable depth test so overlapped discs don't look like fish scales dependent # on order of drawing of discs. r.enable_depth_test(True) ox, oy, oz = self.offset w, h = r.render_size() whmax = max(w,h) sx, sy = h/whmax, w/whmax r.set_projection_matrix(((sx, 0, 0, 0), (0, sy, 0, 0), (0, 0, 1, 0), (ox, oy, oz, 1))) rot = self.session.main_view.camera.position.zero_translation() r.set_view_matrix(rot.inverse()) Drawing.draw(self, renderer, draw_pass) # Restore drawing settings so other overlays get expected state. from chimerax.geometry import identity r.set_view_matrix(identity()) r.set_projection_matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))) r.enable_depth_test(False) r.enable_backface_culling(False)
def motion_to_maximum(points, point_weights, volume, max_steps=2000, ijk_step_size_min=0.01, ijk_step_size_max=0.5, optimize_translation=True, optimize_rotation=True, metric='sum product', symmetries=[], request_stop_cb=None): ''' Use gradient ascent to rigidly rotate and shift a set of points within a density map to maximize the average interpolated density value at the point positions. Returns a Place object giving the motion of the points to the local maximum. The point coordinates are not modified. Optionally the correlation value or correlation about mean can be optimized. Copies of the point set can be included in the optimization by specifying a list of Place objects for the symmetries argument. ''' from chimerax.geometry import identity data_array, xyz_to_ijk_transform = \ volume.matrix_and_transform(identity(), subregion = None, step = 1) move_tf, stats = \ locate_maximum(points, point_weights, data_array, xyz_to_ijk_transform, max_steps, ijk_step_size_min, ijk_step_size_max, optimize_translation, optimize_rotation, metric, None, symmetries, request_stop_cb) return move_tf, stats
def atoms_outside_contour(atoms, volume=None): if volume is None: from chimerax.map import active_volume volume = active_volume() points = atom_coordinates(atoms) from chimerax.geometry import identity poc, clevel = points_outside_contour(points, identity(), volume) return poc, clevel
def initial_camera_view(self, pad=0.05, set_pivot=True): '''Set the camera position to show all displayed drawings, looking down the z axis.''' b = self.drawing_bounds() if b is None: return c = self.camera from chimerax.geometry import identity c.position = identity() c.view_all(b, window_size=self.window_size, pad=pad) if set_pivot: self._center_of_rotation = cr = b.center() self._update_center_of_rotation = True
def _contacting_transforms(structures, transforms, distance): from numpy import concatenate, float32 points = concatenate([s.atoms.scene_coords for s in structures]).astype(float32) from chimerax.geometry import identity, find_close_points_sets, Places ident = identity().matrix.astype(float32) orig_points = [(points, ident)] tfnear = Places([ tf for tf in transforms if len( find_close_points_sets(orig_points, [( points, tf.matrix.astype(float32))], distance)[0][0]) > 0 ]) return tfnear
def map_fitting_points(v, envelope, local_coords=False, include_zeros=False): from chimerax.geometry import identity point_to_scene_transform = None if local_coords else identity() from . import fitmap as F try: points, point_weights = F.map_points_and_weights( v, envelope, point_to_scene_transform, include_zeros=include_zeros) except (MemoryError, ValueError) as e: raise UserError('Out of memory, too many points in %s' % v.name) if len(points) == 0: msg = ('No grid points above map threshold.' if envelope else 'Map has no non-zero values.') raise UserError(msg) return points, point_weights
def average_map_value_at_atom_positions(atoms, volume=None): if volume is None: from chimerax.map import active_volume volume = active_volume() points = atom_coordinates(atoms) if volume is None or len(points) == 0: return 0, len(points) from chimerax.geometry import identity data_array, xyz_to_ijk_transform = \ volume.matrix_and_transform(identity(), subregion = None, step = 1) amv, npts = average_map_value(points, xyz_to_ijk_transform, data_array) return amv, npts
def __init__(self, session, filename): from chimerax.core import generic3d self.model = generic3d.Generic3DModel(filename, session) self.session = session self.shapes = [] # parse input self.warned = set() self.lineno = 0 self.transforms = [identity()] self.pure = [True] # True if corresponding transform has pure rotation self.cur_color = [1.0, 1.0, 1.0, 1.0] self.cur_transparency = 0 self.cur_pos = array([0.0, 0.0, 0.0]) self.cur_pos_is_move = True # set whenever cur_pos is set self.cur_char_pos = array([0.0, 0.0, 0.0]) self.cur_font = ['SANS', 12, 'PLAIN'] self.cur_description = None self.cur_atoms = None self.num_objects = 0 self.LINE_RADIUS = 0.08
def __init__(self, name, hkls, vals, dim_scale=2.0, highlight_negatives=True): super().__init__(name) from chimerax.surface.shapes import sphere_geometry2 self.set_geometry(*sphere_geometry2(80)) import numpy from chimerax.geometry import Places, Place, identity id_axes = identity().axes() abs_vals = numpy.abs(vals) import numpy place_array = numpy.zeros((len(hkls), 3, 4)) place_array[:, :, 3] = hkls * dim_scale place_array[:, :, :3] = id_axes for i in range(3): # Volume scales with value, so radius scales with cube root of value place_array[:, i, i] *= abs_vals**(1 / 3) # positions =[Place(origin=hkl*dim_scale, axes=id_axes*max(rval, 0)) # for (hkl, rval) in zip(hkls, vals) # ] if highlight_negatives: import numpy negatives = numpy.argwhere(vals < 0).flatten() place_array[negatives, :, :3] = id_axes # for ni in negatives: # positions[ni] = Place(origin=positions[ni].origin(), axes=id_axes) positions = Places(place_array=place_array) self.set_positions(Places(positions)) from chimerax.core.colors import BuiltinColormaps cmap = BuiltinColormaps['rdylbu'].linear_range(-1, 1) colors = cmap.interpolated_rgba8(vals) if highlight_negatives: colors[vals < 0] = [255, 0, 0, 255] self.set_colors(colors)
def extend_crystal_map(volarray, ijk_cell_size, ijk_symmetries, out_array, out_ijk_to_vol_ijk_transform): ksz, jsz, isz = out_array.shape otf = out_ijk_to_vol_ijk_transform from chimerax.geometry import identity itf = identity() nsym = len(ijk_symmetries) from numpy import empty, float32, delete as delete_array_elements values = empty((nsym, ), float32) nnc = 0 # Number of grid points not covered by symmetry dmax = None # Maximum value discrepancy for multiple overlaps from chimerax.map_data import interpolate_volume_data for k in range(ksz): for j in range(jsz): for i in range(isz): vijk = otf * (i, j, k) vlist = [] for sym in ijk_symmetries: svijk = sym * vijk scijk = [p % s for p, s in zip(svijk, ijk_cell_size) ] # Wrap to unit cell. vlist.append(scijk) values[:] = 0 outside = interpolate_volume_data(vlist, itf, volarray, 'linear', values)[1] n = len(values) - len(outside) if n >= 1: out_array[k, j, i] = float(values.sum()) / n if n >= 2: # Record maximum value discrepancy for multiple overlaps vinside = delete_array_elements(values, outside) d = vinside.max() - vinside.min() if dmax is None or d > dmax: dmax = d else: nnc += 1 # Count grid points not covered by symmetry. return nnc, dmax
def fit_search(models, points, point_weights, volume, n, rotations = True, shifts = True, radius = None, angle_tolerance = 6, shift_tolerance = 3, asymmetric_unit = True, minimum_points_in_contour = 0.5, metric = 'sum product', optimize_translation = True, optimize_rotation = True, max_steps = 2000, ijk_step_size_min = 0.01, ijk_step_size_max = 0.5, request_stop_cb = None): bounds = volume.surface_bounds() if bounds is None: xyz_min, xyz_max = volume.xyz_bounds(step = 1) from chimerax.geometry import Bounds bounds = Bounds(xyz_min, xyz_max) asym_center_f = (.75,.55,.55) asym_center = tuple(x0 + (x1-x0)*f for x0, x1, f in zip(bounds.xyz_min, bounds.xyz_max, asym_center_f)) from chimerax.geometry import translation, identity center = points.mean(axis=0) ctf = translation(-center) ijk_to_xyz_tf = volume.matrix_indices_to_xyz_transform(step = 1) xyz_to_ijk_tf = ijk_to_xyz_tf.inverse() data_array = volume.matrix(step = 1) vtfinv = volume.position.inverse() mtv_list = [vtfinv * m.position for m in models] flist = [] outside = 0 from math import pi from chimerax.geometry import bins b = bins.Binned_Transforms(angle_tolerance*pi/180, shift_tolerance, center) fo = {} from .fitmap import locate_maximum for i in range(n): if request_stop_cb and request_stop_cb('Fit %d of %d' % (i+1,n)): break shift = ((random_translation(bounds) if radius is None else random_translation_step(center, radius)) if shifts else translation(center)) rot = random_rotation() if rotations else identity() tf = shift * rot * ctf optimize = True max_opt = 2 while optimize and max_opt > 0: p_to_ijk_tf = xyz_to_ijk_tf * tf move_tf, stats = \ locate_maximum(points, point_weights, data_array, p_to_ijk_tf, max_steps, ijk_step_size_min, ijk_step_size_max, optimize_translation, optimize_rotation, metric, request_stop_cb = None) ptf = tf * move_tf optimize = False max_opt -= 1 if asymmetric_unit: atf = unique_symmetry_position(ptf, center, asym_center, volume.data.symmetries) if not atf is ptf: ptf = tf = atf optimize = not b.close_transforms(ptf) close = b.close_transforms(ptf) if len(close) == 0: transforms = [ptf * mtv for mtv in mtv_list] stats['hits'] = 1 f = Fit(models, transforms, volume, stats) f.ptf = ptf flist.append(f) b.add_transform(ptf) fo[id(ptf)] = f else: s = fo[id(close[0])].stats s['hits'] += 1 # Filter out solutions with too many points outside volume contour. fflist = [f for f in flist if (in_contour(f.ptf, points, volume, f.stats) >= minimum_points_in_contour)] fset = set(fflist) outside = sum([f.stats['hits'] for f in flist if not f in fset]) # Sort fits by correlation, then average map value. fflist.sort(key = fit_order) fflist.reverse() # Best first return fflist, outside
def write_mol2(session, file_name, *, models=None, atoms=None, status=None, anchor=None, rel_model=None, sybyl_hyd_naming=True, combine_models=False, skip_atoms=None, res_num=False, gaff_type=False, gaff_fail_error=None): """Write a Mol2 file. Parameters ---------- file_name : str, or file object open for writing Output file. models : a list/tuple/set of models (:py:class:`~chimerax.atomic.Structure`s) or a single :py:class:`~chimerax.atomic.Structure` The structure(s) to write out. If None (and 'atoms' is also None) then write out all structures. atoms : an :py:class:`~chimerax.atomic.Atoms` collection or None. If not None, then 'models' must be None. status : function or None If not None, a function that takes a string -- used to report the progress of the write. anchor : :py:class:`~chimerax.atomic.Atoms` collection Atoms (and their implied internal bonds) that should be written out to the @SET section of the file as the rigid framework for flexible ligand docking. rel_model : Model whose coordinate system the coordinates should be written out reletive to, i.e. take the output atoms' coordinates and apply the inverse of the rel_model's transform. sybyl_hyd_naming : bool Controls whether hydrogen names should be "Sybyl-like" or "PDB-like" -- e.g. HG21 vs. 1HG2. combine_models : bool Controls whether multiple structures will be combined into a single @MOLECULE section (value: True) or each given its own section (value: False). skip_atoms : list/set of :py:class:`~chimerax.atomic.Atom`s or an :py:class:`~chimerax.atomic.Atoms` collection or None Atoms to not output res_num : bool Controls whether residue sequence numbers are included in the substructure name. Since Sybyl Mol2 files include them, this defaults to True. gaff_type : bool If 'gaff_type' is True, outout GAFF atom types instead of Sybyl atom types. `gaff_fail_error`, if specified, is the type of error to throw (e.g. UserError) if there is no gaff_type attribute for an atom, otherwise throw the standard AttributeError. """ if status: status("Writing Mol2 file %s" % file_name) from chimerax import io f = io.open_output(file_name, "utf-8") sort_key_func = serial_sort_key = lambda a, ri={}: write_mol2_sort_key( a, res_indices=ri) from chimerax.atomic import Structure, Atoms, Residue class JPBGroup: def __init__(self, atoms): atom_set = set(atoms) pbs = [] for s in atoms.unique_structures: pbg = s.pbg_map.get(s.PBG_METAL_COORDINATION, None) if not pbg: continue for pb in pbg.pseudobonds: if pb.atoms[0] in atom_set and pb.atoms[1] in atom_set: pbs.append(pb) self._pbs = pbs @property def pseudobonds(self): return self._pbs if models is None: if atoms is None: structures = session.models.list(type=Structure) else: structures = atoms else: if atoms is None: if isinstance(models, Structure): structures = [models] else: structures = [m for m in models if isinstance(m, Structure)] else: raise ValueError( "Cannot specify both 'models' and 'atoms' keywords") if isinstance(structures, Atoms): class Jumbo: def __init__(self, atoms): self.atoms = atoms self.residues = atoms.unique_residues self.bonds = atoms.intra_bonds self.name = "(selection)" self.pbg_map = { Structure.PBG_METAL_COORDINATION: JPBGroup(atoms) } structures = [Jumbo(structures)] sort_key_func = lambda a: (a.structure.id, ) + serial_sort_key(a) combine_models = False # transform... if rel_model is None: from chimerax.geometry import identity xform = identity() else: xform = rel_model.scene_position.inverse() # need to find amide moieties since Sybyl has an explicit amide type if status: status("Finding amides") from chimerax.chem_group import find_group amides = find_group("amide", structures) amide_Ns = set([amide[2] for amide in amides]) amide_CNs = set([amide[0] for amide in amides]) amide_CNs.update(amide_Ns) amide_Os = set([amide[1] for amide in amides]) substructure_names = None if combine_models and len(structures) > 1: # create a fictitious jumbo model class Jumbo: def __init__(self, structures): self.name = structures[0].name + " (combined)" from chimerax.atomic import concatenate self.atoms = concatenate([s.atoms for s in structures]) self.bonds = concatenate([s.bonds for s in structures]) self.residues = concatenate([s.residues for s in structures]) self.pbg_map = { Structure.PBG_METAL_COORDINATION: JPBGroup(self.atoms) } # if combining single-residue structures, # can be more informative to use model name # instead of residue type for substructure if len(structures) == len(self.residues): rnames = self.residues.names if len(set(rnames)) < len(rnames): snames = [s.name for s in structures] if len(set(snames)) == len(snames): self.substructure_names = dict( zip(self.residues, snames)) structures = [Jumbo(structures)] if hasattr(structures[-1], 'substructure_names'): substructure_names = structures[-1].substructure_names delattr(structures[-1], 'substructure_names') sort_key_func = lambda a: (a.structure.id, ) + serial_sort(a) # write out structures for struct in structures: if hasattr(struct, 'mol2_comments'): for m2c in struct.mol2_comments: print(m2c, file=f) if hasattr(struct, 'solvent_info'): print(struct.solvent_info, file=f) # molecule section header print("%s" % MOLECULE_HEADER, file=f) # molecule name print("%s" % struct.name, file=f) atoms = list(struct.atoms) bonds = list(struct.bonds) # add metal-coordination bonds coord_grp = struct.pbg_map.get(Structure.PBG_METAL_COORDINATION, None) if coord_grp: bonds.extend(list(coord_grp.pseudobonds)) if skip_atoms: skip_atoms = set(skip_atoms) atoms = [a for a in atoms if a not in skip_atoms] bonds = [ b for b in bonds if b.atoms[0] not in skip_atoms and b.atoms[1] not in skip_atoms ] residues = struct.residues # Put the atoms in the order we want for output if status: status("Putting atoms in input order") atoms.sort(key=sort_key_func) # if anchor is not None, then there will be two entries in # the @SET section of the file... if anchor: sets = 2 else: sets = 0 # number of entries for various sections... print("%d %d %d 0 %d" % (len(atoms), len(bonds), len(residues), sets), file=f) # type of molecule if hasattr(struct, "mol2_type"): mtype = struct.mol2_type else: mtype = "SMALL" from chimerax.atomic import Sequence for r in struct.residues: if Sequence.protein3to1(r.name) != 'X': mtype = "PROTEIN" break if Sequence.nucleic3to1(r.name) != 'X': mtype = "NUCLEIC_ACID" break print(mtype, file=f) # indicate type of charge information if hasattr(struct, 'charge_model'): print(struct.charge_model, file=f) else: print("NO_CHARGES", file=f) if hasattr(struct, 'mol2_comment'): print("\n%s" % struct.mol2_comment, file=f) else: print("\n", file=f) if status: status("writing atoms") # atom section header print("%s" % ATOM_HEADER, file=f) # make a dictionary of residue indices so that we can do quick look ups res_indices = {} for i, r in enumerate(residues): res_indices[r] = i + 1 for i, atom in enumerate(atoms): # atom ID, starting from 1 print("%7d" % (i + 1), end=" ", file=f) # atom name, possibly rearranged if it's a hydrogen if sybyl_hyd_naming and not atom.name[0].isalpha(): atom_name = atom.name[1:] + atom.name[0] else: atom_name = atom.name print("%-8s" % atom_name, end=" ", file=f) # use correct relative coordinate position coord = xform * atom.scene_coord print("%9.4f %9.4f %9.4f" % tuple(coord), end=" ", file=f) # atom type if gaff_type: try: atom_type = atom.gaff_type except AttributeError: if not gaff_fail_error: raise raise gaff_fail_error( "%s has no Amber/GAFF type assigned.\n" "Use the AddCharge tool to assign Amber/GAFF types." % atom) elif hasattr(atom, 'mol2_type'): atom_type = atom.mol2_type elif atom in amide_Ns: atom_type = "N.am" elif atom.structure_category == "solvent" \ and atom.residue.name in Residue.water_res_names: if atom.element.name == "O": atom_type = "O.t3p" else: atom_type = "H.t3p" elif atom.element.name == "N" and len( [r for r in atom.rings() if r.aromatic]) > 0: atom_type = "N.ar" elif atom.idatm_type == "C2" and len( [nb for nb in atom.neighbors if nb.idatm_type == "Ng+"]) > 2: atom_type = "C.cat" elif sulfur_oxygen(atom): atom_type = "O.2" else: try: atom_type = chimera_to_sybyl[atom.idatm_type] except KeyError: session.logger.warning( "Atom whose IDATM type has no equivalent" " Sybyl type: %s (type: %s)" % (atom, atom.idatm_type)) atom_type = str(atom.element) print("%-5s" % atom_type, end=" ", file=f) # residue-related info res = atom.residue # residue index print("%5d" % res_indices[res], end=" ", file=f) # substructure identifier and charge if hasattr(atom, 'charge') and atom.charge is not None: charge = atom.charge else: charge = 0.0 if substructure_names: rname = substructure_names[res] elif res_num: rname = "%3s%-5d" % (res.name, res.number) else: rname = "%3s" % res.name print("%s %9.4f" % (rname, charge), file=f) if status: status("writing bonds") # bond section header print("%s" % BOND_HEADER, file=f) # make an atom-index dictionary to speed lookups atom_indices = {} for i, a in enumerate(atoms): atom_indices[a] = i + 1 for i, bond in enumerate(bonds): a1, a2 = bond.atoms # ID print("%6d" % (i + 1), end=" ", file=f) # atom IDs print("%4d %4d" % (atom_indices[a1], atom_indices[a2]), end=" ", file=f) # bond order; give it our best shot... if hasattr(bond, 'mol2_type'): print(bond.mol2_type, file=f) continue amide_A1 = a1 in amide_CNs amide_A2 = a2 in amide_CNs if amide_A1 and amide_A2: print("am", file=f) continue if amide_A1 or amide_A2: if a1 in amide_Os or a2 in amide_Os: print("2", file=f) else: print("1", file=f) continue aromatic = False # 'bond' might be a metal-coordination bond so do a test for rings if hasattr(bond, 'rings'): for ring in bond.rings(): if ring.aromatic: aromatic = True break if aromatic: print("ar", file=f) continue try: geom1 = idatm_info[a1.idatm_type].geometry except KeyError: print("1", file=f) continue try: geom2 = idatm_info[a2.idatm_type].geometry except KeyError: print("1", file=f) continue # sulfone/sulfoxide is classically depicted as double- # bonded despite the high dipolar character of the # bond making it have single-bond character. For # output, use the classical values. if sulfur_oxygen(a1) or sulfur_oxygen(a2): print("2", file=f) continue if geom1 not in [2, 3] or geom2 not in [2, 3]: print("1", file=f) continue # if either endpoint atom is in an aromatic ring and # the bond isn't, it's a single bond... for endp in [a1, a2]: aromatic = False for ring in endp.rings(): if ring.aromatic: aromatic = True break if aromatic: break else: # neither endpoint in aromatic ring if geom1 == 2 and geom2 == 2: print("3", file=f) else: print("2", file=f) continue print("1", file=f) if status: status("writing residues") # residue section header print("%s" % SUBSTR_HEADER, file=f) for i, res in enumerate(residues): # residue id field print("%6d" % (i + 1), end=" ", file=f) # residue name field if substructure_names: rname = substructure_names[res] elif res_num: rname = "%3s%-4d" % (res.name, res.number) else: rname = "%3s" % res.name print(rname, end=" ", file=f) # ID of the root atom of the residue chain_atom = res.principal_atom if chain_atom is None: # if writing out a selection, not all residue atoms # might be in atom_indices... for chain_atom in res.atoms: if chain_atom in atom_indices: break print("%5d" % atom_indices[chain_atom], end=" ", file=f) print("RESIDUE 4", end=" ", file=f) # Sybyl seems to use chain 'A' when chain ID is blank, # so run with that chain_id = res.chain_id if not chain_id.strip(): chain_id = 'A' print("%-4s %3s" % (chain_id, res.name), end=" ", file=f) # number of out-of-substructure bonds cross_res_bonds = 0 for a in res.atoms: for nb in a.neighbors: if nb.residue != res: cross_res_bonds += 1 print("%5d" % cross_res_bonds, end="", file=f) # print "ROOT" if first or only residue of a chain if not res.chain or res.chain.existing_residues[0] == res: print(" ROOT", file=f) else: print(file=f) # write flexible ligand docking info if anchor: if status: status("writing anchor info") print("%s" % SET_HEADER, file=f) atom_indices = {} for i, a in enumerate(atoms): atom_indices[a] = i + 1 bond_indices = {} for i, b in enumerate(bonds): bond_indices[b] = i + 1 print( "ANCHOR STATIC ATOMS <user> **** Anchor Atom Set", file=f) print(len(anchor), end=" ", file=f) for a in anchor: if a in atom_indices: print(atom_indices[a], end=" ", file=f) print(file=f) print( "RIGID STATIC BONDS <user> **** Rigid Bond Set", file=f) bonds = anchor.intra_bonds print(len(bonds), end=" ", file=f) for b in bonds: if b in bond_indices: print(bond_indices[b], end=" ", file=f) print(file=f) if file_name != f: f.close() if status: status("Wrote Mol2 file %s" % file_name)
def coordinate_system_transform(from_cs, to_cs): global cst if cst: return cst[(from_cs, to_cs)] transform = cst e = icosahedron_edge_length() # Triangle edge length of unit icosahedron. from math import sqrt s25 = e / 2 # Sin/Cos for angle between 2-fold and 5-fold axis c25 = sqrt(1 - s25 * s25) s35 = e / sqrt(3) # Sin/Cos for angle between 3-fold and 5-fold axis c35 = sqrt(1 - s35 * s35) from chimerax.geometry import Place transform[('2n5', '222')] = Place( ((1, 0, 0, 0), (0, c25, -s25, 0), (0, s25, c25, 0))) transform[('2n5', '2n3')] = Place( ((1, 0, 0, 0), (0, c35, s35, 0), (0, -s35, c35, 0))) # Axes permutations. # 90 degree rotation about z: transform[('222', '222r')] = Place( ((0, 1, 0, 0), (-1, 0, 0, 0), (0, 0, 1, 0))) # 180 degree rotation about y: transform[('2n3', '2n3r')] = \ transform[('2n5', '2n5r')] = Place(((-1, 0, 0, 0), (0, 1, 0, 0), (0, 0, -1, 0))) # 180 degree rotation about x: transform[('n25', 'n25r')] = Place( ((1, 0, 0, 0), (0, -1, 0, 0), (0, 0, -1, 0))) # x <-> y and z -> -z: transform[('n25', '2n5')] = Place( ((0, 1, 0, 0), (1, 0, 0, 0), (0, 0, -1, 0))) # Extend to all pairs of transforms. tlist = [] while len(transform) > len(tlist): tlist = transform.keys() # Add inverse transforms for f, t in tlist: if not (t, f) in transform: transform[(t, f)] = transform[(f, t)].inverse() # Use transitivity for f1, t1 in tlist: for f2, t2 in tlist: if f2 == t1 and f1 != t2 and not (f1, t2) in transform: transform[(f1, t2)] = transform[(f2, t2)] \ * transform[(f1, t1)] from chimerax.geometry import identity i = identity() for s in coordinate_system_names: transform[(s, s)] = i return transform[(from_cs, to_cs)]
def fit_sequence(models, volume, steps, subtract_maps=[], envelope=True, include_zeros=False, metric='overlap', optimize_translation=True, optimize_rotation=True, max_steps=2000, ijk_step_size_min=0.01, ijk_step_size_max=0.5, request_stop_cb=None, log=None): from chimerax.geometry import identity data_array, xyz_to_ijk_transform = \ volume.matrix_and_transform(identity(), subregion = None, step = 1) size = tuple(data_array.shape[::-1]) from numpy import float32, multiply, add, array from chimerax.map_data import grid_indices grid_points = grid_indices(size, float32) xyz_to_ijk_transform.inverse().transform_points(grid_points, in_place=True) grid_points_to_scene_transform = identity() # Make float32 copy for subtracting interpolated molecule maps. d = array(data_array, float32) from chimerax.atomic import Atoms from .move import position_history as h h.record_position(models, Atoms(), volume) fits = {} from . import fitmap as F from chimerax.map.volume import minimum_rms_scale from .search import Fit for s in range(steps): mi = s % len(models) v = models[mi] if request_stop_cb and request_stop_cb('Fitting %s in %s, step %d' % (v.name, volume.name, s + 1)): return # Subtract off other maps. d[:] = data_array for m in models + subtract_maps: if not m is v: values = m.interpolated_values(grid_points, grid_points_to_scene_transform, subregion=None, step=None) level = m.minimum_surface_level scale = minimum_rms_scale(values, data_array.ravel(), level) multiply(values, scale, values) add(d, values.reshape(d.shape), d) # Fit in difference map. points, point_weights = F.map_points_and_weights( v, envelope, include_zeros=include_zeros) move_tf, stats = F.locate_maximum(points, point_weights, d, xyz_to_ijk_transform, max_steps, ijk_step_size_min, ijk_step_size_max, optimize_translation, optimize_rotation, metric, request_stop_cb=None) v.position = move_tf * v.position fits[v] = Fit([v], None, volume, stats) if log: log.info(F.map_fit_message(v, volume, stats)) h.record_position(models, Atoms(), volume) flist = [fits[m] for m in models if m in fits] return flist
def locate_maximum(points, point_weights, data_array, xyz_to_ijk_transform, max_steps=2000, ijk_step_size_min=0.01, ijk_step_size_max=0.5, optimize_translation=True, optimize_rotation=True, metric='sum product', rotation_center=None, symmetries=[], request_stop_cb=None): segment_steps = 4 cut_step_size_threshold = .25 step_cut_factor = .5 step_grow_factor = 1.2 ijk_step_size = ijk_step_size_max np = len(points) from time import time report_interval = 1.0 # seconds next_report_time = time() + report_interval if metric == 'correlation about mean': from numpy import sum, float64 wm = sum(point_weights, dtype=float64) / len(point_weights) point_wts = point_weights.copy() point_wts -= wm else: point_wts = point_weights from chimerax.geometry import identity move_tf = identity() if rotation_center is None: from numpy import sum, float64 rotation_center = rc = sum(points, axis=0, dtype=float64) / np syminv = [s.inverse() for s in symmetries] # Use temporary value and gradient arrays instead of reallocating each step. from numpy import empty, float32 values = empty((np, ), float32) gradients = empty(points.shape, float32) step = 0 while step < max_steps and ijk_step_size > ijk_step_size_min: xyz_to_ijk_tf = xyz_to_ijk_transform * move_tf rc = (rotation_center if len(symmetries) == 0 else move_tf.inverse() * rotation_center) seg_tf = step_to_maximum(points, point_wts, data_array, xyz_to_ijk_tf, segment_steps, ijk_step_size, optimize_translation, optimize_rotation, rc, metric, syminv=syminv, values=values, gradients=gradients) step += segment_steps mm = maximum_ijk_motion(points, xyz_to_ijk_tf, seg_tf) mmcut = cut_step_size_threshold * segment_steps * ijk_step_size if mm < mmcut: ijk_step_size *= step_cut_factor else: ijk_step_size = min(ijk_step_size * step_grow_factor, ijk_step_size_max) move_tf = move_tf * seg_tf if request_stop_cb and time() >= next_report_time: next_report_time = time() + report_interval shift, angle = move_tf.shift_and_angle(rc) if request_stop_cb('%d steps, shift %.3g, rotation %.3g degrees' % (step, shift, angle)): break # Record statistics of optimization. shift, angle = move_tf.shift_and_angle(rc) axis, axis_point, angle, axis_shift = move_tf.axis_center_angle_shift() xyz_to_ijk_tf = xyz_to_ijk_transform * move_tf stats = { 'shift': shift, 'axis': axis, 'axis point': axis_point, 'angle': angle, 'axis shift': axis_shift, 'steps': step, 'points': np, 'transform': move_tf } amv, npts = average_map_value(points, xyz_to_ijk_tf, data_array, syminv=syminv, values=values) stats['average map value'] = amv stats['points in map'] = npts # Excludes out-of-bounds points stats['symmetries'] = len(symmetries) if not point_weights is None: map_values = volume_values(points, xyz_to_ijk_tf, data_array, syminv=syminv, values=values) olap, cor, corm = overlap_and_correlation(point_weights, map_values) stats['overlap'] = olap stats['correlation'] = cor stats['correlation about mean'] = corm return move_tf, stats