From e89ec80073a24824edb0b0c79f3e07cb8198c58f Mon Sep 17 00:00:00 2001 From: St3451 Date: Thu, 28 May 2026 17:58:58 +0200 Subject: [PATCH] feat: auto-detect AlphaFold version from data dir in chimerax-plot The chimerax-plot subcommand reconstructs the PDB filename from the --af_version CLI option (default 6), but the Nextflow pipeline never forwards that option and MANE builds force v4. Result: when a user runs chimerax-plot against a v4 dataset without passing --af_version, every candidate PDB path 404s and the script silently skips every gene with no output. Inspect /pdb_structures/ at runtime and pick the AF version actually present. The user-supplied --af_version is used only as a tiebreaker when multiple versions coexist; otherwise it's a hint at best. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/plotting/chimerax_plot.py | 5 +++ scripts/plotting/utils.py | 61 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/scripts/plotting/chimerax_plot.py b/scripts/plotting/chimerax_plot.py index 9e37a7a..b63ba41 100644 --- a/scripts/plotting/chimerax_plot.py +++ b/scripts/plotting/chimerax_plot.py @@ -11,6 +11,7 @@ import daiquiri from scripts import __logger_name__ +from scripts.plotting.utils import detect_af_version logger = daiquiri.getLogger(__logger_name__ + ".plotting.chimerax_plot") @@ -152,6 +153,10 @@ def generate_chimerax_plot(output_dir, result = result.rename(columns={"Ratio_obs_sim" : "Score_obs_sim"}) result["Logscore_obs_sim"] = np.log(result["Score_obs_sim"]) + # Detect the AlphaFold version actually present in the dataset; the + # user-supplied --af_version is used as a tiebreaker only. + af_version = detect_af_version(datasets_dir, requested_version=af_version) + # Process each gene genes = gene_result[gene_result["C_gene"] == 1].Gene.unique() if len(genes) > 0: diff --git a/scripts/plotting/utils.py b/scripts/plotting/utils.py index 101222d..35bca05 100644 --- a/scripts/plotting/utils.py +++ b/scripts/plotting/utils.py @@ -1,4 +1,5 @@ import os +import re import sys import json import logging @@ -16,6 +17,66 @@ logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) +_AF_VERSION_RE = re.compile(r"-model_v(\d+)\.pdb(?:\.gz)?$") + + +def detect_af_version(datasets_dir, requested_version=None): + """ + Detect the AlphaFold version of structures in `datasets_dir/pdb_structures/`. + + Build-datasets writes PDB files named `AF--F-model_v.pdb[.gz]`. + The user-facing `--af_version` flag is easy to get wrong (e.g., a MANE + build forces v4 but the CLI default is v6), and the pipeline doesn't + forward it, leading to silent "no plots" failures when chimerax-plot + looks for the wrong filename. + + This helper inspects the directory and returns the version actually + present. If `requested_version` is supplied, it's used as a tiebreaker + when multiple versions coexist; otherwise the highest version is chosen. + + Returns the detected version (int), or `requested_version` if detection + is not possible. + """ + pdb_dir = os.path.join(datasets_dir, "pdb_structures") + if not os.path.isdir(pdb_dir): + logger.warning( + f"{pdb_dir} is not a directory; falling back to --af_version={requested_version}" + ) + return requested_version + + versions = set() + for fname in os.listdir(pdb_dir): + match = _AF_VERSION_RE.search(fname) + if match: + versions.add(int(match.group(1))) + + if not versions: + logger.warning( + f"No AlphaFold PDB files found in {pdb_dir}; " + f"falling back to --af_version={requested_version}" + ) + return requested_version + + if len(versions) == 1: + detected = next(iter(versions)) + if requested_version is not None and int(requested_version) != detected: + logger.warning( + f"Detected AlphaFold v{detected} in {pdb_dir}, " + f"but --af_version was set to {requested_version}. Using v{detected}." + ) + return detected + + sorted_versions = sorted(versions) + if requested_version is not None and int(requested_version) in versions: + chosen = int(requested_version) + else: + chosen = max(versions) + logger.warning( + f"Multiple AlphaFold versions found in {pdb_dir}: {sorted_versions}. Using v{chosen}." + ) + return chosen + + def get_species(species): """ Simply change species name to accepted format.