#[cfg(feature = "refeff-runner")]
mod accelerated;
mod analysis;
mod calibration;
mod constraints;
mod convergence;
mod engine;
mod ensemble;
mod evolution;
mod first_shell;
mod geometry;
mod local_spectrum;
mod moments;
mod objective;
mod options;
mod paths;
#[cfg(feature = "refeff-runner")]
mod prepared;
mod proposals;
#[cfg(feature = "refeff-runner")]
mod refeff;
mod session;
mod spectrum;
mod structural;
mod workflows;
#[cfg(feature = "refeff-runner")]
pub use accelerated::*;
pub use analysis::*;
pub use calibration::*;
pub use constraints::*;
pub use convergence::*;
pub use engine::{evaluate, refine, refine_with_progress};
pub use ensemble::*;
pub use evolution::*;
pub use first_shell::*;
pub use geometry::{Atom, Configuration};
pub use local_spectrum::*;
pub use moments::*;
pub use objective::{transform_path_fourier, transform_spectrum_fourier, Objective};
pub use options::RefeffOptions;
pub use paths::*;
#[cfg(feature = "refeff-runner")]
pub use prepared::*;
pub use proposals::*;
#[cfg(feature = "refeff-runner")]
pub use refeff::{RefeffCacheStats, RefeffCalculator, RefeffStageTiming};
pub use session::*;
pub use spectrum::*;
pub use structural::*;
pub use workflows::*;
use crate::structure::Edge;
use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum RmcError {
#[error("invalid RMC input: {0}")]
Invalid(String),
#[error("RMC calculator failed: {0}")]
Calculator(String),
}
pub(crate) fn require(condition: bool, message: impl Into<String>) -> Result<(), RmcError> {
if condition {
Ok(())
} else {
Err(RmcError::Invalid(message.into()))
}
}
pub trait ExafsCalculator {
fn name(&self) -> &str;
fn calculate(
&mut self,
configuration: &Configuration,
absorber: usize,
edge: Edge,
k: &[f64],
) -> Result<Vec<f64>, RmcError>;
fn identity(&self) -> String {
self.name().to_owned()
}
fn calculate_request(
&mut self,
request: CalculationRequest<'_>,
) -> Result<CalculatedSpectrum, RmcError> {
require(
request.options.is_none() && !request.paths,
"this calculator does not support per-dataset options or path reports",
)?;
Ok(CalculatedSpectrum {
chi: self.calculate(
request.configuration,
request.absorber,
request.edge,
request.k,
)?,
paths: Vec::new(),
})
}
fn calculate_batch(
&mut self,
requests: &[CalculationRequest<'_>],
) -> Result<Vec<CalculatedSpectrum>, RmcError> {
requests
.iter()
.map(|request| self.calculate_request(*request))
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ExafsDataset {
pub name: String,
pub absorbers: Vec<usize>,
pub edge: Edge,
pub k: Vec<f64>,
pub chi: Vec<f64>,
pub sigma: Vec<f64>,
pub weight: f64,
pub kweight: u8,
pub s02: f64,
pub delta_e0: f64,
}
impl ExafsDataset {
pub fn theoretical_k(&self) -> Result<Vec<f64>, RmcError> {
require(
self.k.len() >= 2
&& self.k.iter().all(|k| k.is_finite() && *k >= 0.)
&& self.k.windows(2).all(|w| w[1] > w[0])
&& self.delta_e0.is_finite(),
"theoretical k needs increasing nonnegative samples and finite delta_e0",
)?;
engine::shifted_grid(self)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct RmcProblem {
pub configuration: Configuration,
pub datasets: Vec<ExafsDataset>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
pub struct RmcSettings {
pub steps: usize,
pub seed: u64,
pub step_size: f64,
pub temperature: f64,
pub min_distance: f64,
pub max_displacement: Option<f64>,
pub movable_atoms: Vec<usize>,
}
impl Default for RmcSettings {
fn default() -> Self {
Self {
steps: 100,
seed: 0,
step_size: 0.05,
temperature: 1.0,
min_distance: 1.0,
max_displacement: Some(0.5),
movable_atoms: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DatasetFit {
pub name: String,
pub chi: Vec<f64>,
pub score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Evaluation {
pub score: f64,
pub datasets: Vec<DatasetFit>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RmcState {
pub configuration: Configuration,
pub evaluation: Evaluation,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RmcStep {
pub step: usize,
pub atom: usize,
pub accepted: bool,
pub constraint_rejected: bool,
pub trial_score: Option<f64>,
pub score: f64,
pub best_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RmcResult {
pub calculator: String,
pub settings: RmcSettings,
pub initial: RmcState,
pub best: RmcState,
pub final_state: RmcState,
pub history: Vec<RmcStep>,
pub stopped: bool,
}