Skip to content

diverse feat, perf and fixes - #80

Merged
fmoorhof merged 6 commits into
mainfrom
div-fixes
Jul 15, 2026
Merged

fmoorhof merged 6 commits into
mainfrom
div-fixes

Conversation

@fmoorhof

@fmoorhof fmoorhof commented Jul 15, 2026

Copy link
Copy Markdown
Owner

@soucery-ai summary

Summary by Sourcery

Support downloading PDB structures from both AlphaFoldDB and ESM Metagenomic Atlas and wire AlphaFold cross-references into the UI and config, while updating prediction logic to be device-aware and more robust to existing columns.

New Features:

  • Add automatic database selection for PDB downloads based on FASTA identifiers, supporting AlphaFoldDB and ESM Metagenomic Atlas.
  • Expose AlphaFoldDB cross-reference URLs in the selection UI when available, falling back to UniProt-based AlphaFold URLs.
  • Extend solubility prediction to allow configurable ensemble size via a pred_frequency parameter.

Enhancements:

  • Relax FASTA header parsing to support multiple identifier formats beyond UniProt IDs.
  • Make CLEAN EC prediction and NetSolP solubility/usability prediction conditional on column presence rather than null checks, with clearer logging when predictions are skipped.
  • Use the available CUDA device when running CLEAN EC inference and pass the device consistently through the EC prediction pipeline.
  • Adjust NetSolP model path to an absolute scratch location and wrap solubility prediction in a run_time decorator for timing.
  • Update test configuration to include xref_alphafolddb in the dataframe columns of interest.

fmoorhof added 6 commits July 2, 2026 10:26
error message: 'DataFrame' object has no attribute 'CLEAN_EC_pred'
revert: tqdm progress bars as they are only usable for batches ()pred_frequency). This is little helpful so progress bars were added to my NetSolP fork instead
chore: change regex to not only extract id before '| 'but also blank ' '
@fmoorhof fmoorhof added this to the core milestone Jul 15, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors PDB download logic to support both AlphaFoldDB and ESM Metagenomic Atlas IDs, tightens prediction triggering conditions, adds GPU awareness and timing, makes NetSolP ensemble size configurable, propagates device usage through CLEAN EC prediction, and surfaces AlphaFold URLs in the UI/config.

Sequence diagram for dual-source PDB download routing

sequenceDiagram
    actor User
    participant Script_get_pdb_af_from_fasta as script
    participant AlphaFoldAPI as alphafold
    participant ESMAPI as esm

    User->>script: run get_pdb_af_from_fasta.py
    script->>script: extract_ids(FASTA_FILE)
    loop for each identifier
        script->>script: download_pdb(identifier)
        alt identifier starts with MGYP
            script->>script: download_from_esm(identifier)
            script->>esm: requests.get(ESM_API.format(identifier))
            esm-->>script: PDB text
            script->>script: write .pdb file
        else other identifier
            script->>alphafold: requests.get(ALPHAFOLD_API.format(identifier))
            alphafold-->>script: JSON with pdbUrl
            script->>alphafold: requests.get(pdbUrl, stream=True)
            alphafold-->>script: PDB stream
            script->>script: write .pdb file
        end
    end
Loading

Sequence diagram for prediction pipeline with device and column checks

sequenceDiagram
    participant Predict_run_predictions as run_predictions
    participant CLEAN_run as run_clean_inference_with_embeddings
    participant NetSolP_run as get_preds
    participant Torch as torch_device

    run_predictions->>Torch: torch.device("cuda" if torch.cuda.is_available() else "cpu")
    Torch-->>run_predictions: device

    run_predictions->>run_predictions: check CLEAN_EC_pred, CLEAN_probability columns
    alt CLEAN EC not present and config['predictions']['ec'] == True and plm_model == esm1b
        run_predictions->>CLEAN_run: run_clean_inference_with_embeddings(..., device=device)
        CLEAN_run->>CLEAN_run: torch.load(emb_train_path, map_location=device)
        CLEAN_run->>CLEAN_run: CLEAN_model(esm_emb_inference.to(device))
        CLEAN_run-->>run_predictions: df_clean
        run_predictions->>run_predictions: merge CLEAN_EC_pred, CLEAN_probability
    else skip CLEAN
        run_predictions->>run_predictions: logging.info("CLEAN predictions ... Skipping prediction.")
    end

    run_predictions->>run_predictions: check netSolP_solubility, netSolP_usability columns
    alt solubility prediction enabled and NetSolP columns not present
        run_predictions->>NetSolP_run: get_preds(df, args, pred_frequency=2)
        NetSolP_run->>NetSolP_run: get_preds_split(..., "Solubility")
        NetSolP_run->>NetSolP_run: get_preds_split(..., "Usability")
        NetSolP_run-->>run_predictions: df with NetSolP columns
    else skip NetSolP
        run_predictions->>run_predictions: logging.info("NetSolP predictions already exist... Skipping prediction.")
    end

    run_predictions-->>run_predictions: return df
Loading

