use std::path::PathBuf;
use feffit::feffdat::{FeffPath, Interp, KGrid, PathParams, ff2chi};
use feffit::params::Parameters;
use feffit::transform::FitSpace;
use feffit::xafsft::Window;
use feffit::{DataSet, FitDataSet, PathSpec, Spec, Transform, feffit};
fn cu_feff_inp() -> String {
let p = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/feff.inp");
std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()))
}
fn runner() -> Option<feffit::feffrun::Feff8l> {
if let Some(dir) = std::env::var_os(feffit::feffrun::BIN_DIR_ENV)
&& PathBuf::from(&dir).join("feff8l_pot").is_file()
{
return Some(feffit::feffrun::Feff8l::with_bin_dir(dir));
}
if let Some(paths) = std::env::var_os("PATH") {
for d in std::env::split_paths(&paths) {
if d.join("feff8l_pot").is_file() {
return Some(feffit::feffrun::Feff8l::new());
}
}
}
None
}
#[test]
fn feffrun_to_feffit_recovers_known_path_params() {
let Some(runner) = runner() else {
eprintln!(
"SKIP feffrun_to_feffit_recovers_known_path_params: \
feff8l_* not found (set {} or add to PATH)",
feffit::feffrun::BIN_DIR_ENV
);
return;
};
let workdir = std::env::temp_dir().join(format!("feffit-capstone-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&workdir);
let out = runner
.run(&cu_feff_inp(), &workdir)
.expect("FEFF8L pipeline failed");
assert!(
out.dat_files.len() >= 10,
"expected many feffNNNN.dat, got {}",
out.dat_files.len()
);
let first = workdir.join("feff0001.dat");
assert!(first.is_file(), "feff0001.dat was not generated");
let probe = FeffPath::from_path(&first).unwrap();
assert_eq!(
probe.feffdat.nleg, 2,
"first path should be single-scattering"
);
assert!(
(probe.feffdat.reff - 2.55).abs() < 0.05,
"reff {} not ~2.55 Å",
probe.feffdat.reff
);
let degen = probe.feffdat.degen;
const TRUE_S02: f64 = 0.9;
const TRUE_SIG2: f64 = 0.005;
let mut truth = FeffPath::from_path(&first)
.unwrap()
.with_params(PathParams {
s02: TRUE_S02,
sigma2: TRUE_SIG2,
..PathParams::defaults(degen)
});
let (data_k, data_chi) = ff2chi(
std::slice::from_mut(&mut truth),
&KGrid::default_step(),
Interp::Cubic,
);
let fit_path = FeffPath::from_path(&first).unwrap();
let mut spec = PathSpec::defaults(degen);
spec.s02 = Spec::Expr("amp".into());
spec.sigma2 = Spec::Expr("sig2".into());
let transform = Transform::new(
2.0, 17.0, vec![2], 1.0, None, Window::Hanning, 2048, 0.05, 1.0, 4.0, 0.0, None, Window::Hanning, 0.0, FitSpace::K, );
let dataset = DataSet::new(data_k, data_chi, vec![fit_path], transform);
let mut fds = vec![FitDataSet {
dataset,
specs: vec![spec],
epsilon_k: None,
}];
let mut p = Parameters::new();
p.add_var("amp", 1.0);
p.add_var("sig2", 0.003);
let res = feffit(&mut p, &mut fds).expect("feffit");
assert!(
res.info >= 1 && res.info <= 4,
"fit did not converge: info={}",
res.info
);
let amp = res.best.iter().find(|b| b.name == "amp").unwrap().value;
let sig2 = res.best.iter().find(|b| b.name == "sig2").unwrap().value;
eprintln!(
"capstone: recovered amp={amp:.6} (true {TRUE_S02}), \
sig2={sig2:.6} (true {TRUE_SIG2}), nfev={}",
res.nfev
);
let rel = |g: f64, w: f64| (g - w).abs() / w.abs();
assert!(rel(amp, TRUE_S02) < 1e-2, "amp {amp} vs {TRUE_S02}");
assert!(rel(sig2, TRUE_SIG2) < 1e-2, "sig2 {sig2} vs {TRUE_SIG2}");
let _ = std::fs::remove_dir_all(&workdir);
}