Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:RBVI/ChimeraX into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
elainecmeng committed Jan 31, 2025
2 parents dbba239 + 5a3b50c commit fde3a61
Show file tree
Hide file tree
Showing 34 changed files with 303 additions and 154 deletions.
4 changes: 4 additions & 0 deletions docs/presentations.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ <h2>ChimeraX Demonstrations, Talks and Posters</h2>
<a href="https://rbvi.github.io/chimerax-recipes/">ChimeraX recipes</a>
</p>

<p>
<a href="https://www.rbvi.ucsf.edu/chimerax/data/phenix-r24-jan2025/phenix_r24.html">ChimeraX graphical user interfaces for Phenix tools</a>. January 30, 2025.
</p>

<p>
<a href="https://www.rbvi.ucsf.edu/chimerax/data/macromethods-jan2025/af3_drugs.html">AlphaFold 3 drug screening on the UCSF Wynton cluster</a>. January 13, 2025.
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ include $(TOP)/mk/config.make
SYNC_DIR = $(wildcard $(TOP)/build/sync)

REST_SUBDIRS = add_charge addh alignment_algs alignment_headers \
alphafold altloc_explorer amber_info arrays atom_search \
alphafold altloc_explorer amber_info aniso arrays atom_search \
atomic atomic_lib axes_planes basic_actions bild \
blastprotein bond_rot bug_reporter build_structure \
bumps buttons cage_builder \
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/alignment_headers/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors= [{name = "UCSF RBVI", email="[email protected]"}]
description = "Alignment header support"
dependencies = [
"ChimeraX-Core ~=1.0"
, "ChimeraX-Alignments ~=2.17"
, "ChimeraX-Alignments ~=2.18"
, "ChimeraX-AlignmentMatrices ~=2.0"
, "ChimeraX-Geometry ~=1.0"
]
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/alignment_headers/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# This notice must be embedded in or attached to all copies, including partial
# copies, of the software or any revisions or derivations thereof.
# === UCSF ChimeraX Copyright ===
__version__ = "3.5"
__version__ = "3.6"

import os

Expand Down
22 changes: 5 additions & 17 deletions src/bundles/alignment_headers/src/conservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,24 +171,12 @@ def position_color(self, pos):

def percent_identity(self, pos, for_histogram=False):
"""actually returns a fraction"""
occur = {}
for seq in self.alignment.seqs:
let = seq[pos]
try:
occur[let] += 1
except KeyError:
occur[let] = 1
best = 0
for let, num in occur.items():
if not let.isalpha():
continue
if num > best:
best = num
if best == 0:
char, count = self.alignment.most_common(pos)
if count == 0:
return 0.0
if for_histogram:
return (best - 1) / (len(self.alignment.seqs) - 1)
return best / len(self.alignment.seqs)
return (count - 1) / (len(self.alignment.seqs) - 1)
return count / len(self.alignment.seqs)

def reevaluate(self, pos1=0, pos2=None, *, evaluation_func=None):
if self.style == self.STYLE_AL2CO:
Expand Down Expand Up @@ -295,7 +283,7 @@ def _reeval_al2co(self, pos1, pos2):
sseq.name = str(i)
sseq.characters = sseq.characters.replace(' ', '.')