File-Level Changes

Change Details Files
Refactor FASTA ID extraction and PDB download pipeline to support multiple identifier types and both AlphaFoldDB and ESM Metagenomic Atlas.
  • Split imports and configuration, introducing separate AlphaFold and ESM API constants.
  • Generalize FASTA header parsing to extract any identifier before the first pipe or whitespace.
  • Add dedicated download functions for AlphaFoldDB and ESM, including existence checks, error handling, and streaming writes.
  • Introduce a dispatcher that routes MGYP* IDs to ESM and all other IDs to AlphaFoldDB, with unified error reporting.
  • Update the main script to use the new identifier extractor and dispatcher, and adjust log messages accordingly.
scripts/get_pdb_af_from_fasta.py
Tighten conditions under which CLEAN EC and NetSolP solubility/usability predictions are executed, add device selection for torch, and improve logging.
  • Add a torch-based device selection (CUDA vs CPU) at module import time for predictions.
  • Change CLEAN prediction trigger to check for missing columns rather than all-null values.
  • Pass the resolved torch device into CLEAN inference instead of a hard-coded CPU string, and add logging when CLEAN predictions are skipped.
  • Change NetSolP prediction trigger to require config flag and absence of result columns, and add logging when predictions are skipped because columns exist.
  • Update NetSolP model path to a scratch directory and wrap solubility prediction in a timing decorator.
selectzyme/backend/predict.py
selectzyme/backend/predict_solubility.py
Make NetSolP ensemble prediction frequency configurable and reuse the parameter for averaging.
  • Decorate the solubility prediction function with a run_time utility to log duration.
  • Add a pred_frequency parameter with default 2 to control number of splits evaluated.
  • Replace hard-coded loop over 5 splits with a loop over pred_frequency for both solubility and usability predictions.
  • Compute average predictions by dividing by pred_frequency instead of a fixed constant.
selectzyme/backend/predict_solubility.py
Propagate device configuration through CLEAN EC prediction inference and training embedding loading.
  • Stop forcing model inference embeddings back to CPU immediately after applying the model.
  • Pass the device parameter through to the distance map computation function instead of a hard-coded CPU device.
  • Load training embeddings with torch.load using the configurable map_location device.
  • Relax the type annotation on the device parameter to allow passing a torch.device rather than just a string.
selectzyme/backend/predict_ec.py
Expose AlphaFold URLs in UI callbacks and extend test configuration to include AlphaFold DB cross-reference.
  • When a row is selected in the UI, set an AlphaFold URL based on xref_alphafolddb when available and xref_brenda is known, otherwise construct a UniProt-based AlphaFold entry URL.
  • Add xref_alphafolddb into the default dataframe columns of interest in the test config, repositioning df_coi to include AlphaFold and BRENDA cross-reference fields.
  • Ensure the selection logic marks the row as selected before adding URLs.
selectzyme/pages/callbacks.py
results/input_configs/test_config.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The new checks in run_predictions for CLEAN and NetSolP ("..." not in df.columns) change behavior from 'all null' to 'column missing'; if partially-null columns are expected you may want to keep the null-based logic or explicitly handle mixed/null cases.
  • In _process_selection, the Alphafold URL logic can raise a KeyError when xref_brenda or xref_alphafolddb is missing and writes to two different columns depending on conditions; consider guarding column access and consistently using a single output field for the Alphafold URL.
  • The hard-coded absolute MODELS_PATH in predict_solubility.py reduces portability compared to the previous relative path; consider deriving this from configuration or environment variables instead of a scratch-specific path.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new checks in `run_predictions` for CLEAN and NetSolP (`"..." not in df.columns`) change behavior from 'all null' to 'column missing'; if partially-null columns are expected you may want to keep the null-based logic or explicitly handle mixed/null cases.
- In `_process_selection`, the Alphafold URL logic can raise a KeyError when `xref_brenda` or `xref_alphafolddb` is missing and writes to two different columns depending on conditions; consider guarding column access and consistently using a single output field for the Alphafold URL.
- The hard-coded absolute `MODELS_PATH` in `predict_solubility.py` reduces portability compared to the previous relative path; consider deriving this from configuration or environment variables instead of a scratch-specific path.

## Individual Comments

### Comment 1
<location path="selectzyme/backend/predict_solubility.py" line_range="15-17" />
<code_context>
-def get_preds(df, args):
+
+@run_time
+def get_preds(df, args, pred_frequency=2):
     alphabet_path = os.path.join(args.MODELS_PATH, "ESM1b_alphabet.pkl")  # args.MODELS_PATH

</code_context>
<issue_to_address>
**suggestion:** Using `pred_frequency` in place of fixed 5 splits needs validation and guarding against invalid values.

This change adds useful flexibility, but `pred_frequency` should be validated to avoid runtime issues (e.g., division-by-zero or shape mismatches when 0/negative or not aligned with the number of model splits). Please add a guard (e.g., `assert pred_frequency > 0`) and document the expected range/relationship to the model directory splits to prevent confusing errors or silent quality regressions.

