File size: 7,478 Bytes
f381be8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""
api.gradio_app
==============
Gradio interface for interactive battery SOH / RUL prediction.
Mounted at /gradio inside the FastAPI application.
"""

from __future__ import annotations

import gradio as gr
import numpy as np
import pandas as pd
import plotly.graph_objects as go

from api.model_registry import registry, classify_degradation, soh_to_color

# ── Prediction function ──────────────────────────────────────────────────────
def predict_soh(
    cycle_number: int,
    ambient_temperature: float,
    peak_voltage: float,
    min_voltage: float,
    avg_current: float,
    avg_temp: float,
    temp_rise: float,
    cycle_duration: float,
    Re: float,
    Rct: float,
    delta_capacity: float,
    model_name: str,
):
    features = {
        "cycle_number": cycle_number,
        "ambient_temperature": ambient_temperature,
        "peak_voltage": peak_voltage,
        "min_voltage": min_voltage,
        "voltage_range": peak_voltage - min_voltage,
        "avg_current": avg_current,
        "avg_temp": avg_temp,
        "temp_rise": temp_rise,
        "cycle_duration": cycle_duration,
        "Re": Re,
        "Rct": Rct,
        "delta_capacity": delta_capacity,
    }

    name = model_name if model_name != "auto" else None
    result = registry.predict(features, model_name=name)

    soh = result["soh_pct"]
    rul = result["rul_cycles"]
    state = result["degradation_state"]
    model_used = result["model_used"]
    ci_lo = result.get("confidence_lower", soh - 2)
    ci_hi = result.get("confidence_upper", soh + 2)

    # Summary text
    summary = (
        f"## Prediction Result\n\n"
        f"- **SOH:** {soh:.1f}%\n"
        f"- **RUL:** {rul:.0f} cycles\n"
        f"- **State:** {state}\n"
        f"- **95% CI:** [{ci_lo:.1f}%, {ci_hi:.1f}%]\n"
        f"- **Model:** {model_used}\n"
    )

    # SOH gauge figure
    fig = go.Figure(go.Indicator(
        mode="gauge+number+delta",
        value=soh,
        title={"text": "State of Health (%)"},
        delta={"reference": 100, "decreasing": {"color": "red"}},
        gauge={
            "axis": {"range": [0, 100]},
            "bar": {"color": soh_to_color(soh)},
            "steps": [
                {"range": [0, 70], "color": "#fee2e2"},
                {"range": [70, 80], "color": "#fef3c7"},
                {"range": [80, 90], "color": "#fef9c3"},
                {"range": [90, 100], "color": "#dcfce7"},
            ],
            "threshold": {
                "line": {"color": "red", "width": 3},
                "thickness": 0.75,
                "value": 70,
            },
        },
    ))
    fig.update_layout(height=350)

    return summary, fig


# ── Capacity trajectory ──────────────────────────────────────────────────────
def plot_capacity_trajectory(battery_id: str):
    from pathlib import Path
    meta_path = Path(__file__).resolve().parents[1] / "cleaned_dataset" / "metadata.csv"
    if not meta_path.exists():
        return None
    meta = pd.read_csv(meta_path)
    sub = meta[meta["battery_id"] == battery_id].sort_values("start_time")
    if sub.empty:
        return None

    caps = sub["Capacity"].dropna().values
    cycles = np.arange(1, len(caps) + 1)
    soh = (caps / 2.0) * 100

    fig = go.Figure()
    fig.add_trace(go.Scatter(
        x=cycles, y=soh, mode="lines+markers",
        marker=dict(size=3), line=dict(width=2),
        name=battery_id,
    ))
    fig.add_hline(y=70, line_dash="dash", line_color="red",
                  annotation_text="EOL (70%)")
    fig.update_layout(
        title=f"SOH Trajectory β€” {battery_id}",
        xaxis_title="Cycle", yaxis_title="SOH (%)",
        template="plotly_white", height=400,
    )
    return fig


# ── Build Gradio app ─────────────────────────────────────────────────────────
def create_gradio_app() -> gr.Blocks:
    model_choices = ["auto"] + [m["name"] for m in registry.list_models()]

    with gr.Blocks(
        title="Battery Lifecycle Predictor",
    ) as demo:
        gr.Markdown(
            "# AI Battery Lifecycle Predictor\n"
            "Predict **State of Health (SOH)** and **Remaining Useful Life (RUL)** "
            "using machine-learning models trained on the NASA PCoE Li-ion Battery Dataset."
        )

        with gr.Tab("Predict"):
            with gr.Row():
                with gr.Column(scale=1):
                    cycle_number = gr.Number(label="Cycle Number", value=100, precision=0)
                    ambient_temp = gr.Slider(0, 60, value=24, label="Ambient Temperature (Β°C)")
                    peak_v = gr.Number(label="Peak Voltage (V)", value=4.2)
                    min_v = gr.Number(label="Min Voltage (V)", value=2.7)
                    avg_curr = gr.Number(label="Avg Discharge Current (A)", value=2.0)
                    avg_t = gr.Number(label="Avg Cell Temp (Β°C)", value=25.0)
                    temp_rise = gr.Number(label="Temp Rise (Β°C)", value=3.0)
                    duration = gr.Number(label="Cycle Duration (s)", value=3600)
                    re = gr.Number(label="Re (Ξ©)", value=0.04)
                    rct = gr.Number(label="Rct (Ξ©)", value=0.02)
                    delta_cap = gr.Number(label="Ξ”Capacity (Ah)", value=-0.005)
                    model_dd = gr.Dropdown(choices=model_choices, value="auto", label="Model")
                    btn = gr.Button("Predict", variant="primary")

                with gr.Column(scale=1):
                    result_md = gr.Markdown()
                    gauge = gr.Plot(label="SOH Gauge")

            btn.click(
                fn=predict_soh,
                inputs=[cycle_number, ambient_temp, peak_v, min_v, avg_curr,
                        avg_t, temp_rise, duration, re, rct, delta_cap, model_dd],
                outputs=[result_md, gauge],
            )

        with gr.Tab("Battery Explorer"):
            bid_input = gr.Textbox(label="Battery ID", value="B0005", placeholder="e.g., B0005")
            explore_btn = gr.Button("Load Trajectory")
            cap_plot = gr.Plot(label="Capacity Trajectory")
            explore_btn.click(fn=plot_capacity_trajectory, inputs=[bid_input], outputs=[cap_plot])

        with gr.Tab("About"):
            gr.Markdown(
                "## About\n\n"
                "This application predicts Li-ion battery degradation using models trained on the "
                "**NASA Prognostics Center of Excellence (PCoE)** Battery Dataset.\n\n"
                "### Models\n"
                "- Classical ML: Ridge, Lasso, ElasticNet, KNN, SVR, Random Forest, XGBoost, LightGBM\n"
                "- Deep Learning: LSTM (4 variants), BatteryGPT, TFT, iTransformer (3 variants), VAE-LSTM\n"
                "- Ensemble: Stacking, Weighted Average\n\n"
                "### Dataset\n"
                "- 36 Li-ion 18650 cells (nominal 2.0Ah)\n"
                "- Charge/discharge/impedance cycles at three temperature regimes\n"
                "- End-of-Life: 30% capacity fade (1.4Ah)\n\n"
                "### Reference\n"
                "B. Saha and K. Goebel (2007). *Battery Data Set*, NASA Prognostics Data Repository."
            )

    return demo