Output format reference#
Every design comes back as a CIF, with per-atom provenance (standard annotations that record
where each atom came from and how the model produced it) recorded in ordinary _atom_site
columns that any CIF reader picks up. This page covers the full output picture:
where a run’s files land on disk
how they’re named
what the metrics CSVs contain
the provenance annotation schema
For the metrics that consume these files, see the metrics reference; for the task-oriented refolding walkthrough, see the refolding guide; for the input-side conditioning annotations (what you told the model to build, rather than what it built) see the annotations reference; for using the provenance annotations to find a region again in a generated structure, see Reading outputs.
Contents
Where things land#
Every run writes under out_dir (./runs/<run_name_>/ when you leave it null).
rfd4-generate and rfd4-evaluate use different subdirectory names, but the same layout
underneath:
Subdirectory |
Written by |
Contains |
|---|---|---|
|
|
Designed CIFs, one subdirectory per input example |
|
|
|
|
|
Same per-example layout; the original input structure is symlinked in rather than copied |
|
|
|
Training-time validation (both the flow model and the autoencoder) writes the analogous
val_samples/ and val_metrics/ instead.
Depending on what you asked the run to produce, each example’s own subdirectory can hold:
the designed structure
a copy (or, for
rfd4-evaluate, a symlink) of the input structurea chain-of-interest-only CIF, when
save_chain_of_interest=truea
trajectories/folder, whengeneration.args.save_trajectory_everyis nonzeroone refolded CIF plus a confidence JSON per oracle call, when refolding metrics are on
FoldSeek diversity clustering additionally stages extracted single-chain CIFs under
foldseek_input_chains/{dataset_hash}/{task}/. These are deleted once clustering finishes unless
you pass --cleanup_chains=false to aggregate_metrics — they’re cheap to regenerate from the
chain-of-interest output, so there’s rarely a reason to keep them.
RFD4-Proteina does not write a separate run manifest. Hydra’s own config snapshot goes to
./hydra_outputs (or wherever hydra.run.dir points), not under out_dir. The closest thing to a
per-design manifest is per_example_metrics.csv itself: every row embeds the fully resolved
generation config as generation.* columns, alongside ckpt_path, ckpt_name, and seed.
File naming#
{example_id} is the input’s example key: the filename stem for a single annotated CIF input, or
the key assigned to each entry of a condition-spec file or directory.
File |
Pattern |
|---|---|
Designed structure |
|
Input sidecar |
|
Chain of interest |
|
Trajectory, all-atom |
|
Trajectory, CA only (default) |
|
Refold |
|
Refold confidences |
|
Per-job metrics |
|
Refolds are cached by this filename: re-running generation or evaluation into the same output
directory reuses whatever a previous run already left under a given save_id, instead of calling
the oracle again. That also means repointing a pre-oracle
transform at a
different structure needs a fresh output directory, or the stale refold gets reported under the new
name.
Compression#
rfd4-generate and rfd4-evaluate write plain .cif by default; training-time validation
defaults to .cif.zst (Zstandard level 3). compress_outputs=true/false overrides either
direction, applying uniformly to every structure file above — designed, input, chain-of-interest,
trajectory, and refolded CIFs all get the same suffix. Plain, gzip, and Zstd inputs are all readable
regardless of this setting.
Two exceptions always write plain CIF, regardless of compress_outputs: FoldSeek’s extracted
chain-of-interest inputs, and any CIF exported for sharing. skip_existing and resuming an
interrupted run preserve whatever encoding a file already has, so keep one encoding per output
directory rather than mixing them.
Metrics CSVs and aggregation#
Each row of per_example_metrics.csv is one design, with columns prefixed by the metric’s config
key — clash_metrics.frac_atoms_clashing_1p5A, oracle_ppi.min_ipae_coi, ppi.pass, and so on.
See the metrics reference for every column that ships.
Large runs split across SLURM array jobs each write their own per_example_metrics_job{N}.csv.
Concatenate them with:
python -m rfproteina.inference_utils.aggregate_metrics ./runs/my_run/inference_metrics/
This writes aggregated_metrics.csv and per_task_metrics.csv next to the per-job files, and
moves the originals into individual_job_results/. launch_generate.sh runs this automatically as
a dependent job.
Pass --cluster_with_foldseek to also cluster designs structurally: it adds a
foldseek_cluster_id column, reading the chain_of_interest chain the pipeline already identified
from conditioning. That needs save_chain_of_interest=true at generation time, since clustering
reads the extracted single-chain CIFs rather than the full design. See Evaluating
results for the full command set, including
--output_dir, --foldseek_threads, and the other aggregate_metrics flags.
Refold addresses#
A design can have more than one refold: each configured oracle produces one, and a
pre-oracle transform can fan a single oracle call out into several
named outputs (for example, folding the designed chain alone as apo, or against each of several
off-targets). Every refolding metric receives the design as ground_truth_atom_array and the refold
currently being scored as predicted_atom_array, plus access to every refold the design has. You
can name any of those refolds two ways:
Address |
Names the refold by |
Example |
|---|---|---|
|
what it is, either one output of a pre-oracle transform or the design folded as is, and only when exactly one refold is of that kind |
|
|
which oracle produced it |
|
The by-oracle form is always available, since oracle config keys are unique. The by-kind form is a
convenience alias that only exists when it is unambiguous: if two untransformed oracles run at once,
or the name would shadow an oracle’s own config key, there is no refold.untransformed at all, and no
is_refold: or exists_refold: tags for it either. Name the oracle instead, as in refold.oracle_ppi.
Running two untransformed oracles together therefore switches off every metric gated on
is_refold:untransformed, with a warning, rather than scoring each of them into one column and
reporting whichever pass happened to fire last.
A transform’s fan-out outputs get addressed and tagged the same way per output. An oracle with the
config key oracle_off_target and a two output transform reports oracle_off_target.her2.* and
oracle_off_target.egfr.* — as everywhere else, the prefix is the oracle’s config key, not its
oracle_name — and you get both refold.her2 / is_refold:her2 and refold.oracle_off_target.her2 /
is_refold:oracle_off_target.her2. A metric gated on is_refold:her2 scores that one output alone;
gate on the oracle’s own tag instead of an is_refold: address and the metric runs once per output,
repeating itself under every save_id. Config keyed by the bare output name, such as a
frac_under_thresholds entry or an aggregation list, applies to every output of the fan out.
Comparing two refolds#
Naming two addresses in a metric’s input_kwarg_names turns any StructureComparisonMetric into a
comparison between two refolds of the same design, rather than design against refold. That is all
the built in refolded_apo_holo_rmsd is — see
Comparing two refolds for the worked example.
Standard annotations, and why there is no output JSON#
Older workflows expected an output JSON next to the generated CIF, carrying the unindex mapping,
which input contig an inserted region came from, and which atoms were coordinate conditioned. That
JSON no longer exists. The same information now goes into standard annotations written directly
into the output CIF, defined in rfproteina.utils.standard_annotations. It round trips with the
structure itself instead of living in a side file that can drift out of sync.
These are distinct from the input conditioning annotations: those tell the model what to build, these record what it actually built and where each piece came from.
Standard annotation |
CIF column |
What it tells you |
|---|---|---|
|
|
Per atom source component contig label like |
|
|
Per atom source contig for the coordinate-conditioned region, where |
|
|
Original unindexed |
|
|
The ground truth coordinate to compare against for coordinate-conditioned atoms, which is what |
|
|
Per atom boolean, where |
Read any of these off a parsed structure the same way, for example SA_CRDSRC.mask(atom_array) or
SA_SRC.annotation(atom_array). They are exactly the fields the motif and unindexed RMSD
metrics already consume — that is the whole
point of turning on design_motif_allatom_rmsd and design_unindexed_motif_allatom_rmsd instead of
reading the CIF by hand; the pipeline does the bookkeeping for you. Generated CIFs already carry
saved conditioning targets onto the matched atoms, and chaining a refinement run should preserve
them — rebuilding them from the generated structure instead would change the design problem, since
the saved coordinate targets can differ from the generated coordinates.
For using these annotations to find a conditioned region or ligand again in a generated structure, including a worked selection-syntax example, see Reading outputs.
