pfas-sbead-optimization / utils /calculations.py
shrut27's picture
Upload folder using huggingface_hub
bcb2d6c verified
Raw
History Blame Contribute Delete
4.93 kB
"""AI models and optimization calculations for the PFAS-SBEAD pipeline."""
from __future__ import annotations
from typing import Any
import numpy as np
import pandas as pd
from sklearn.ensemble import (
GradientBoostingClassifier,
GradientBoostingRegressor,
IsolationForest,
RandomForestRegressor,
)
from sklearn.model_selection import cross_val_score
FEATURE_COLUMNS = [
"OLR_kg_m3_d",
"HRT_days",
"pH",
"temperature_C",
"COD_mg_L",
"VFA_mg_L",
"alkalinity_mg_CaCO3_L",
"voltage_V",
"current_A",
"current_density_A_m2",
"conductivity_mS_cm",
"electrode_area_m2",
"electrode_spacing_cm",
"initial_PFAS_ug_L",
]
AI_SCORE_WEIGHTS = {
"pfas_degradation": 0.40,
"fluoride_release": 0.30,
"short_chain_risk": -0.15,
"energy_input": -0.10,
"instability": -0.05,
}
def compute_ai_score(
degradation_norm: float,
fluoride_norm: float,
short_chain_risk: float,
energy_norm: float,
instability: float,
) -> float:
"""Compute the composite AI optimization score."""
score = (
AI_SCORE_WEIGHTS["pfas_degradation"] * degradation_norm
+ AI_SCORE_WEIGHTS["fluoride_release"] * fluoride_norm
+ AI_SCORE_WEIGHTS["short_chain_risk"] * short_chain_risk
+ AI_SCORE_WEIGHTS["energy_input"] * energy_norm
+ AI_SCORE_WEIGHTS["instability"] * instability
)
return float(np.clip(score, 0, 1))
def train_degradation_model(df: pd.DataFrame) -> tuple[GradientBoostingRegressor, float]:
"""XGBoost-style regressor for PFAS degradation prediction."""
X = df[FEATURE_COLUMNS].values
y = df["PFAS_degradation_pct"].values
model = GradientBoostingRegressor(
n_estimators=100, max_depth=4, learning_rate=0.1, random_state=42
)
scores = cross_val_score(model, X, y, cv=5, scoring="r2")
model.fit(X, y)
return model, float(scores.mean())
def train_fluoride_model(df: pd.DataFrame) -> tuple[RandomForestRegressor, float]:
"""Random Forest for fluoride release prediction."""
X = df[FEATURE_COLUMNS].values
y = df["fluoride_release_mg_L"].values
model = RandomForestRegressor(n_estimators=100, max_depth=5, random_state=42)
scores = cross_val_score(model, X, y, cv=5, scoring="r2")
model.fit(X, y)
return model, float(scores.mean())
def train_short_chain_classifier(df: pd.DataFrame) -> tuple[GradientBoostingClassifier, float]:
"""XGBoost classifier for short-chain PFAS risk."""
X = df[FEATURE_COLUMNS].values
y = (df["short_chain_formation_ratio"] > 0.3).astype(int).values
model = GradientBoostingClassifier(
n_estimators=80, max_depth=3, learning_rate=0.1, random_state=42
)
scores = cross_val_score(model, X, y, cv=5, scoring="accuracy")
model.fit(X, y)
return model, float(scores.mean())
def train_instability_detector(df: pd.DataFrame) -> IsolationForest:
"""Isolation Forest for reactor instability detection."""
stability_cols = ["pH_drop", "VFA_accumulation_mg_L", "ORP_drift_mV", "current_instability_index"]
X = df[stability_cols].values
model = IsolationForest(contamination=0.15, random_state=42)
model.fit(X)
return model
def compute_shap_importance(model: Any, df: pd.DataFrame) -> pd.DataFrame:
"""Compute feature importance as SHAP-like proxy using tree-based importances."""
X = df[FEATURE_COLUMNS]
importances = model.feature_importances_
imp_df = pd.DataFrame({
"feature": FEATURE_COLUMNS,
"importance": importances,
}).sort_values("importance", ascending=False).reset_index(drop=True)
return imp_df
def bayesian_next_recommendation(df: pd.DataFrame) -> dict[str, Any]:
"""
Simple Bayesian-inspired recommendation for the next experiment.
Finds conditions that maximize expected AI score based on model trends.
"""
top_pct = df.nlargest(10, "AI_score")
rec = {}
for col in FEATURE_COLUMNS:
rec[col] = float(np.round(top_pct[col].mean(), 3))
rec["predicted_degradation_pct"] = float(np.round(top_pct["PFAS_degradation_pct"].mean(), 1))
rec["predicted_fluoride_release"] = float(np.round(top_pct["fluoride_release_mg_L"].mean(), 1))
rec["expected_ai_score"] = float(np.round(top_pct["AI_score"].mean(), 3))
rec["confidence"] = float(np.round(1.0 - top_pct["AI_score"].std(), 2))
return rec
def sensitivity_analysis(df: pd.DataFrame) -> pd.DataFrame:
"""Compute correlation-based sensitivity of AI score to each input feature."""
correlations = []
for col in FEATURE_COLUMNS:
corr = df[col].corr(df["AI_score"])
correlations.append({"feature": col, "correlation_with_AI_score": round(corr, 4)})
sens_df = pd.DataFrame(correlations)
sens_df["abs_correlation"] = sens_df["correlation_with_AI_score"].abs()
return sens_df.sort_values("abs_correlation", ascending=False).reset_index(drop=True)