mod calvados3;
mod kimhummer;
mod pasquier;
use crate::BeadType;
pub use calvados3::Calvados3;
pub use kimhummer::KimHummer;
pub use pasquier::Pasquier;
#[derive(Clone, Copy)]
pub struct BeadParams {
pub mass: f64,
pub sigma: f64,
pub epsilon: f64,
pub lambda: f64,
}
pub trait ForceField {
fn params(&self, res_name: &str, bead_type: BeadType) -> Option<BeadParams>;
fn format_atom_fields(&self, params: &BeadParams) -> String;
fn nonbonded_yaml(&self, types: &[(&str, f64, BeadParams)]) -> String;
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub enum HydrophobicScaling {
#[default]
NoScale,
ScaleLambda(f64),
ScaleEpsilon(f64),
}
impl std::str::FromStr for HydrophobicScaling {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(rest) = s.strip_prefix("lambda:") {
return rest
.parse::<f64>()
.map(Self::ScaleLambda)
.map_err(|e| format!("bad lambda value: {e}"));
}
if let Some(rest) = s.strip_prefix("epsilon:") {
return rest
.parse::<f64>()
.map(Self::ScaleEpsilon)
.map_err(|e| format!("bad epsilon value: {e}"));
}
Err(format!("expected 'lambda:<f>' or 'epsilon:<f>'; got '{s}'"))
}
}
impl std::fmt::Display for HydrophobicScaling {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HydrophobicScaling::NoScale => f.write_str("none"),
HydrophobicScaling::ScaleLambda(c) => write!(f, "lambda×{c}"),
HydrophobicScaling::ScaleEpsilon(c) => write!(f, "epsilon×{c}"),
}
}
}
pub fn from_name(
name: &str,
scaling: HydrophobicScaling,
) -> Result<Option<Box<dyn ForceField>>, String> {
match name {
"calvados3" => Ok(Some(Box::new(Calvados3::new(scaling)))),
"kimhummer" | "kh" => {
if matches!(scaling, HydrophobicScaling::ScaleLambda(_)) {
return Err(
"lambda scaling is not supported for Kim-Hummer (use epsilon:<factor>)"
.to_string(),
);
}
Ok(Some(Box::new(KimHummer::new(scaling))))
}
"pasquier" => {
if matches!(scaling, HydrophobicScaling::ScaleLambda(_)) {
return Err(
"lambda scaling is not supported for Pasquier (use epsilon:<factor>)"
.to_string(),
);
}
Ok(Some(Box::new(Pasquier::new(scaling))))
}
"none" => Ok(None),
_ => Err(format!(
"unknown force field model: '{name}' (available: calvados3, kimhummer/kh, pasquier, none)"
)),
}
}