Skip to content
This repository was archived by the owner on Mar 10, 2026. It is now read-only.

Commit e87f5be

Browse files
committed
feat(core): Exclude disulfide-bonded cysteines from optimization in placement workflow
1 parent 6951a12 commit e87f5be

1 file changed

Lines changed: 30 additions & 5 deletions

File tree

  • crates/scream-core/src/workflows

crates/scream-core/src/workflows/place.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::engine::utils::query;
1919
use rand::prelude::IteratorRandom;
2020
use rand::{Rng, seq::SliceRandom, thread_rng};
2121
use std::collections::{HashMap, HashSet};
22-
use tracing::{info, instrument};
22+
use tracing::{debug, info, instrument};
2323

2424
const CLASH_THRESHOLD_KCAL_MOL: f64 = 25.0;
2525

@@ -42,7 +42,7 @@ pub struct PlacementResult {
4242
/// into a set of optimized, low-energy solutions.
4343
///
4444
/// The workflow follows the multi-stage algorithm described in the original SCREAM paper:
45-
/// 1. **Preparation**: Loads all necessary resources (forcefield, rotamers) and identifies active residues.
45+
/// 1. **Preparation**: Loads all necessary resources (forcefield, rotamers), identifies active residues, and excludes disulfide-bonded cysteines from optimization.
4646
/// 2. **Pre-computation**: Calculates the Empty Lattice (EL) energy for every possible rotamer of every active residue.
4747
/// 3. **Initialization**: Creates an initial "ground state" conformation by selecting the lowest EL-energy rotamer for each residue.
4848
/// 4. **Clash Resolution**: Iteratively identifies and resolves the worst energetic clashes using doublet optimization.
@@ -196,7 +196,8 @@ pub fn run(
196196
/// Prepares the optimization context by resolving active residues from the configuration.
197197
///
198198
/// This function determines which residues will be optimized based on the user's selection
199-
/// criteria and ensures the rotamer library contains appropriate conformations.
199+
/// criteria, ensures the rotamer library contains appropriate conformations, and automatically
200+
/// excludes any cysteine residues that are part of disulfide bonds to prevent invalid structures.
200201
///
201202
/// # Arguments
202203
///
@@ -206,7 +207,7 @@ pub fn run(
206207
///
207208
/// # Return
208209
///
209-
/// A set of residue IDs that will be optimized.
210+
/// A set of residue IDs that will be optimized, with disulfide-bonded cysteines excluded.
210211
///
211212
/// # Errors
212213
///
@@ -216,11 +217,35 @@ fn prepare_context(
216217
config: &PlacementConfig,
217218
rotamer_library: &mut RotamerLibrary,
218219
) -> Result<HashSet<ResidueId>, EngineError> {
219-
let active_residues = query::resolve_selection_to_ids(
220+
let mut active_residues = query::resolve_selection_to_ids(
220221
initial_system,
221222
&config.residues_to_optimize,
222223
rotamer_library,
223224
)?;
225+
226+
info!("Detecting disulfide bonds to exclude from optimization.");
227+
let disulfide_bonded_residues = initial_system.find_disulfide_bonded_residues();
228+
229+
if !disulfide_bonded_residues.is_empty() {
230+
let original_count = active_residues.len();
231+
232+
active_residues.retain(|res_id| !disulfide_bonded_residues.contains(res_id));
233+
234+
let final_count = active_residues.len();
235+
236+
if original_count > final_count {
237+
let excluded_count = original_count - final_count;
238+
info!(
239+
"Excluded {} residue(s) from optimization because they are part of disulfide bonds.",
240+
excluded_count
241+
);
242+
debug!(
243+
"Disulfide-bonded residues excluded: {:?}",
244+
disulfide_bonded_residues
245+
);
246+
}
247+
}
248+
224249
Ok(active_residues)
225250
}
226251

0 commit comments

Comments
 (0)