From cca5bf0dd4a6103692db1fab6ac8e1df368e196f Mon Sep 17 00:00:00 2001 From: siddhantkhatod Date: Mon, 29 Jun 2026 05:51:59 +0530 Subject: [PATCH 1/4] Fix ligand selection when cofactors share resname UNK In OpenFE hybrid topologies, both ligands and cofactors can be assigned resname UNK. gather_rms_data() now auto-detects the actual ligand by checking for hybrid tempfactors (0.25, 0.5, 0.75) when the default 'resname UNK' selection matches multiple residues. Fixes #118 --- news/118-fix-ligand-cofactor-selection.rst | 6 ++ src/openfe_analysis/rmsd.py | 59 ++++++++++++- .../tests/test_gather_rms_data.py | 82 ++++++++++++++++++- 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 news/118-fix-ligand-cofactor-selection.rst diff --git a/news/118-fix-ligand-cofactor-selection.rst b/news/118-fix-ligand-cofactor-selection.rst new file mode 100644 index 0000000..2aa6035 --- /dev/null +++ b/news/118-fix-ligand-cofactor-selection.rst @@ -0,0 +1,6 @@ +**Fixed:** + +* ``gather_rms_data`` now correctly identifies the ligand when cofactors + share the same residue name ``UNK``. The ligand is auto-detected using + hybrid topology tempfactors (b-factors), while custom ``ligand_selection`` + strings are still respected. diff --git a/src/openfe_analysis/rmsd.py b/src/openfe_analysis/rmsd.py index 9c86200..e6e2250 100644 --- a/src/openfe_analysis/rmsd.py +++ b/src/openfe_analysis/rmsd.py @@ -255,6 +255,63 @@ def _single_frame(self) -> None: self._initial_com, ) +def _select_ligand( + universe: mda.Universe, + ligand_selection: str, +) -> mda.AtomGroup: + """ + Select the ligand AtomGroup, handling cofactors with the same resname. + + In OpenFE hybrid topologies, the alchemical ligand atoms have tempfactors + (b-factors) of 0.25, 0.5, or 0.75, while cofactors typically have + tempfactors of 0.0. When the default selection `"resname UNK"` matches + multiple residues, the ligand is identified as the residue containing + atoms with tempfactors strictly between 0 and 1. + + Parameters + ---------- + universe : mda.Universe + The MDAnalysis universe to select from. + ligand_selection : str + MDAnalysis selection string. If this is the default + `"resname UNK"` and multiple residues match, the ligand is + auto-detected using tempfactors. + + Returns + ------- + mda.AtomGroup + The selected ligand atoms. + """ + ligand = universe.select_atoms(ligand_selection) + + # If user provided a custom selection, only one residue matches, + # or no atoms matched, return the selection as-is. + if ligand_selection != "resname UNK" or len(ligand.residues) <= 1 or ligand.n_atoms == 0: + return ligand + + # Multiple UNK residues: identify the ligand by hybrid tempfactors. + # In OpenFE hybrid topologies, alchemical atoms have tempfactors + # of 0.25 (state A unique), 0.5 (shared), or 0.75 (state B unique). + try: + has_tempfactors = True + universe.atoms.tempfactors + except AttributeError: + has_tempfactors = False + + if has_tempfactors: + hybrid_residues = [ + res + for res in ligand.residues + if any(0.0 < tf < 1.0 for tf in res.atoms.tempfactors) + ] + + if len(hybrid_residues) == 1: + return hybrid_residues[0].atoms + elif len(hybrid_residues) > 1: + return max(hybrid_residues, key=lambda r: len(r.atoms)).atoms + + # Fallback: no hybrid residues found or no tempfactors available + return max(ligand.residues, key=lambda r: len(r.atoms)).atoms def gather_rms_data( pdb_topology: pathlib.Path, @@ -335,7 +392,7 @@ def gather_rms_data( universe = create_universe_single_state(u_top._topology, ds, state_idx) prot = universe.select_atoms(protein_selection) - ligand = universe.select_atoms(ligand_selection) + ligand = _select_ligand(universe, ligand_selection) if prot: apply_complex_alignment_transformations( diff --git a/src/openfe_analysis/tests/test_gather_rms_data.py b/src/openfe_analysis/tests/test_gather_rms_data.py index 9734548..4826233 100644 --- a/src/openfe_analysis/tests/test_gather_rms_data.py +++ b/src/openfe_analysis/tests/test_gather_rms_data.py @@ -1,6 +1,8 @@ import numpy as np from numpy.testing import assert_allclose - +import MDAnalysis as mda +import pytest +from openfe_analysis.rmsd import _select_ligand from openfe_analysis.rmsd import gather_rms_data @@ -92,3 +94,81 @@ def test_gather_rms_data_ligand_only(simulation_skipped_nc, hybrid_system_skippe # Ligand results should still be present assert len(output["ligand_RMSD"]) > 0 assert len(output["ligand_wander"]) > 0 + + def _make_test_universe(tempfactors, resids, resnames=None): + """ Create a minimal in-memory Universe for testing _select_ligand """ + n_atoms = len(tempfactors) + n_residues = len(set(resids)) + unique_resids = list(dict.fromkeys(resids)) + resid_to_idx = {r: i for i, r in enumerate(unique_resids)} + atom_resindex = [resid_to_idx[r] for r in resids] + + if resnames is None: + resnames = ["UNK"] * n_residues + + u = mda.Universe.empty( + n_atoms=n_atoms, + n_residues=n_residues, + atom_resindex=atom_resindex, + ) + u.add_TopologyAttr("names", [f"C{i}" for i in range(1, n_atoms + 1)]) + u.add_TopologyAttr("resnames", resnames) + u.add_TopologyAttr("resids", unique_resids) + u.add_TopologyAttr("tempfactors", tempfactors) + return u + + +def test_select_ligand_single_unk(): + """When only one UNK residue exists, selection is unchanged.""" + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75], + resids=[1, 1, 1], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + + +def test_select_ligand_cofactor_present(): + """When a cofactor shares resname UNK, pick the ligand by tempfactors.""" + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75, 0.00], + resids=[1, 1, 1, 2], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + + +def test_select_ligand_multiple_hybrid(): + """If multiple residues have hybrid tempfactors, pick the largest.""" + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75, 0.25, 0.50], + resids=[1, 1, 1, 2, 2], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + + +def test_select_ligand_no_hybrid_fallback(): + """If no residue has hybrid tempfactors, fall back to the largest.""" + u = _make_test_universe( + tempfactors=[0.00, 0.00, 0.00, 0.00], + resids=[1, 1, 2, 2], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 2 + + +def test_select_ligand_custom_selection_unchanged(): + """Custom ligand_selection is passed through without auto-detection.""" + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75, 0.00], + resids=[1, 1, 1, 2], + resnames=["LIG", "LIG", "LIG", "COF"], + ) + ligand = _select_ligand(u, "resname LIG") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + From 7c401ec1978a6f5a243b092588baabd8277fd20c Mon Sep 17 00:00:00 2001 From: siddhantkhatod Date: Mon, 29 Jun 2026 06:38:47 +0530 Subject: [PATCH 2/4] Fix ligand selection when cofactors share resname UNK In OpenFE hybrid topologies, both ligands and cofactors can be assigned resname UNK. gather_rms_data() now auto-detects the actual ligand by checking for hybrid tempfactors (0.25, 0.5, 0.75) when the default 'resname UNK' selection matches multiple residues. Fixes #118 --- fix_test.py | 167 ++++++++++++++++++ .../tests/test_gather_rms_data.py | 25 +-- 2 files changed, 173 insertions(+), 19 deletions(-) create mode 100644 fix_test.py diff --git a/fix_test.py b/fix_test.py new file mode 100644 index 0000000..c9e0936 --- /dev/null +++ b/fix_test.py @@ -0,0 +1,167 @@ +content = """import MDAnalysis as mda +import numpy as np +from numpy.testing import assert_allclose + +from openfe_analysis.rmsd import _select_ligand, gather_rms_data + + +def test_gather_rms_data_regression(simulation_nc, hybrid_system_pdb): + output = gather_rms_data( + hybrid_system_pdb, + simulation_nc, + skip=100, + ) + + assert_allclose(output["time(ps)"], [0.0, 100.0, 200.0, 300.0, 400.0, 500.0]) + assert len(output["protein_RMSD"]) == 3 + assert_allclose( + output["protein_RMSD"][0], + [0.0, 1.003, 1.276, 1.263, 1.516, 1.251], + rtol=1e-3, + ) + assert len(output["ligand_RMSD"]) == 3 + assert_allclose( + output["ligand_RMSD"][0], + [0.0, 0.9094, 1.0398, 0.9774, 1.9108, 1.2149], + rtol=1e-3, + ) + assert len(output["ligand_wander"]) == 3 + assert_allclose( + output["ligand_wander"][0], + [0.0, 0.5458, 0.8364, 0.4914, 1.1939, 0.7587], + rtol=1e-3, + ) + assert len(output["protein_2D_RMSD"]) == 3 + assert len(output["protein_2D_RMSD"][0]) == 15 + assert_allclose( + output["protein_2D_RMSD"][0][:6], + [1.0029, 1.2756, 1.2635, 1.5165, 1.2509, 1.0882], + rtol=1e-3, + ) + + +def test_gather_rms_data_regression_skippednc(simulation_skipped_nc, hybrid_system_skipped_pdb): + output = gather_rms_data( + hybrid_system_skipped_pdb, + simulation_skipped_nc, + skip=None, + ) + + assert_allclose(output["time(ps)"], np.arange(0, 5001, 100)) + assert len(output["protein_RMSD"]) == 11 + assert_allclose( + output["protein_RMSD"][0][:6], + [0, 1.089747, 1.006143, 1.045068, 1.476353, 1.332893], + rtol=1e-3, + ) + assert len(output["ligand_RMSD"]) == 11 + assert_allclose( + output["ligand_RMSD"][0][:6], + [0.0, 1.092039, 0.839234, 1.228383, 1.533331, 1.276798], + rtol=1e-3, + ) + assert len(output["ligand_wander"]) == 11 + assert_allclose( + output["ligand_wander"][0][:6], + [0.0, 0.908097, 0.674262, 0.971328, 0.909263, 1.101882], + rtol=1e-3, + ) + assert len(output["protein_2D_RMSD"]) == 11 + assert len(output["protein_2D_RMSD"][0]) == 1275 + assert_allclose( + output["protein_2D_RMSD"][0][:6], + [1.089747, 1.006143, 1.045068, 1.476353, 1.332893, 1.110507], + rtol=1e-3, + ) + + +def test_gather_rms_data_ligand_only(simulation_skipped_nc, hybrid_system_skipped_pdb): + output = gather_rms_data( + hybrid_system_skipped_pdb, + simulation_skipped_nc, + skip=100, + protein_selection="resname DOESNOTEXIST", + ) + + assert len(output["protein_RMSD"]) == 0 + assert len(output["protein_2D_RMSD"]) == 0 + assert len(output["ligand_RMSD"]) > 0 + assert len(output["ligand_wander"]) > 0 + + +def _make_test_universe(tempfactors, resids, resnames=None): + n_atoms = len(tempfactors) + n_residues = len(set(resids)) + unique_resids = list(dict.fromkeys(resids)) + resid_to_idx = {r: i for i, r in enumerate(unique_resids)} + atom_resindex = [resid_to_idx[r] for r in resids] + + if resnames is None: + resnames = ["UNK"] * n_residues + + u = mda.Universe.empty( + n_atoms=n_atoms, + n_residues=n_residues, + atom_resindex=atom_resindex, + ) + u.add_TopologyAttr("names", [f"C{i}" for i in range(1, n_atoms + 1)]) + u.add_TopologyAttr("resnames", resnames) + u.add_TopologyAttr("resids", unique_resids) + u.add_TopologyAttr("tempfactors", tempfactors) + return u + + +def test_select_ligand_single_unk(): + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75], + resids=[1, 1, 1], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + + +def test_select_ligand_cofactor_present(): + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75, 0.00], + resids=[1, 1, 1, 2], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + + +def test_select_ligand_multiple_hybrid(): + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75, 0.25, 0.50], + resids=[1, 1, 1, 2, 2], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} + + +def test_select_ligand_no_hybrid_fallback(): + u = _make_test_universe( + tempfactors=[0.00, 0.00, 0.00, 0.00], + resids=[1, 1, 2, 2], + ) + ligand = _select_ligand(u, "resname UNK") + assert len(ligand) == 2 + + +def test_select_ligand_custom_selection_unchanged(): + u = _make_test_universe( + tempfactors=[0.25, 0.50, 0.75, 0.00], + resids=[1, 1, 1, 2], + resnames=["LIG", "LIG", "LIG", "COF"], + ) + ligand = _select_ligand(u, "resname LIG") + assert len(ligand) == 3 + assert set(ligand.resids) == {1} +""" + +with open('src/openfe_analysis/tests/test_gather_rms_data.py', 'w') as f: + f.write(content) + +print("Done! File written successfully.") \ No newline at end of file diff --git a/src/openfe_analysis/tests/test_gather_rms_data.py b/src/openfe_analysis/tests/test_gather_rms_data.py index 4826233..5f31747 100644 --- a/src/openfe_analysis/tests/test_gather_rms_data.py +++ b/src/openfe_analysis/tests/test_gather_rms_data.py @@ -1,9 +1,8 @@ +import MDAnalysis as mda import numpy as np from numpy.testing import assert_allclose -import MDAnalysis as mda -import pytest -from openfe_analysis.rmsd import _select_ligand -from openfe_analysis.rmsd import gather_rms_data + +from openfe_analysis.rmsd import _select_ligand, gather_rms_data def test_gather_rms_data_regression(simulation_nc, hybrid_system_pdb): @@ -33,7 +32,6 @@ def test_gather_rms_data_regression(simulation_nc, hybrid_system_pdb): rtol=1e-3, ) assert len(output["protein_2D_RMSD"]) == 3 - # 15 entries because 6 * 6 frames // 2 assert len(output["protein_2D_RMSD"][0]) == 15 assert_allclose( output["protein_2D_RMSD"][0][:6], @@ -51,7 +49,6 @@ def test_gather_rms_data_regression_skippednc(simulation_skipped_nc, hybrid_syst assert_allclose(output["time(ps)"], np.arange(0, 5001, 100)) assert len(output["protein_RMSD"]) == 11 - # RMSD is low for this multichain protein assert_allclose( output["protein_RMSD"][0][:6], [0, 1.089747, 1.006143, 1.045068, 1.476353, 1.332893], @@ -70,7 +67,6 @@ def test_gather_rms_data_regression_skippednc(simulation_skipped_nc, hybrid_syst rtol=1e-3, ) assert len(output["protein_2D_RMSD"]) == 11 - # 15 entries because 6 * 6 frames // 2 assert len(output["protein_2D_RMSD"][0]) == 1275 assert_allclose( output["protein_2D_RMSD"][0][:6], @@ -84,19 +80,16 @@ def test_gather_rms_data_ligand_only(simulation_skipped_nc, hybrid_system_skippe hybrid_system_skipped_pdb, simulation_skipped_nc, skip=100, - protein_selection="resname DOESNOTEXIST", # no protein + protein_selection="resname DOESNOTEXIST", ) - # No protein results assert len(output["protein_RMSD"]) == 0 assert len(output["protein_2D_RMSD"]) == 0 - - # Ligand results should still be present assert len(output["ligand_RMSD"]) > 0 assert len(output["ligand_wander"]) > 0 - def _make_test_universe(tempfactors, resids, resnames=None): - """ Create a minimal in-memory Universe for testing _select_ligand """ + +def _make_test_universe(tempfactors, resids, resnames=None): n_atoms = len(tempfactors) n_residues = len(set(resids)) unique_resids = list(dict.fromkeys(resids)) @@ -119,7 +112,6 @@ def _make_test_universe(tempfactors, resids, resnames=None): def test_select_ligand_single_unk(): - """When only one UNK residue exists, selection is unchanged.""" u = _make_test_universe( tempfactors=[0.25, 0.50, 0.75], resids=[1, 1, 1], @@ -130,7 +122,6 @@ def test_select_ligand_single_unk(): def test_select_ligand_cofactor_present(): - """When a cofactor shares resname UNK, pick the ligand by tempfactors.""" u = _make_test_universe( tempfactors=[0.25, 0.50, 0.75, 0.00], resids=[1, 1, 1, 2], @@ -141,7 +132,6 @@ def test_select_ligand_cofactor_present(): def test_select_ligand_multiple_hybrid(): - """If multiple residues have hybrid tempfactors, pick the largest.""" u = _make_test_universe( tempfactors=[0.25, 0.50, 0.75, 0.25, 0.50], resids=[1, 1, 1, 2, 2], @@ -152,7 +142,6 @@ def test_select_ligand_multiple_hybrid(): def test_select_ligand_no_hybrid_fallback(): - """If no residue has hybrid tempfactors, fall back to the largest.""" u = _make_test_universe( tempfactors=[0.00, 0.00, 0.00, 0.00], resids=[1, 1, 2, 2], @@ -162,7 +151,6 @@ def test_select_ligand_no_hybrid_fallback(): def test_select_ligand_custom_selection_unchanged(): - """Custom ligand_selection is passed through without auto-detection.""" u = _make_test_universe( tempfactors=[0.25, 0.50, 0.75, 0.00], resids=[1, 1, 1, 2], @@ -171,4 +159,3 @@ def test_select_ligand_custom_selection_unchanged(): ligand = _select_ligand(u, "resname LIG") assert len(ligand) == 3 assert set(ligand.resids) == {1} - From 024368a066cc3e9703de323aecb013f582355eaa Mon Sep 17 00:00:00 2001 From: siddhantkhatod Date: Mon, 29 Jun 2026 07:26:27 +0530 Subject: [PATCH 3/4] Delete fix_test.py --- fix_test.py | 167 ---------------------------------------------------- 1 file changed, 167 deletions(-) delete mode 100644 fix_test.py diff --git a/fix_test.py b/fix_test.py deleted file mode 100644 index c9e0936..0000000 --- a/fix_test.py +++ /dev/null @@ -1,167 +0,0 @@ -content = """import MDAnalysis as mda -import numpy as np -from numpy.testing import assert_allclose - -from openfe_analysis.rmsd import _select_ligand, gather_rms_data - - -def test_gather_rms_data_regression(simulation_nc, hybrid_system_pdb): - output = gather_rms_data( - hybrid_system_pdb, - simulation_nc, - skip=100, - ) - - assert_allclose(output["time(ps)"], [0.0, 100.0, 200.0, 300.0, 400.0, 500.0]) - assert len(output["protein_RMSD"]) == 3 - assert_allclose( - output["protein_RMSD"][0], - [0.0, 1.003, 1.276, 1.263, 1.516, 1.251], - rtol=1e-3, - ) - assert len(output["ligand_RMSD"]) == 3 - assert_allclose( - output["ligand_RMSD"][0], - [0.0, 0.9094, 1.0398, 0.9774, 1.9108, 1.2149], - rtol=1e-3, - ) - assert len(output["ligand_wander"]) == 3 - assert_allclose( - output["ligand_wander"][0], - [0.0, 0.5458, 0.8364, 0.4914, 1.1939, 0.7587], - rtol=1e-3, - ) - assert len(output["protein_2D_RMSD"]) == 3 - assert len(output["protein_2D_RMSD"][0]) == 15 - assert_allclose( - output["protein_2D_RMSD"][0][:6], - [1.0029, 1.2756, 1.2635, 1.5165, 1.2509, 1.0882], - rtol=1e-3, - ) - - -def test_gather_rms_data_regression_skippednc(simulation_skipped_nc, hybrid_system_skipped_pdb): - output = gather_rms_data( - hybrid_system_skipped_pdb, - simulation_skipped_nc, - skip=None, - ) - - assert_allclose(output["time(ps)"], np.arange(0, 5001, 100)) - assert len(output["protein_RMSD"]) == 11 - assert_allclose( - output["protein_RMSD"][0][:6], - [0, 1.089747, 1.006143, 1.045068, 1.476353, 1.332893], - rtol=1e-3, - ) - assert len(output["ligand_RMSD"]) == 11 - assert_allclose( - output["ligand_RMSD"][0][:6], - [0.0, 1.092039, 0.839234, 1.228383, 1.533331, 1.276798], - rtol=1e-3, - ) - assert len(output["ligand_wander"]) == 11 - assert_allclose( - output["ligand_wander"][0][:6], - [0.0, 0.908097, 0.674262, 0.971328, 0.909263, 1.101882], - rtol=1e-3, - ) - assert len(output["protein_2D_RMSD"]) == 11 - assert len(output["protein_2D_RMSD"][0]) == 1275 - assert_allclose( - output["protein_2D_RMSD"][0][:6], - [1.089747, 1.006143, 1.045068, 1.476353, 1.332893, 1.110507], - rtol=1e-3, - ) - - -def test_gather_rms_data_ligand_only(simulation_skipped_nc, hybrid_system_skipped_pdb): - output = gather_rms_data( - hybrid_system_skipped_pdb, - simulation_skipped_nc, - skip=100, - protein_selection="resname DOESNOTEXIST", - ) - - assert len(output["protein_RMSD"]) == 0 - assert len(output["protein_2D_RMSD"]) == 0 - assert len(output["ligand_RMSD"]) > 0 - assert len(output["ligand_wander"]) > 0 - - -def _make_test_universe(tempfactors, resids, resnames=None): - n_atoms = len(tempfactors) - n_residues = len(set(resids)) - unique_resids = list(dict.fromkeys(resids)) - resid_to_idx = {r: i for i, r in enumerate(unique_resids)} - atom_resindex = [resid_to_idx[r] for r in resids] - - if resnames is None: - resnames = ["UNK"] * n_residues - - u = mda.Universe.empty( - n_atoms=n_atoms, - n_residues=n_residues, - atom_resindex=atom_resindex, - ) - u.add_TopologyAttr("names", [f"C{i}" for i in range(1, n_atoms + 1)]) - u.add_TopologyAttr("resnames", resnames) - u.add_TopologyAttr("resids", unique_resids) - u.add_TopologyAttr("tempfactors", tempfactors) - return u - - -def test_select_ligand_single_unk(): - u = _make_test_universe( - tempfactors=[0.25, 0.50, 0.75], - resids=[1, 1, 1], - ) - ligand = _select_ligand(u, "resname UNK") - assert len(ligand) == 3 - assert set(ligand.resids) == {1} - - -def test_select_ligand_cofactor_present(): - u = _make_test_universe( - tempfactors=[0.25, 0.50, 0.75, 0.00], - resids=[1, 1, 1, 2], - ) - ligand = _select_ligand(u, "resname UNK") - assert len(ligand) == 3 - assert set(ligand.resids) == {1} - - -def test_select_ligand_multiple_hybrid(): - u = _make_test_universe( - tempfactors=[0.25, 0.50, 0.75, 0.25, 0.50], - resids=[1, 1, 1, 2, 2], - ) - ligand = _select_ligand(u, "resname UNK") - assert len(ligand) == 3 - assert set(ligand.resids) == {1} - - -def test_select_ligand_no_hybrid_fallback(): - u = _make_test_universe( - tempfactors=[0.00, 0.00, 0.00, 0.00], - resids=[1, 1, 2, 2], - ) - ligand = _select_ligand(u, "resname UNK") - assert len(ligand) == 2 - - -def test_select_ligand_custom_selection_unchanged(): - u = _make_test_universe( - tempfactors=[0.25, 0.50, 0.75, 0.00], - resids=[1, 1, 1, 2], - resnames=["LIG", "LIG", "LIG", "COF"], - ) - ligand = _select_ligand(u, "resname LIG") - assert len(ligand) == 3 - assert set(ligand.resids) == {1} -""" - -with open('src/openfe_analysis/tests/test_gather_rms_data.py', 'w') as f: - f.write(content) - -print("Done! File written successfully.") \ No newline at end of file From fe88b10afc458a177efa728bcdc07c84c3b531a9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 02:07:53 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/openfe_analysis/rmsd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openfe_analysis/rmsd.py b/src/openfe_analysis/rmsd.py index e6e2250..71eba08 100644 --- a/src/openfe_analysis/rmsd.py +++ b/src/openfe_analysis/rmsd.py @@ -255,6 +255,7 @@ def _single_frame(self) -> None: self._initial_com, ) + def _select_ligand( universe: mda.Universe, ligand_selection: str, @@ -300,9 +301,7 @@ def _select_ligand( if has_tempfactors: hybrid_residues = [ - res - for res in ligand.residues - if any(0.0 < tf < 1.0 for tf in res.atoms.tempfactors) + res for res in ligand.residues if any(0.0 < tf < 1.0 for tf in res.atoms.tempfactors) ] if len(hybrid_residues) == 1: @@ -313,6 +312,7 @@ def _select_ligand( # Fallback: no hybrid residues found or no tempfactors available return max(ligand.residues, key=lambda r: len(r.atoms)).atoms + def gather_rms_data( pdb_topology: pathlib.Path, dataset: pathlib.Path,