Hi, I hope the summer is starting off really well for you.
tldr: I leave ChimeraX running movie/image generating commands from a python script in a tmux session and it segfaults on multiple machines.
I was wondering if someone has encountered this before or can lend advice as to why this happens. I'd like to produce some movies (or at least pictures) in bulk (around 2000 individual molecules). I preprocess each one (load locally or open, add some representation, and either start recording a movie or "save XXXX.png"). After about 8-10 successful movies or images are produced I get a ChimeraX segfault and the process dies.
This is quite perplexing to me because
- I tried it on multiple machines: Ubuntu 22.04, installed from the
.deb and Arch, installed from aur distribution.
- both with `--offscreen` and `--nogui` flags and without them ( in fact, following up some threads in the mailing list it seems like it's not possible to produce movies with --nogui, just pngs)
- there seems to be no excessive memory or page cache issues, although i haven't profiled it. Only one core is occupied when rendering both mp4/png's.
Below I'm attaching a crash log from one of the machines (Ubuntu 22.04) and the description of my setup, which is a little awkward, but I don't think it's the cause since it produces some samples successfully before crashing. I hope i'm wrong.
Best regards and any tips are really appreciated! I was really looking forward to having some crisp images.
---------------------------------------------------------------------------------------
I start the process by calling a python script at the command line: ` chimerax --script "/home/student/dev/riboxyz/chimerax/ribrepr.py" --nogui --offscreen` when i tried generated images. This was after I gave up on getting the movies, which i did by just opening a ChimeraX gui on our workstation and calling a "loop.py" after a load the commands from "ribrepr.py" file from the gui command line. I do both in a tmux session.
```loop.py
import os
from chimerax.core.commands import run
from concurrent.futures import ThreadPoolExecutor
DEST_DIR = "/home/rtviii/dev/riboxyz/chimerax/movies"
# This expands to ~1800 PDB ids: rcsb_id:= {4UG0, 7K00 and so on}
for rcsb_id in os.listdir('/home/rtviii/dev/RIBETL_DATA'):
movie_dir = os.path.join(DEST_DIR, "{}.mp4".format(rcsb_id))
print("Attempting to write ", movie_dir)
if "{}.mp4".format(rcsb_id) in os.listdir(DEST_DIR):
print("Skipping {}. Exists".format(rcsb_id))
continue
else:
run(session,"ribmovie {}".format(rcsb_id)) # <---- ribmovie command corresponds to the `produce_and_save_movie` in ribrepr.py below
```
The contents of the `ribrepr.py` are just to register a command for custom visualization of a given molecule + loop over molecules for image generation.
``` ribrepr.py
# just a data folder, it exists.
RIBETL_DATA = os.environ.get("RIBETL_DATA", "/home/rtviii/dev/RIBETL_DATA")
class PolymerClass(str, enum.Enum):
tRNA = "tRNA"
bS1m = "bS1m"
# ... another ~200 enum members, redacted for brevity
CHIMERAX_COLORS = [
["tan", "#d2b48c"], ["sienna", "#a0522d"], #... more colors redacted for brevity
]
# helper function to map colors
def get_polymer_color(polymer_class:str):
polyix = list(map(lambda x: x.value, (PolymerClass)))
class_index = polyix.index(polymer_class) % len(polyix)
if polymer_class == None:
return "gray"
return CHIMERAX_COLORS[(class_index % len(CHIMERAX_COLORS))][1]
def ribosome_representation(session, structure: AtomicStructure):
for _ in list(PolymerClass):
print(_.name)
from chimerax.core.commands import run
from chimerax.core.colors import hex_color
from chimerax.atomic import Residue, Atom, Chain
# i get the PDB ID from the StructureArg
rcsb_id = str(structure.name).upper().split('.')[0] # <-- the structure gets opened with the basename ex "(5AFI.cif)"
run(session, "set bgColor white")
run(session, "sym #1 assembly 1") # take only one assembly if multiple are available
run(session, "hide #2")
with open(os.path.join(RIBETL_DATA, rcsb_id, "{}.json".format(rcsb_id)), "r") as f:
profile = json.load(f) # load my annotations, just a json file
#--------------------------------------------[ This whole bit just colors by chain annotation basically]
polymers = {}
polymer_chains = [
*profile["proteins"],
*profile["rnas"],
*profile["other_polymers"],
]
[ polymers.update(x) for x in [{chain["auth_asym_id"]: chain} for chain in polymer_chains] ]
c: Chain
for c in structure.chains:
aaid = c.chain_id
polyclass = None
if len(polymers[aaid]["nomenclature"]) < 1:
continue
else:
polyclass = polymers[aaid]["nomenclature"][0]
if polymers[aaid]["entity_poly_polymer_type"] == "RNA":
run(session, "surf /{}".format(aaid))
run(session, "transp /{} 100".format(aaid))
run(session, "color /{} gray".format(aaid))
else:
run(session, "show /{} cartoon".format(aaid, get_polymer_color(polyclass)))
run(session, "color /{} {}".format(aaid, get_polymer_color(polyclass)))
#--------------------------------------------[ This whole bit just colors by chain annotation basically]
run(session, "graphics silhouettes true width 1")
run(session, "light soft")
# # ! Didn't work out for now. This is how i tried to make movies. (Loop over every structure (loop.py above) and call this command). Chimerax segfaults when looped on this.
# def produce_and_save_movie(session, target:str):
# print("GOT TARGET", target)
# RCSB_ID = target
# run(session, "open /home/rtviii/dev/RIBETL_DATA/{}/{}.cif".format(RCSB_ID, RCSB_ID))
# run(session, "sym #1 assembly 1") # take only one assembly if multiple are available
# run(session, "ribrep #2")
# run(session, "movie record")
# run(session, "turn y 2 180")
# run(session, "wait 180")
# run(session, "movie encode /home/rtviii/dev/riboxyz/chimerax/movies/{}.mp4".format(RCSB_ID))
# run(session, "close all")
#
# ! This is just to not download the mmcif files i have locally.
def register_ribetl_command(logger):
def ribetl(session, rcsb_id:str):
rcsb_id = rcsb_id.upper()
run(session, "open /home/rtviii/dev/RIBETL_DATA/{}/{}.cif".format(rcsb_id, rcsb_id))
desc = CmdDesc( required= [("rcsb_id", StringArg)], )
register("ribetl", desc, ribetl, logger=logger)
#
# ! Register the above repr sequence as a command.
def register_ribrepr_command(logger):
from chimerax.core.commands import CmdDesc, register
from chimerax.atomic import AtomicStructureArg, Chain, Residue, Atom
desc = CmdDesc(
required = [("structure", AtomicStructureArg)],
required_arguments = ["structure"],
synopsis = "representation ",
)
register("ribrep", desc, ribosome_representation, logger=logger)
register_ribrepr_command(session.logger)
register_ribetl_command(session.logger)
# This loop expands into a list of ~1800 PDB : 5AFI, 4UG0 and so on.
for rcsb_id in os.listdir(RIBETL_DATA):
run(session,"open {}".format(rcsb_id))
run(session,"ribrep #1")
run(session,"save /home/student/dev/riboxyz/chimerax/{}.png width 800 height 800 transparentBackgroun
d true".format(rcsb_id))
run(session,"close all")
```
Let me know if i can elucidate this situation further with hardware specs or whatnot. I'm quite at loss as to how to proceed with this.