use crate::{
ast::{ArgumentKind, FixDef},
check_commands::{invalid_arguments, utils},
styles::FixStyle,
};
pub(crate) fn parse_nh_fixes(fix: &FixDef) -> Result<(), invalid_arguments::InvalidArgumentsType> {
let args = &fix.args;
let mut iter = args.iter();
let is_barostatting = matches!(fix.fix_style, FixStyle::Npt | FixStyle::Nph);
let is_thermostatting = matches!(fix.fix_style, FixStyle::Npt | FixStyle::Nvt);
let barostat_only = |kwarg: &str| {
if !is_barostatting {
Err(invalid_arguments::InvalidArgumentsType::InvalidKeyword {
kwarg: kwarg.to_string(),
fix_style: fix.fix_style,
})
} else {
Ok(())
}
};
let thermostat_only = |kwarg: &str| {
if !is_thermostatting {
Err(invalid_arguments::InvalidArgumentsType::InvalidKeyword {
kwarg: kwarg.to_string(),
fix_style: fix.fix_style,
})
} else {
Ok(())
}
};
while let Some(arg) = iter.next() {
match &arg.kind {
ArgumentKind::Word(kwarg) if kwarg == "temp" => {
thermostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 3, "<Tstart> <Tstop> <Tdamp>")?;
}
ArgumentKind::Word(kwarg)
if matches!(
kwarg.as_ref(),
"iso" | "aniso" | "tri" | "x" | "y" | "z" | "xy" | "xz" | "yz"
) =>
{
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 3, "<Pstart> <Pstop> <Pdamp>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "couple" => {
barostat_only(kwarg)?;
utils::kwarg_expected_enum(
&mut iter,
"couple",
1,
&["none", "xyz", "xy", "xz", "yz"],
)?;
}
ArgumentKind::Word(kwarg) if kwarg == "tchain" => {
thermostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<N> chain.")?;
}
ArgumentKind::Word(kwarg) if kwarg == "pchain" => {
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<N> chain.")?;
}
ArgumentKind::Word(kwarg) if kwarg == "mtk" => {
barostat_only(kwarg)?;
utils::kwarg_expected_bool(&mut iter, kwarg, 1, "<value>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "tloop" => {
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<N> sub-cycles")?;
}
ArgumentKind::Word(kwarg) if kwarg == "ploop" => {
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<N> sub-cycles")?;
}
ArgumentKind::Word(kwarg) if kwarg == "nreset" => {
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<N> reset")?;
}
ArgumentKind::Word(kwarg) if kwarg == "drag" => {
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<Df> drag factor")?;
}
ArgumentKind::Word(kwarg) if kwarg == "ptemp" => {
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<Ttarget>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "dilate" => {
barostat_only(kwarg)?;
utils::kwarg_expected_str(&mut iter, kwarg, 1, "<dilate-group-ID>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "scalexy" => {
barostat_only(kwarg)?;
utils::kwarg_expected_bool(&mut iter, kwarg, 1, "<value>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "scaleyz" => {
barostat_only(kwarg)?;
utils::kwarg_expected_bool(&mut iter, kwarg, 1, "<value>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "scalexz" => {
barostat_only(kwarg)?;
utils::kwarg_expected_bool(&mut iter, kwarg, 1, "<value>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "flip" => {
barostat_only(kwarg)?;
utils::kwarg_expected_bool(&mut iter, kwarg, 1, "<value>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "fixedpoint" => {
barostat_only(kwarg)?;
utils::kwarg_expected_floats(&mut iter, kwarg, 1, "<x> <y> <z>")?;
}
ArgumentKind::Word(kwarg) if kwarg == "update" => {
utils::kwarg_expected_enum(&mut iter, "update", 1, &["dipole", "dipole/dlm"])?
}
ArgumentKind::Word(kwarg) => Err(invalid_arguments::InvalidArgumentsType::Custom(
format!("Unknown kwarg argument: {kwarg}",),
))?,
_ => Err(invalid_arguments::InvalidArgumentsType::Custom(format!(
"Unknown argument: {arg}",
)))?,
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ast::from_node::FromNode, utils::testing::parse};
#[test]
fn valid_nvt() {
let text = "fix NVT all nvt temp 1 $(v_T*1.5) $(100*dt)\n";
let tree = parse(text);
let node = tree.root_node().child(0).unwrap();
let fix = FixDef::from_node(&node, text).unwrap();
assert_eq!(fix.fix_id.name, "NVT");
assert_eq!(fix.group_id.contents, "all");
assert_eq!(fix.fix_style, FixStyle::Nvt);
assert!(!fix.args.is_empty());
assert_eq!(parse_nh_fixes(&fix), Ok(()));
}
#[test]
fn valid_npt() {
let text = "fix NPT all npt temp 1 $(v_T*1.5) $(100*dt) iso 1 $(v_p*1.5) ${pdamp}\n";
let tree = parse(text);
let node = tree.root_node().child(0).unwrap();
let fix = FixDef::from_node(&node, text).unwrap();
assert_eq!(fix.fix_id.name, "NPT");
assert_eq!(fix.group_id.contents, "all");
assert_eq!(fix.fix_style, FixStyle::Npt);
assert!(!fix.args.is_empty());
assert_eq!(dbg!(parse_nh_fixes(&fix)), Ok(()));
}
#[test]
fn complicated_nph() {
let text = "fix 2 ice nph x 1.0 1.0 0.5 y 2.0 2.0 0.5 z 3.0 3.0 0.5 yz 0.1 0.1 0.5 xz 0.2 0.2 0.5 xy 0.3 0.3 0.5 nreset 1000\n";
let tree = parse(text);
let node = tree.root_node().child(0).unwrap();
let fix = FixDef::from_node(&node, text).unwrap();
assert_eq!(fix.fix_id.name, "2");
assert_eq!(fix.group_id.contents, "ice");
assert_eq!(fix.fix_style, FixStyle::Nph);
assert!(!fix.args.is_empty());
assert_eq!(dbg!(parse_nh_fixes(&fix)), Ok(()));
}
#[test]
fn invalid_nvt() {
let text = "fix NVT all nvt temp 1.0 $(v_T*1.5) TEMP\n";
let tree = parse(text);
let node = tree.root_node().child(0).unwrap();
let fix = FixDef::from_node(&node, text).unwrap();
assert_eq!(fix.fix_id.name, "NVT");
assert_eq!(fix.group_id.contents, "all");
assert_eq!(fix.fix_style, FixStyle::Nvt);
assert!(!fix.args.is_empty());
assert!(parse_nh_fixes(&fix).is_err());
}
}