temp_alignment = session.alignments.new_alignment(sane_seqs, False, auto_associate=False, name="temp", create_headers=False, copy_seqs=False)
temp_alignment = session.alignments.new_alignment(sane_seqs, False, auto_associate=False, name="temp", create_headers=False)
from tempfile import NamedTemporaryFile
temp_stream = NamedTemporaryFile(mode='w', encoding='utf8', suffix=".aln", delete=False)
temp_alignment.save(temp_stream, format_name="aln")
Expand Down
72 changes: 37 additions & 35 deletions src/bundles/aniso/src/mgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(self, session, structure, *, from_session=False):
if not from_session:
from chimerax.atomic import Atoms
self.shown_atoms = Atoms()
self.atom_depictions = {}
self.atom_depictions = None
self.drawing_params = {
'axis_color': None,
'axis_factor': None,
Expand All @@ -65,30 +65,28 @@ def destroy(self):
for handler in self.handlers:
handler.remove()
if not self.structure.deleted:
self.structure.remove_drawings(self.atom_depictions.values())
self.structure.remove_drawing(self.atom_depictions)
self.structure = self.session = None
super().destroy()

def hide(self, atoms):
"""Hide thermal ellipsoids for these atoms"""
cur_shown = len(self.shown_atoms)
self.shown_atoms = self.shown_atoms.subtract(atoms)
if len(self.shown_atoms) == cur_shown:
return
for a in atoms:
self.atom_depictions[a].display = False
self.shown_atoms = self.shown_atoms.subtract(self._filter_aniso(atoms))
if len(self.shown_atoms) != cur_shown:
self._create_depictions()

def show(self, atoms):
"""Show thermal ellipsoids for these atoms"""
cur_shown = len(self.shown_atoms)
atoms = self._filter_aniso(atoms)
self.shown_atoms = self.shown_atoms.merge(atoms)
if len(self.shown_atoms) == cur_shown:
return
displayed_atoms = atoms.filter(atoms.displays)
for a in displayed_atoms.filter(displayed_atoms.hides == 0):
self.atom_depictions[a].display = True
self._create_depictions(atoms)

def style(self, **kw):
print("style keywords:", kw)
need_rebuild = False
for param, value in kw.items():
if self.drawing_params.get(param) != value:
Expand Down Expand Up @@ -117,34 +115,31 @@ def _changes_cb(self, trigger_name, change_info):
and set(changes.atom_reasons()).isdisjoint(self.atom_reasons)):
self._create_depictions()
elif changes.num_deleted_atoms() > 0:
dead_drawings = [d for a, d in self.atom_depictions.items() if a.deleted]
if dead_drawings:
structure.remove_drawings(dead_drawings)
self.atom_depictions = {a:d for a,d in self.atom_depictions.items() if not a.deleted}

def _create_depictions(self):
if self.atom_depictions:
self.structure.remove_drawings(self.atom_depictions.values())
self.atom_depictions.clear()

atoms = self.structure.atoms
atoms = atoms[atoms.has_aniso_u]
displayed_atoms = self.shown_atoms.filter(self.shown_atoms.displays)
explicitly_depicted = set(displayed_atoms.filter(displayed_atoms.hides == 0))
self._create_depictions()

def _create_depictions(self, added_atoms=None):
# added_atoms and self.shown_atoms already filtered for having aniso info
from chimerax.atomic.shapedrawing import AtomicShapeDrawing
if not added_atoms:
if self.atom_depictions is not None:
self.structure.remove_drawing(self.atom_depictions)
self.atom_depictions = self.structure.new_drawing('thermal ellipsoid',
subclass=AtomicShapeDrawing)
added_atoms = self.shown_atoms

displayed_atoms = added_atoms.filter(added_atoms.displays)
explicitly_depicted = set(displayed_atoms.filter(displayed_atoms.hides == 0))

from chimerax.atomic import Atoms
from numpy.linalg import svd
from numpy import dot, sqrt, negative, cross, array
dp = self.drawing_params
drawing_info = []
for a in atoms:
drawing = self.structure.new_drawing('thermal ellipsoid', subclass=AtomicShapeDrawing)
self.atom_depictions[a] = drawing
drawing.display = a in explicitly_depicted
for a in explicitly_depicted:
ignore, lengths, axes = svd(a.aniso_u)
lengths2 = sqrt(lengths)
lengths2 *= dp['scale']
drawing_info.append((a, drawing, Atoms([a]), axes, lengths2))
drawing_info.append((Atoms([a]), axes, lengths2))

smoothing = dp['smoothing']

Expand All @@ -154,7 +149,8 @@ def _create_depictions(self):
transparency = dp['transparency']
from chimerax.geometry.icosahedron import icosahedron_triangulation
varray, tarray = icosahedron_triangulation(subdivision_levels=smoothing, sphere_factor=1.0)
for atom, drawing, atoms_arg, axes, lengths2 in drawing_info:
for atoms_arg, axes, lengths2 in drawing_info:
atom = atoms_arg[0]
ee = varray * lengths2
if dot(cross(axes[0], axes[1]), axes[2]) < 0:
axes = negative(axes)
Expand All @@ -168,13 +164,14 @@ def _create_depictions(self):
if transparency is not None:
# transparency is a percentage
color = color[:-1] + (round((100 - transparency) * 2.55),)
drawing.add_shape(ev, calc_normals(ev, tarray), tarray, color, atoms_arg)
self.atom_depictions.add_shape(ev, calc_normals(ev, tarray), tarray, color, atoms_arg)

