use pdbtbx::{Atom, Residue};
use rust_sasa::options::{AtomLevel, SASAOptions};
use std::process;
#[derive(Debug)]
pub struct ExtendedAtom {
pub atom: Atom,
pub sasa: f64,
}
impl ExtendedAtom {
pub fn new(atom: Atom, sasa: f64) -> Self {
ExtendedAtom { atom, sasa }
}
}
pub struct ExtendedRes {
pub residue: Residue,
pub chain: String,
pub sasa_bb: f64,
pub sasa_sc: f64,
pub rel_sasa_bb: f64,
pub rel_sasa_sc: f64,
pub rel_sasa_total: f64,
}
impl ExtendedRes {
pub fn new(
residue: Residue,
chain: String,
sasa_bb: f64,
sasa_sc: f64,
rel_sasa_bb: f64,
rel_sasa_sc: f64,
rel_sasa_total: f64,
) -> Self {
ExtendedRes {
residue,
chain,
sasa_bb,
sasa_sc,
rel_sasa_bb,
rel_sasa_sc,
rel_sasa_total,
}
}
}
pub fn calculate_sasa(mut pdbtbx_struct: pdbtbx::PDB) -> Vec<ExtendedRes> {
let options = SASAOptions::<AtomLevel>::new()
.with_probe_radius(1.4)
.with_n_points(100);
let atom_sasa = options
.process(&pdbtbx_struct)
.unwrap()
.into_iter()
.map(f64::from)
.collect::<Vec<f64>>();
let mut extended_atoms: Vec<ExtendedAtom> = Vec::new();
for (atom, sasa) in pdbtbx_struct.atoms_mut().zip(atom_sasa.iter()) {
let _ = atom.set_b_factor(*sasa);
let extended_atom = ExtendedAtom::new(atom.clone(), *sasa);
extended_atoms.push(extended_atom);
}
let mut extended_residues: Vec<ExtendedRes> = Vec::new();
for chain in pdbtbx_struct.chains() {
for residue in chain.residues() {
let mut sasa_bb = 0.0;
let mut sasa_sc = 0.0;
for atom in residue.atoms() {
let extended_atom = extended_atoms.iter().find(|&x| x.atom == *atom).unwrap();
if atom.is_backbone() {
sasa_bb += extended_atom.sasa;
} else {
sasa_sc += extended_atom.sasa;
}
}
let extended_residue = ExtendedRes::new(
residue.clone(),
chain.id().to_string(),
sasa_bb,
sasa_sc,
0.0,
0.0,
0.0,
);
extended_residues.push(extended_residue);
}
}
for (e_res, _res) in extended_residues
.iter_mut()
.zip(pdbtbx_struct.residues_mut())
{
let resname = e_res.residue.name().unwrap();
match REL_ASA.iter().find(|(name, _)| *name == resname) {
Some(rel_asa) => {
let rel_sasa_bb = (e_res.sasa_bb / rel_asa.1.bb) * 100_f64;
let rel_sasa_sc = (e_res.sasa_sc / rel_asa.1.sc) * 100_f64;
let rel_total = (e_res.sasa_bb + e_res.sasa_sc / rel_asa.1.total) * 100_f64;
e_res.rel_sasa_bb = rel_sasa_bb;
e_res.rel_sasa_sc = rel_sasa_sc;
e_res.rel_sasa_total = rel_total;
}
None => {
eprintln!("\n### ERROR CALCULATING SASA ###");
eprintln!(
"# No relative accessibility found for residue `{}`",
resname
);
process::exit(1);
}
}
}
extended_residues
}
pub struct AsaValues {
pub total: f64,
pub bb: f64,
pub sc: f64,
}
pub const REL_ASA: &[(&str, AsaValues)] = &[
(
"ALA",
AsaValues {
total: 107.95,
bb: 38.54,
sc: 69.41,
},
),
(
"CYS",
AsaValues {
total: 134.28,
bb: 37.53,
sc: 96.75,
},
),
(
"ASP",
AsaValues {
total: 140.39,
bb: 37.70,
sc: 102.69,
},
),
(
"GLU",
AsaValues {
total: 172.25,
bb: 37.51,
sc: 134.74,
},
),
(
"PHE",
AsaValues {
total: 199.48,
bb: 35.37,
sc: 164.11,
},
),
(
"GLY",
AsaValues {
total: 80.10,
bb: 47.77,
sc: 32.33,
},
),
(
"HIS",
AsaValues {
total: 182.88,
bb: 35.80,
sc: 147.08,
},
),
(
"ILE",
AsaValues {
total: 175.12,
bb: 37.16,
sc: 137.96,
},
),
(
"LYS",
AsaValues {
total: 200.81,
bb: 37.51,
sc: 163.30,
},
),
(
"LEU",
AsaValues {
total: 178.63,
bb: 37.51,
sc: 141.12,
},
),
(
"MET",
AsaValues {
total: 194.15,
bb: 37.51,
sc: 156.64,
},
),
(
"ASN",
AsaValues {
total: 143.94,
bb: 37.70,
sc: 106.24,
},
),
(
"PRO",
AsaValues {
total: 136.13,
bb: 16.23,
sc: 119.90,
},
),
(
"GLN",
AsaValues {
total: 178.50,
bb: 37.51,
sc: 140.99,
},
),
(
"ARG",
AsaValues {
total: 238.76,
bb: 37.51,
sc: 201.25,
},
),
(
"SER",
AsaValues {
total: 116.50,
bb: 38.40,
sc: 78.11,
},
),
(
"THR",
AsaValues {
total: 139.27,
bb: 37.57,
sc: 101.70,
},
),
(
"VAL",
AsaValues {
total: 151.44,
bb: 37.16,
sc: 114.28,
},
),
(
"TRP",
AsaValues {
total: 249.36,
bb: 38.10,
sc: 211.26,
},
),
(
"TYR",
AsaValues {
total: 212.76,
bb: 35.38,
sc: 177.38,
},
),
(
"ASH",
AsaValues {
total: 140.39,
bb: 37.70,
sc: 102.69,
},
),
(
"DDZ",
AsaValues {
total: 107.95,
bb: 38.54,
sc: 69.41,
},
),
(
"GLH",
AsaValues {
total: 172.25,
bb: 37.51,
sc: 134.74,
},
),
(
"CYM",
AsaValues {
total: 134.28,
bb: 37.53,
sc: 96.75,
},
),
(
"CSP",
AsaValues {
total: 134.28,
bb: 37.53,
sc: 96.75,
},
),
(
"CYF",
AsaValues {
total: 134.28,
bb: 37.53,
sc: 96.75,
},
),
(
"CYC",
AsaValues {
total: 134.28,
bb: 37.53,
sc: 96.75,
},
),
(
"CFE",
AsaValues {
total: 134.28,
bb: 37.53,
sc: 96.75,
},
),
(
"NEP",
AsaValues {
total: 182.88,
bb: 35.80,
sc: 147.08,
},
),
(
"ALY",
AsaValues {
total: 200.81,
bb: 37.51,
sc: 163.30,
},
),
(
"MLZ",
AsaValues {
total: 200.81,
bb: 37.51,
sc: 163.30,
},
),
(
"MLY",
AsaValues {
total: 200.81,
bb: 37.51,
sc: 163.30,
},
),
(
"M3L",
AsaValues {
total: 200.81,
bb: 37.51,
sc: 163.30,
},
),
(
"HYP",
AsaValues {
total: 136.13,
bb: 16.23,
sc: 119.90,
},
),
(
"SEP",
AsaValues {
total: 116.50,
bb: 38.40,
sc: 78.11,
},
),
(
"TOP",
AsaValues {
total: 139.27,
bb: 37.57,
sc: 101.70,
},
),
(
"TYP",
AsaValues {
total: 212.76,
bb: 35.38,
sc: 177.38,
},
),
(
"PTR",
AsaValues {
total: 212.76,
bb: 35.38,
sc: 177.38,
},
),
(
"TYS",
AsaValues {
total: 212.76,
bb: 35.38,
sc: 177.38,
},
),
(
"PNS",
AsaValues {
total: 116.50,
bb: 38.40,
sc: 78.11,
},
),
(
"QSR",
AsaValues {
total: 390.53,
bb: 26.64,
sc: 363.88,
},
),
];
#[cfg(test)]
mod test {
use crate::core::structure;
use super::*;
#[test]
fn test_calculate_sasa() {
let pdb = structure::load_pdb("tests/data/complex.pdb").unwrap();
let extended_residues = calculate_sasa(pdb);
assert_eq!(extended_residues.len(), 116);
}
}