hartree-cli 0.2.3

Command-line driver for hartree: an XYZ file plus a basis and method, to an SCF energy.
Documentation
use std::process::Command;

fn water_xyz(name: &str) -> std::path::PathBuf {
    let path = std::env::temp_dir().join(name);
    std::fs::write(
        &path,
        "3\nwater\nO 0.0 0.0 0.117\nH 0.0 0.757 -0.470\nH 0.0 -0.757 -0.470\n",
    )
    .unwrap();
    path
}

#[test]
fn ri_hf_runs_and_prints_aux_header() {
    let xyz = water_xyz("hartree_cli_ri_water.xyz");
    let output = Command::new(env!("CARGO_BIN_EXE_hartree"))
        .arg(&xyz)
        .args(["--basis", "sto-3g", "--method", "rhf", "--ri"])
        .output()
        .expect("run hartree");
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        output.status.success(),
        "exit failure.\nstdout:\n{stdout}\nstderr:\n{}",
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(
        stdout.contains("RI-JK density fitting: aux basis def2-universal-jkfit"),
        "no RI aux header:\n{stdout}"
    );
    assert!(stdout.contains("aux fns"), "no naux in header:\n{stdout}");
    assert!(stdout.contains("converged in"), "not converged:\n{stdout}");
}

#[test]
fn ri_pbe0_runs() {
    let xyz = water_xyz("hartree_cli_ri_pbe0.xyz");
    let output = Command::new(env!("CARGO_BIN_EXE_hartree"))
        .arg(&xyz)
        .args([
            "--basis", "sto-3g", "--method", "pbe0", "--ri", "--grid", "1",
        ])
        .output()
        .expect("run hartree");
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        output.status.success(),
        "exit failure.\nstdout:\n{stdout}\nstderr:\n{}",
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(
        stdout.contains("RI-JK density fitting"),
        "no RI header:\n{stdout}"
    );
    assert!(
        stdout.contains("exact exchange c_x"),
        "no hybrid c_x line:\n{stdout}"
    );
}

#[test]
fn ri_guards_reject_incompatible_flags() {
    let xyz = water_xyz("hartree_cli_ri_guards.xyz");
    let cases: &[(&[&str], &str)] = &[
        (&["--ri", "--direct"], "contradictory"),
        (&["--ri", "--opt"], "geometry optimization"),
        (&["--ri", "--properties"], "properties"),
        (&["--ri", "--freq"], "properties or frequencies"),
        (&["--ri", "--method", "mp2"], "post-HF"),
        (&["--ri", "--method", "ccsd"], "post-HF"),
        (&["--ri", "--method", "ccsd(t)"], "post-HF"),
    ];
    for (flags, needle) in cases {
        let output = Command::new(env!("CARGO_BIN_EXE_hartree"))
            .arg(&xyz)
            .args(["--basis", "sto-3g"])
            .args(*flags)
            .output()
            .expect("run hartree");
        let stderr = String::from_utf8_lossy(&output.stderr);
        assert!(!output.status.success(), "{flags:?} unexpectedly succeeded");
        assert!(
            stderr.contains(needle),
            "{flags:?}: expected {needle:?} in stderr:\n{stderr}"
        );
    }
}

#[test]
fn aux_set_rejected_as_orbital_basis() {
    let xyz = water_xyz("hartree_cli_ri_auxbasis.xyz");
    let output = Command::new(env!("CARGO_BIN_EXE_hartree"))
        .arg(&xyz)
        .args(["--basis", "def2-universal-jkfit", "--method", "rhf"])
        .output()
        .expect("run hartree");
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(!output.status.success());
    assert!(
        stderr.contains("auxiliary fitting set"),
        "expected aux-set rejection message:\n{stderr}"
    );
}