axis_factor = dp['axis_factor']
if axis_factor is not None:
color_param = dp['axis_color']
thickness = dp['axis_thickness']
for atom, drawing, atoms_arg, axes, lengths2 in drawing_info:
for atoms_arg, axes, lengths2 in drawing_info:
atom = atoms_arg[0]
if color_param is None:
# match atom
color = atom.color
Expand All @@ -187,7 +184,7 @@ def _create_depictions(self):
ev = dot(ee, axes)
ev += atom.coord
tarray = cube_triangles
drawing.add_shape(ev, calc_normals(ev, tarray), tarray, color, atoms_arg)
self.atom_depictions.add_shape(ev, calc_normals(ev, tarray), tarray, color, atoms_arg)

ellipse_factor = dp['ellipse_factor']
if ellipse_factor is not None:
Expand All @@ -198,7 +195,8 @@ def _create_depictions(self):
nz, nc = cylinder_divisions(1.0, 1.0, 9 * (2**smoothing))
self._cylinder_cache[smoothing] = cylinder_geometry(1.0, 1.0, nz, nc, True)
ellipse_vertices, ellipse_triangles = self._cylinder_cache[smoothing]
for atom, drawing, atoms_arg, axes, lengths2 in drawing_info:
for atoms_arg, axes, lengths2 in drawing_info:
atom = atoms_arg[0]
if color_param is None:
# match atom
color = atom.color
Expand All @@ -214,7 +212,10 @@ def _create_depictions(self):
ev = dot(ee, axes)
ev += atom.coord
tarray = ellipse_triangles
drawing.add_shape(ev, calc_normals(ev, tarray), tarray, color, atoms_arg)
self.atom_depictions.add_shape(ev, calc_normals(ev, tarray), tarray, color, atoms_arg)

def _filter_aniso(self, atoms):
return atoms[atoms.has_aniso_u]

def _models_closed_cb(self, trigger_name, closed_models):
if self.structure in closed_models:
Expand All @@ -228,6 +229,7 @@ def reset_state(self, session):
def restore_snapshot(cls, session, data):
inst = cls(session, data['structure'], from_session=True)
inst.atom_depictions = data['atom_depictions']
inst.structure.add_drawing(inst.atom_depictions)
inst.drawing_params = data['drawing_params']
inst.shown_atoms = data['shown_atoms']
def delayed_registration(*args, inst=inst):
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/atomic/bundle_info.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- Edit bundle_info.xml.in, not bundle_info.xml; then run make_selectors.py -->
<BundleInfo name="ChimeraX-Atomic" version="1.60"
<BundleInfo name="ChimeraX-Atomic" version="1.60.2"
package="chimerax.atomic"
purePython="false"
customInit="true"
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/atomic/bundle_info.xml.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- Edit bundle_info.xml.in, not bundle_info.xml; then run make_selectors.py -->
<BundleInfo name="ChimeraX-Atomic" version="1.60"
<BundleInfo name="ChimeraX-Atomic" version="1.60.2"
package="chimerax.atomic"
purePython="false"
customInit="true"
Expand Down
4 changes: 4 additions & 0 deletions src/bundles/atomic/src/molobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ def deleted(self):
def __copy__(self, copy_seq=None):
if copy_seq is None:
copy_seq = Sequence(name=self.name, characters=self.characters)
if hasattr(self, 'description'):
copy_seq.description = self.description
else:
copy_seq.characters = self.characters
from copy import copy
Expand Down Expand Up @@ -1139,6 +1141,8 @@ def bulk_set(self, residues, characters, *, fire_triggers=True):
self._fire_trigger('residues changed', self)