```suggestion
@run_time
def get_preds(df, args, pred_frequency=2):
    """
    Run solubility predictions.

    Parameters
    ----------
    df : pandas.DataFrame
        Input sequences / metadata for prediction.
    args : Namespace
        Configuration namespace. Must provide MODELS_PATH.
    pred_frequency : int, optional
        Positive integer controlling how often predictions are made per input batch.
        Must be > 0 and should be consistent with the number of model splits
        configured in the models directory to avoid shape mismatches or
        misaligned prediction batches.
    """
    if not isinstance(pred_frequency, int):
        raise TypeError(
            f"pred_frequency must be an integer, got {type(pred_frequency).__name__}"
        )
    if pred_frequency <= 0:
        raise ValueError(
            "pred_frequency must be a positive integer greater than 0 to avoid "
            "division-by-zero and invalid batching."
        )

    alphabet_path = os.path.join(args.MODELS_PATH, "ESM1b_alphabet.pkl")  # args.MODELS_PATH
```
</issue_to_address>

### Comment 2
<location path="selectzyme/backend/predict_ec.py" line_range="98" />
<code_context>
+        model_emb_inference  = CLEAN_model(esm_emb_inference.to(device))
     inference_dist = get_dist_map_test(
-        emb_train, model_emb_inference, ec_id_dict_train, id_ec_inference_dummy, "cpu", torch.float32)
+        emb_train, model_emb_inference, ec_id_dict_train, id_ec_inference_dummy, device, torch.float32)
     inference_df = pd.DataFrame.from_dict(inference_dist)
     max_sep_predictions_dict = get_max_sep_predictions_dict(inference_df, gmm_path)
</code_context>
<issue_to_address>
**issue (bug_risk):** The `device` argument here appears undefined in this scope, which will raise at runtime.

Within `clean_max_sep_predictions`, `device` is neither defined nor passed, yet it’s used for `.to(device)` and forwarded to `get_dist_map_test`, which will cause a `NameError` at runtime. Please either add `device` as a parameter to `clean_max_sep_predictions` or infer it from existing inputs (e.g. `model_emb_inference.device`), and confirm the type matches what `get_dist_map_test` expects (string vs `torch.device`).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +15 to 17
@run_time
def get_preds(df, args, pred_frequency=2):
alphabet_path = os.path.join(args.MODELS_PATH, "ESM1b_alphabet.pkl") # args.MODELS_PATH

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Using pred_frequency in place of fixed 5 splits needs validation and guarding against invalid values.

This change adds useful flexibility, but pred_frequency should be validated to avoid runtime issues (e.g., division-by-zero or shape mismatches when 0/negative or not aligned with the number of model splits). Please add a guard (e.g., assert pred_frequency > 0) and document the expected range/relationship to the model directory splits to prevent confusing errors or silent quality regressions.

Suggested change
@run_time
def get_preds(df, args, pred_frequency=2):
alphabet_path = os.path.join(args.MODELS_PATH, "ESM1b_alphabet.pkl") # args.MODELS_PATH
@run_time
def get_preds(df, args, pred_frequency=2):
"""
Run solubility predictions.
Parameters
----------
df : pandas.DataFrame
Input sequences / metadata for prediction.
args : Namespace
Configuration namespace. Must provide MODELS_PATH.
pred_frequency : int, optional
Positive integer controlling how often predictions are made per input batch.
Must be > 0 and should be consistent with the number of model splits
configured in the models directory to avoid shape mismatches or
misaligned prediction batches.
"""
if not isinstance(pred_frequency, int):
raise TypeError(
f"pred_frequency must be an integer, got {type(pred_frequency).__name__}"
)
if pred_frequency <= 0:
raise ValueError(
"pred_frequency must be a positive integer greater than 0 to avoid "
"division-by-zero and invalid batching."
)
alphabet_path = os.path.join(args.MODELS_PATH, "ESM1b_alphabet.pkl") # args.MODELS_PATH

model_emb_inference = CLEAN_model(esm_emb_inference.to(device))
inference_dist = get_dist_map_test(
emb_train, model_emb_inference, ec_id_dict_train, id_ec_inference_dummy, "cpu", torch.float32)
emb_train, model_emb_inference, ec_id_dict_train, id_ec_inference_dummy, device, torch.float32)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The device argument here appears undefined in this scope, which will raise at runtime.

Within clean_max_sep_predictions, device is neither defined nor passed, yet it’s used for .to(device) and forwarded to get_dist_map_test, which will cause a NameError at runtime. Please either add device as a parameter to clean_max_sep_predictions or infer it from existing inputs (e.g. model_emb_inference.device), and confirm the type matches what get_dist_map_test expects (string vs torch.device).

@fmoorhof fmoorhof changed the title Div fixes diverse feat, perf and fixes Jul 15, 2026
@fmoorhof
fmoorhof merged commit d40901d into main Jul 15, 2026
3 checks passed
@fmoorhof
fmoorhof deleted the div-fixes branch July 15, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant