use std::path::PathBuf;
use clap::{Args, Parser, Subcommand, ValueEnum};
#[derive(Parser)]
#[command(
name = "dforge",
about = "DREIDING force field parameterization",
version,
author,
before_help = crate::display::banner_for_help(),
propagate_version = true
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
#[command(visible_alias = "b")]
Bio(BioArgs),
#[command(visible_alias = "c")]
Chem(ChemArgs),
}
#[derive(Args)]
pub struct IoOptions {
#[arg(short, long, value_name = "FILE")]
pub input: Option<PathBuf>,
#[arg(short, long, value_name = "FILE", action = clap::ArgAction::Append)]
pub output: Vec<PathBuf>,
#[arg(short, long)]
pub quiet: bool,
}
#[derive(Args)]
#[command(next_help_heading = "Charge Calculation")]
pub struct ChargeOptions {
#[arg(long = "charge", value_name = "METHOD", default_value = "none")]
pub method: ChargeMethod,
#[arg(
long = "total-charge",
value_name = "Q",
default_value = "0.0",
allow_hyphen_values = true
)]
pub total_charge: f64,
}
#[derive(Args)]
#[command(next_help_heading = "Hybrid Charge Options")]
pub struct HybridChargeOptions {
#[arg(
long = "protein-scheme",
value_name = "SCHEME",
default_value = "amber-ffsb"
)]
pub protein_scheme: ProteinScheme,
#[arg(
long = "nucleic-scheme",
value_name = "SCHEME",
default_value = "amber"
)]
pub nucleic_scheme: NucleicScheme,
#[arg(long = "water-scheme", value_name = "SCHEME", default_value = "tip3p")]
pub water_scheme: WaterScheme,
#[arg(long = "ligand", value_name = "CONFIG", action = clap::ArgAction::Append)]
pub ligands: Vec<String>,
#[arg(
long = "default-ligand-method",
value_name = "METHOD",
default_value = "embedded"
)]
pub default_ligand_method: LigandQeqMethod,
#[arg(
long = "default-ligand-cutoff",
value_name = "Å",
default_value = "10.0"
)]
pub default_ligand_cutoff: f64,
}
#[derive(Args)]
#[command(next_help_heading = "QEq Solver Options")]
pub struct QeqSolverOptions {
#[arg(long = "qeq-tolerance", value_name = "TOL", default_value = "1e-6")]
pub tolerance: f64,
#[arg(long = "qeq-max-iter", value_name = "N", default_value = "100")]
pub max_iterations: u32,
#[arg(long = "qeq-lambda", value_name = "λ", default_value = "0.5")]
pub lambda_scale: f64,
#[arg(long = "qeq-hydrogen-scf", value_name = "BOOL", default_value = "true")]
pub hydrogen_scf: bool,
#[arg(long = "qeq-basis", value_name = "TYPE", default_value = "sto")]
pub basis_type: BasisType,
#[arg(long = "qeq-damping", value_name = "STRATEGY", default_value = "auto")]
pub damping: DampingStrategy,
}
#[derive(Args)]
#[command(next_help_heading = "Potential Functions")]
pub struct PotentialOptions {
#[arg(long, value_name = "TYPE", default_value = "harmonic")]
pub bond_potential: BondPotential,
#[arg(long, value_name = "TYPE", default_value = "cosine")]
pub angle_potential: AnglePotential,
#[arg(long, value_name = "TYPE", default_value = "lj")]
pub vdw_potential: VdwPotential,
#[arg(long, value_name = "FILE")]
pub rules: Option<PathBuf>,
#[arg(long, value_name = "FILE")]
pub params: Option<PathBuf>,
}
#[derive(Args)]
pub struct BioArgs {
#[command(flatten)]
pub io: IoOptions,
#[arg(long = "infmt", value_name = "FORMAT")]
pub input_format: Option<BioInputFormat>,
#[arg(long = "outfmt", value_name = "FORMAT")]
pub output_format: Option<BioOutputFormat>,
#[command(flatten)]
pub clean: CleanOptions,
#[command(flatten)]
pub protonation: ProtonationOptions,
#[command(flatten)]
pub solvation: SolvationOptions,
#[command(flatten)]
pub topology: TopologyOptions,
#[command(flatten)]
pub charge: ChargeOptions,
#[command(flatten)]
pub hybrid: HybridChargeOptions,
#[command(flatten)]
pub qeq: QeqSolverOptions,
#[command(flatten)]
pub potential: PotentialOptions,
}
#[derive(Args)]
#[command(next_help_heading = "Structure Cleaning")]
pub struct CleanOptions {
#[arg(long)]
pub no_water: bool,
#[arg(long)]
pub no_ions: bool,
#[arg(long)]
pub no_hydrogens: bool,
#[arg(long)]
pub no_hetero: bool,
#[arg(long, value_name = "RES", value_delimiter = ',')]
pub remove: Vec<String>,
#[arg(long, value_name = "RES", value_delimiter = ',')]
pub keep: Vec<String>,
}
#[derive(Args)]
#[command(next_help_heading = "Protonation")]
pub struct ProtonationOptions {
#[arg(long, value_name = "PH")]
pub ph: Option<f64>,
#[arg(long, value_name = "STRATEGY", default_value = "network")]
pub his: HisStrategy,
#[arg(long)]
pub no_his_salt_bridge: bool,
}
#[derive(Args)]
#[command(next_help_heading = "Solvation")]
pub struct SolvationOptions {
#[arg(long)]
pub solvate: bool,
#[arg(long = "solv-margin", value_name = "Å", default_value = "10.0")]
pub box_margin: f64,
#[arg(long = "solv-spacing", value_name = "Å", default_value = "3.1")]
pub spacing: f64,
#[arg(long = "solv-cutoff", value_name = "Å", default_value = "2.4")]
pub vdw_cutoff: f64,
#[arg(long = "solv-cation", value_name = "ION", default_value = "na")]
pub cation: Cation,
#[arg(long = "solv-anion", value_name = "ION", default_value = "cl")]
pub anion: Anion,
#[arg(
long = "solv-charge",
value_name = "Q",
default_value = "0",
allow_hyphen_values = true
)]
pub target_charge: i32,
#[arg(long = "solv-seed", value_name = "SEED")]
pub seed: Option<u64>,
}
#[derive(Args)]
#[command(next_help_heading = "Topology")]
pub struct TopologyOptions {
#[arg(long = "ss-cutoff", value_name = "Å", default_value = "2.2")]
pub ss_cutoff: f64,
#[arg(long = "template", value_name = "FILE", action = clap::ArgAction::Append)]
pub templates: Vec<PathBuf>,
}
#[derive(Args)]
pub struct ChemArgs {
#[command(flatten)]
pub io: IoOptions,
#[arg(long = "infmt", value_name = "FORMAT")]
pub input_format: Option<ChemInputFormat>,
#[arg(long = "outfmt", value_name = "FORMAT")]
pub output_format: Option<ChemOutputFormat>,
#[command(flatten)]
pub charge: ChargeOptions,
#[command(flatten)]
pub qeq: QeqSolverOptions,
#[command(flatten)]
pub potential: PotentialOptions,
}
#[derive(Clone, Copy, ValueEnum)]
pub enum BioInputFormat {
Pdb,
Mmcif,
}
#[derive(Clone, Copy, ValueEnum)]
pub enum ChemInputFormat {
Mol2,
Sdf,
}
#[derive(Clone, Copy, ValueEnum)]
pub enum BioOutputFormat {
Bgf,
Pdb,
Mmcif,
Mol2,
Sdf,
}
#[derive(Clone, Copy, ValueEnum)]
pub enum ChemOutputFormat {
Mol2,
Sdf,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum ChargeMethod {
#[default]
None,
Qeq,
Hybrid,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum ProteinScheme {
#[default]
#[value(name = "amber-ffsb", alias = "ffsb")]
AmberFfsb,
#[value(name = "amber-ff03", alias = "ff03")]
AmberFf03,
Charmm,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum NucleicScheme {
#[default]
Amber,
Charmm,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum WaterScheme {
#[default]
Tip3p,
#[value(name = "tip3p-fb")]
Tip3pFb,
Spc,
#[value(name = "spc-e")]
SpcE,
Opc3,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum LigandQeqMethod {
Vacuum,
#[default]
Embedded,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum BasisType {
Gto,
#[default]
Sto,
}
#[derive(Clone, Debug)]
pub enum DampingStrategy {
None,
Fixed(f64),
Auto {
initial: f64,
},
}
impl Default for DampingStrategy {
fn default() -> Self {
Self::Auto { initial: 0.5 }
}
}
impl std::str::FromStr for DampingStrategy {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
if s == "none" {
Ok(Self::None)
} else if s == "auto" {
Ok(Self::Auto { initial: 0.5 })
} else if let Some(val) = s.strip_prefix("auto:") {
let initial = val
.parse::<f64>()
.map_err(|_| format!("invalid auto damping value: {}", val))?;
if initial <= 0.0 || initial > 1.0 {
return Err("auto damping initial must be in (0, 1]".into());
}
Ok(Self::Auto { initial })
} else if let Some(val) = s.strip_prefix("fixed:") {
let factor = val
.parse::<f64>()
.map_err(|_| format!("invalid fixed damping value: {}", val))?;
if factor <= 0.0 || factor > 1.0 {
return Err("fixed damping must be in (0, 1]".into());
}
Ok(Self::Fixed(factor))
} else {
Err(format!(
"unknown damping strategy: '{}' (use none, auto, auto:<f>, or fixed:<f>)",
s
))
}
}
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum BondPotential {
#[default]
Harmonic,
Morse,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum AnglePotential {
#[default]
Cosine,
Theta,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum VdwPotential {
#[default]
#[value(alias = "lennard-jones")]
Lj,
#[value(alias = "buckingham")]
Exp6,
}
#[derive(Clone, Copy, ValueEnum, Default)]
pub enum HisStrategy {
Hid,
Hie,
Random,
#[default]
Network,
}
#[derive(Clone, Copy, Debug, ValueEnum, Default)]
pub enum Cation {
#[default]
Na,
K,
Mg,
Ca,
Li,
Zn,
}
#[derive(Clone, Copy, Debug, ValueEnum, Default)]
pub enum Anion {
#[default]
Cl,
Br,
I,
F,
}
pub fn parse() -> Cli {
Cli::parse()
}