#[derive(Debug, Clone, Copy)]
pub struct HydrothermalConditions {
pub temperature_k: f64,
pub pressure_pa: f64,
pub distance_m: f64,
pub flow_rate: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AlterationZone {
Potassic,
Phyllic,
Argillic,
Propylitic,
Unaltered,
}
#[must_use]
pub fn classify_alteration(temperature_c: f64) -> AlterationZone {
if temperature_c > 500.0 {
AlterationZone::Potassic
} else if temperature_c > 350.0 {
AlterationZone::Phyllic
} else if temperature_c > 250.0 {
AlterationZone::Argillic
} else if temperature_c > 150.0 {
AlterationZone::Propylitic
} else {
AlterationZone::Unaltered
}
}
#[must_use]
pub fn metal_solubility(temperature_c: f64, precipitation_temp_c: f64) -> f64 {
if temperature_c <= precipitation_temp_c {
0.0
} else {
let excess = temperature_c - precipitation_temp_c;
(1.0 - (-excess / 200.0).exp()).clamp(0.0, 1.0)
}
}
#[must_use]
pub fn precipitation_rate(temperature_c: f64, precipitation_temp_c: f64) -> f64 {
let delta = temperature_c - precipitation_temp_c;
(-delta.powi(2) / (2.0 * 30.0_f64.powi(2))).exp()
}
#[must_use]
pub fn estimated_ore_grade(
fluid_flux: f64,
temperature_c: f64,
precipitation_temp_c: f64,
porosity: f64,
background_grade: f64,
) -> f64 {
let precip = precipitation_rate(temperature_c, precipitation_temp_c);
let flux_factor = (fluid_flux / 1e-6).min(10.0); let trap_factor = porosity * 5.0; let enhancement = precip * flux_factor * trap_factor;
(background_grade * (1.0 + enhancement)).min(1.0)
}
pub mod precipitation_temps {
pub const GOLD: f64 = 300.0;
pub const COPPER: f64 = 350.0;
pub const SILVER: f64 = 250.0;
pub const LEAD: f64 = 200.0;
pub const ZINC: f64 = 250.0;
pub const TIN: f64 = 400.0;
pub const MOLYBDENUM: f64 = 500.0;
pub const TUNGSTEN: f64 = 450.0;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alteration_zones_temperature_order() {
assert_eq!(classify_alteration(600.0), AlterationZone::Potassic);
assert_eq!(classify_alteration(400.0), AlterationZone::Phyllic);
assert_eq!(classify_alteration(300.0), AlterationZone::Argillic);
assert_eq!(classify_alteration(200.0), AlterationZone::Propylitic);
assert_eq!(classify_alteration(100.0), AlterationZone::Unaltered);
}
#[test]
fn metal_soluble_when_hot() {
let hot = metal_solubility(500.0, 300.0);
let cold = metal_solubility(200.0, 300.0);
assert!(hot > 0.0);
assert!(cold.abs() < f64::EPSILON);
}
#[test]
fn precipitation_peaks_at_target_temp() {
let at_target = precipitation_rate(300.0, 300.0);
let above = precipitation_rate(400.0, 300.0);
let below = precipitation_rate(200.0, 300.0);
assert!(at_target > above);
assert!(at_target > below);
assert!((at_target - 1.0).abs() < 0.01); }
#[test]
fn ore_grade_enhanced_at_precipitation_temp() {
let bg = 0.001;
let enhanced = estimated_ore_grade(1e-6, 300.0, 300.0, 0.1, bg);
assert!(
enhanced > bg,
"Grade should be enhanced at precipitation temp"
);
}
#[test]
fn ore_grade_not_enhanced_far_from_precip_temp() {
let bg = 0.001;
let far = estimated_ore_grade(1e-6, 100.0, 300.0, 0.1, bg);
assert!(
(far - bg).abs() < bg * 0.1,
"Grade should be near background far from precip temp"
);
}
#[test]
fn ore_grade_bounded() {
let grade = estimated_ore_grade(1e-3, 300.0, 300.0, 0.5, 0.5);
assert!(grade <= 1.0);
}
#[test]
fn higher_flux_higher_grade() {
let low = estimated_ore_grade(1e-8, 300.0, 300.0, 0.1, 0.001);
let high = estimated_ore_grade(1e-5, 300.0, 300.0, 0.1, 0.001);
assert!(high > low);
}
#[test]
fn gold_precipitates_at_300c() {
assert!((precipitation_temps::GOLD - 300.0).abs() < f64::EPSILON);
}
}