RosettaCommons / RosettaCommons/foundry

[BUG] Hydrogen Bond Metrics Not Getting Calculated

Open
#374 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
966
Forks
181
Avg merge
4d 4h
Merged PRs (30d)
2

Description

Describe the bug
When select_hbond_donor or select_hbond_acceptor are included in the input and HBPLUS is properly installed, hydrogen bond metrics are still not getting calculated. Setting verbose to True gives this error:
WARNING rfd3.metrics.hbonds_hbplus_metrics: [rank: 0] Could not calculate hbond metrics: 'AtomArray' object has no attribute 'is_motif_atom'

To Reproduce
HBPLUS must be installed and the environment variable must be set as is described in the README

The input PDB, JSON, and command line arguments I used for the run are attached along with a logfile and the warnings/error messages seen during the run.

I am using the version of RFD3 available through the foundry module on PyPI.

theozyme.pdb
metalloprotease_rfd3_input_hbond.json
logfile.txt
error.txt

Expected behavior
When the hydrogen bond constraints are added to an RFD3 input file additional metrics should appear in the output JSON file (such as num_hbonds).

Additional context
Without setting verbose=True no warning messages are generated and the calculation of the hydrogen-bonding metrics fails silently.

Claude's summary of the issue:

Summary

When running RFD3 inference with select_hbond_donor/select_hbond_acceptor set in the input spec (and HBPLUS_PATH correctly configured), hydrogen-bond metrics are silently never produced in the output. With verbose logging enabled, the actual failure surfaces as:

WARNING rfd3.metrics.hbonds_hbplus_metrics: [rank: 0] Could not calculate hbond metrics: 'AtomArray' object has no attribute 'is_motif_atom'

Without verbose logging, this failure is completely silent — the run completes cleanly with no errors or warnings, and the output <example>_model_<idx>.json simply has no hbond-related keys (num_hbonds, correct_donor_percent, correct_acceptor_percent, etc.) in its metrics dict.

Root cause

calculate_hbonds() in src/rfd3/transforms/hbonds_hbplus.py:159-160,209-210 directly accesses atom_array.is_motif_atom as if it's a guaranteed annotation:

current_donor_is_motif = atom_array.is_motif_atom[current_donor_mask][0]
current_acceptor_is_motif = atom_array.is_motif_atom[current_acceptor_mask][0]

is_motif_atom is not part of the guaranteed atom-array schema. REQUIRED_CONDITIONING_ANNOTATION_VALUES (src/rfd3/constants.py:8-13) only guarantees:
- is_motif_atom_with_fixed_seq
- is_motif_atom_with_fixed_coord
- is_motif_atom_unindexed
- is_motif_atom_unindexed_motif_breakpoint

is_motif_atom is meant to be derived from the first three, on demand, via get_motif_features() (src/rfd3/transforms/conditioning_base.py:55-66):

is_motif_atom = is_fixed | is_sequence_fixed | is_unindexed

Worse, the inference input-parsing path explicitly deletes is_motif_atom if present, precisely because it's meant to always be recomputed rather than reused (src/rfd3/inference/input_parsing.py:904-910):

# Ensure motif annotations are removed
atom_array.del_annotation("is_motif_token") if "is_motif_token" in atom_array.get_annotation_categories() else None
atom_array.del_annotation("is_motif_atom") if "is_motif_atom" in atom_array.get_annotation_categories() else None

So by design, no atom array flowing through inference ever carries a real is_motif_atom annotation. hbonds_hbplus_metrics.py (the metrics entry point used at inference, calculate_hbond_stats → get_hbond_metrics) correctly recomputes it downstream via get_motif_features():

# src/rfd3/metrics/hbonds_hbplus_metrics.py:156
motif_mask = np.array(get_motif_features(output_atom_array)["is_motif_atom"])

...but calculate_hbonds() is called before that, inside calculate_hbond_stats (hbonds_hbplus_metrics.py:148-150), and it never recomputes the annotation — it assumes it already exists, so it raises AttributeError. This is caught by a blanket try/except in get_hbond_metrics() (hbonds_hbplus_metrics.py:268-270) and silently downgraded to a warning + empty {}, so the failure is invisible unless verbose/warning-level logging is enabled.

This bug does not affect the training path — during training, an earlier transform in the full conditioning pipeline happens to set is_motif_atom before CalculateHbondsPlus runs, masking the issue. It only manifests at inference time, where that upstream annotation is guaranteed absent.

Impact

Hydrogen-bond metrics (num_hbonds, correct_donor_percent, correct_acceptor_percent, donor_atom_names, acceptor_atom_names, hbond_connections) can never be computed during inference in the current codebase, regardless of correct HBPLUS installation/configuration or correct select_hbond_donor/select_hbond_acceptor input specification. The failure is silent by default (no warning at normal log levels — only visible with verbose mode), making it easy to believe hbond conditioning "isn't working" when it actually is; only the metrics reporting is broken.

Suggested fix

In calculate_hbonds() (src/rfd3/transforms/hbonds_hbplus.py), derive is_motif_atom the same way get_motif_features() does, rather than assuming the annotation exists — e.g. at the top of the function (or immediately before first use at line 159):

from rfd3.transforms.conditioning_base import get_motif_features
...
is_motif_atom = get_motif_features(atom_array)["is_motif_atom"]
...
current_donor_is_motif = is_motif_atom[current_donor_mask][0]
current_acceptor_is_motif = is_motif_atom[current_acceptor_mask][0]

and similarly at the two other direct-access sites in CalculateHbondsPlus.forward (lines 209-210), or by having calculate_hbonds() set the annotation on the array itself before use so downstream callers stay unchanged.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with calculate_hbonds() in src/rfd3/transforms/hbonds_hbplus.py and compare its motif handling with get_motif_features() in src/rfd3/transforms/conditioning_base.py. Reproduce the failure using the attached PDB, JSON, and HBPLUS setup, then verify that inference output metrics include values such as num_hbonds, correct_donor_percent, and correct_acceptor_percent.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
bioinformatics, machine-learning
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.