File size: 5,038 Bytes
ff9fbcf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""
FBMC Chronos-2 Forecasting API
HuggingFace Space Gradio Interface
Version: 1.0.2 (fixed memory fragmentation - expandable_segments)
"""

# CRITICAL: Set PyTorch memory allocator config BEFORE any imports
# This prevents memory fragmentation issues that cause OOM even with sufficient free memory
# Must be set before torch is imported the first time (including via gradio or other dependencies)
import os
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'

import sys
print(f"[STARTUP] Python version: {sys.version}", flush=True)
print(f"[STARTUP] Python path: {sys.path[:3]}", flush=True)
print(f"[STARTUP] PyTorch memory config: {os.environ.get('PYTORCH_CUDA_ALLOC_CONF')}", flush=True)

import gradio as gr
from datetime import datetime

print("[STARTUP] Basic imports successful", flush=True)

try:
    from src.forecasting.chronos_inference import run_inference
    print("[STARTUP] chronos_inference import successful", flush=True)
except Exception as e:
    print(f"[ERROR] Failed to import chronos_inference: {e}", flush=True)
    import traceback
    traceback.print_exc()
    run_inference = None


# Global configuration
FORECAST_TYPES = {
    "smoke_test": "Smoke Test (1 border × 7 days)",
    "full_14day": "Full Forecast (All borders × 14 days)"
}

print("[STARTUP] Configuration loaded", flush=True)


def forecast_api(run_date_str, forecast_type):
    """
    API endpoint for triggering forecasts.

    Args:
        run_date_str: Date in YYYY-MM-DD format
        forecast_type: 'smoke_test' or 'full_14day'

    Returns:
        Path to downloadable forecast results file
    """
    try:
        # Validate run date
        run_date = datetime.strptime(run_date_str, "%Y-%m-%d")

        # Run inference
        result_path = run_inference(
            run_date=run_date_str,
            forecast_type=forecast_type,
            output_dir="/tmp"
        )

        return result_path

    except Exception as e:
        error_msg = f"Error: {str(e)}"
        print(error_msg)
        # Return error message as text file
        error_path = "/tmp/error.txt"
        with open(error_path, 'w') as f:
            f.write(error_msg)
        return error_path


# Build Gradio interface
with gr.Blocks(title="FBMC Chronos-2 Forecasting") as demo:
    gr.Markdown("""
    # FBMC Chronos-2 Zero-Shot Forecasting API

    **Flow-Based Market Coupling** electricity flow forecasting using Amazon Chronos-2.

    This Space provides GPU-accelerated zero-shot inference for cross-border electricity flows.
    """)

    with gr.Row():
        with gr.Column():
            gr.Markdown("### Configuration")

            run_date_input = gr.Textbox(
                label="Run Date (YYYY-MM-DD)",
                value="2025-09-30",
                placeholder="2025-09-30",
                info="Date when forecast is made (data up to this date is historical)"
            )

            forecast_type_input = gr.Radio(
                choices=list(FORECAST_TYPES.keys()),
                value="smoke_test",
                label="Forecast Type",
                info="Smoke test: Quick validation (1 border, 7 days). Full: Production forecast (all borders, 14 days)"
            )

            submit_btn = gr.Button("Run Forecast", variant="primary")

        with gr.Column():
            gr.Markdown("### Results")

            output_file = gr.File(
                label="Download Forecast Results",
                type="filepath"
            )

            gr.Markdown("""
            **Output format**: Parquet file with columns:
            - `timestamp`: Hourly timestamps (D+1 to D+7 or D+14)
            - `{border}_median`: Median forecast (MW)
            - `{border}_q10`: 10th percentile (MW)
            - `{border}_q90`: 90th percentile (MW)

            **Inference environment**:
            - GPU: NVIDIA T4 (16GB VRAM)
            - Model: Chronos-T5-Large (710M parameters)
            - Precision: bfloat16
            """)

    # Wire up the interface
    submit_btn.click(
        fn=forecast_api,
        inputs=[run_date_input, forecast_type_input],
        outputs=output_file
    )

    gr.Markdown("""
    ---
    ### About

    **Zero-shot forecasting**: No model training required. The pre-trained Chronos-2 model
    generalizes directly to FBMC cross-border flows using historical patterns and future covariates.

    **Features**:
    - 2,553 engineered features (weather, CNEC constraints, load forecasts, LTA)
    - 24-month historical context (Oct 2023 - Oct 2025)
    - Time-aware extraction (prevents data leakage)
    - Probabilistic forecasts (10th/50th/90th percentiles)

    **Performance**:
    - Smoke test: ~30 seconds (1 border × 168 hours)
    - Full forecast: ~5 minutes (38 borders × 336 hours)

    **Project**: FBMC Flow Forecasting MVP | **Author**: Evgueni Poloukarov
    """)

# Launch the app
if __name__ == "__main__":
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False
    )