def __copy__(self):
if self.structure is None:
return super().__copy__()
f = c_function('sseq_copy', args = (ctypes.c_void_p,), ret = ctypes.c_void_p)
copy_sseq = StructureSeq(f(self._c_pointer))
Sequence.__copy__(self, copy_seq = copy_sseq)
Expand Down
1 change: 1 addition & 0 deletions src/bundles/atomic/src/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ def _execute(session, name):
residues.ribbon_displays = False
residues.ring_displays = False
s.worm_ribbon = False
s.ribbon_mode_helix = s.RIBBON_MODE_DEFAULT
s.apply_auto_styling(**kw)
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ t0 = t1;
continue;
if (sqlen > p4n2c)
continue;
if (bondee->idatm_type() == "Cac")
if (bondee->idatm_type() == "Cac" || bondee->idatm_type() == "C")
continue;
if (bondee->idatm_type() == "C2") {
bool grand_O2 = false;
Expand Down Expand Up @@ -1048,7 +1048,7 @@ std::cerr << "pass 3 took " << (t1 - t0) / (float)CLOCKS_PER_SEC << " seconds\n"
t0 = t1;
#endif

// "pass 4": re-examine all atoms with non-zero 'redo' values and
// "pass 4": re-examine most atoms with non-zero 'redo' values and
// retype them if necessary
for (auto a: untyped_atoms) {

Expand Down Expand Up @@ -1081,7 +1081,7 @@ t0 = t1;
a->set_computed_idatm_type("Npl");
break;
}
} else {
} else if (redo[a] == 3) {
if ((sqlen <= p4c2c && bondee_element == Element::C)
|| (sqlen <= p4c2n && bondee_element == Element::N)) {
a->set_computed_idatm_type("C2");
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/atomic_lib/bundle_info.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<BundleInfo name="ChimeraX-AtomicLibrary" version="14.1.11"
<BundleInfo name="ChimeraX-AtomicLibrary" version="14.1.13"
package="chimerax.atomic_lib"
purePython="false"
installedDataDir="data"
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/clashes/src/clashes.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def find_clashes(session, test_atoms,
if r:
chain_pos[r] = i
from chimerax.atom_search import AtomSearchTree
tree = AtomSearchTree(search_atoms, scene_coords=inter_model)
tree = AtomSearchTree(search_atoms, scene_coords=use_scene_coords)
clashes = {}
from chimerax.geometry import distance
intra_mol_map = {}
Expand Down
23 changes: 15 additions & 8 deletions src/bundles/dicom/src/dicom_hierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,14 +890,21 @@ def pixel_spacing(self):
return x_scale, y_scale, z_scale

def rotation(self):
affine = self.affine
x_scale, y_scale, z_scale = self.pixel_spacing()
rotation_matrix = [
[affine[0][0] / x_scale, affine[0][1] / y_scale, affine[0][2] / z_scale],
[affine[1][0] / x_scale, affine[1][1] / y_scale, affine[1][2] / z_scale],
[affine[2][0] / x_scale, affine[2][1] / y_scale, affine[2][2] / z_scale],
]
return rotation_matrix
#affine = self.affine
#x_scale, y_scale, z_scale = self.pixel_spacing()
#rotation_matrix = [
# [affine[0][0] / x_scale, affine[0][1] / y_scale, affine[0][2] / z_scale],
# [affine[1][0] / x_scale, affine[1][1] / y_scale, affine[1][2] / z_scale],
# [affine[2][0] / x_scale, affine[2][1] / y_scale, affine[2][2] / z_scale],
#]
# We're ignoring the rotation given by the DICOM files until someone complains about it.
# Doing this simplifies other areas of the codebase significantly.
# 1) The plane viewers use orthographic cameras pointed down the X, Y, and Z axes, and
# ignoring the rotations of the files means we don't have to calculate new axes to
# point the cameras down when files aren't axis aligned.
# 2) We don't have to modify the raycasting shader to do such calculations either.
return [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
#return rotation_matrix

def origin(self):
affine = self.affine
Expand Down
8 changes: 7 additions & 1 deletion src/bundles/graphics/src/opengl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3199,7 +3199,13 @@ def reload_texture(self, data, now = False):
'''
# PyOpenGL 3.1.5 leaks memory if data not contiguous, PyOpenGL github issue #47.
d = data if data.flags['C_CONTIGUOUS'] else data.copy()
self.data = d
# OpenGL doesn't support float64 textures so if for some reason that's the data type
# we end up with truncate it to float32
from numpy import float64, float32
if d.dtype == float64:
self.data = d.astype(float32)
else:
self.data = d
if now:
self.fill_opengl_texture()

Expand Down
3 changes: 3 additions & 0 deletions src/bundles/map/src/image3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,9 @@ def _auto_projection_mode(self):
pm = ("2d-x", "2d-y", "2d-z")[list(s).index(smin)]
else:
pm = "3d"
if pm == 'rays':
self._rendering_options.colormap_on_gpu = True
self._rendering_options.full_region_on_gpu = True
return pm

# ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/bundles/match_maker/bundle_info.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<BundleInfo name="ChimeraX-MatchMaker" version="2.1.6"
<BundleInfo name="ChimeraX-MatchMaker" version="2.1.7"
package="chimerax.match_maker"
minSessionVersion="1" maxSessionVersion="1">

Expand Down
Loading

0 comments on commit fde3a61

Please sign in to comment.