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.
Describe the bug
When
select_hbond_donororselect_hbond_acceptorare 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=Trueno 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_acceptorset in the input spec (andHBPLUS_PATHcorrectly 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>.jsonsimply has no hbond-related keys (num_hbonds,correct_donor_percent,correct_acceptor_percent, etc.) in itsmetricsdict.Root cause
calculate_hbonds()insrc/rfd3/transforms/hbonds_hbplus.py:159-160,209-210directly accessesatom_array.is_motif_atomas if it's a guaranteed annotation: