napoles3d commited on
Commit
7f4459e
·
verified ·
1 Parent(s): 058c0c9

Upload 5 files

Browse files
Files changed (5) hide show
  1. README.md +20 -14
  2. about.py +53 -0
  3. app.py +34 -0
  4. requirements.txt +3 -0
  5. utils.py +172 -0
README.md CHANGED
@@ -1,14 +1,20 @@
1
- ---
2
- title: Leadpde
3
- emoji: 🏃
4
- colorFrom: pink
5
- colorTo: gray
6
- sdk: gradio
7
- sdk_version: 6.12.0
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: test
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
1
+ # Public Space
2
+
3
+ Gradio leaderboard Space for The Well benchmark MVP.
4
+
5
+ Responsibilities:
6
+
7
+ - accept submission archives,
8
+ - upload packages and metadata to the submissions dataset,
9
+ - load results from the results dataset,
10
+ - render the public leaderboard and benchmark description.
11
+
12
+ Required Space secrets:
13
+
14
+ - `HF_TOKEN`
15
+ - `SUBMISSIONS_REPO`
16
+ - `RESULTS_REPO`
17
+
18
+ Recommended Space variables:
19
+
20
+ - `MAX_SUBMISSION_MB`
about.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TITLE = "# The Well Leaderboard MVP"
2
+
3
+ INTRO_TEXT = """
4
+ Benchmark task: `turbulent_radiative_layer_2D_1step`
5
+
6
+ This leaderboard evaluates one-step forecasting on The Well dataset
7
+ `turbulent_radiative_layer_2D` using:
8
+
9
+ - `n_steps_input = 4`
10
+ - `n_steps_output = 1`
11
+ - primary metric: `avg_vrmse`
12
+
13
+ Lower scores are better.
14
+ """
15
+
16
+ SUBMISSION_TEXT = """
17
+ ## Submission format
18
+
19
+ Upload a `.zip` file containing exactly:
20
+
21
+ - `submission.json`
22
+ - `predictions.npz`
23
+
24
+ Expected tensor shape in `predictions.npz`:
25
+
26
+ `(N, 1, 128, 384, 4)`
27
+
28
+ Field order:
29
+
30
+ 1. `density`
31
+ 2. `pressure`
32
+ 3. `velocity_x`
33
+ 4. `velocity_y`
34
+ """
35
+
36
+ ABOUT_TEXT = """
37
+ ## Benchmark overview
38
+
39
+ This is an MVP benchmark for a single The Well task:
40
+ `turbulent_radiative_layer_2D_1step`.
41
+
42
+ The leaderboard Space is public. The evaluator Space is private and holds
43
+ the evaluation logic and hidden targets.
44
+
45
+ Submissions are validated and then scored with VRMSE. The public ranking uses
46
+ the average VRMSE across fields.
47
+
48
+ ## Notes
49
+
50
+ - Lower `avg_vrmse` is better.
51
+ - Invalid submissions are recorded with a failure message.
52
+ - This benchmark is intended to validate protocol and infrastructure first.
53
+ """
app.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from about import ABOUT_TEXT, INTRO_TEXT, SUBMISSION_TEXT, TITLE
4
+ from utils import load_results_dataframe, submit_zip
5
+
6
+
7
+ def refresh_leaderboard():
8
+ return load_results_dataframe()
9
+
10
+
11
+ with gr.Blocks(title="The Well Leaderboard MVP") as demo:
12
+ gr.Markdown(TITLE)
13
+ gr.Markdown(INTRO_TEXT)
14
+
15
+ with gr.Tab("Leaderboard"):
16
+ leaderboard = gr.Dataframe(
17
+ value=refresh_leaderboard,
18
+ label="Ranked results",
19
+ wrap=True,
20
+ )
21
+ refresh_button = gr.Button("Refresh leaderboard")
22
+ refresh_button.click(fn=refresh_leaderboard, outputs=leaderboard)
23
+
24
+ with gr.Tab("Submit"):
25
+ gr.Markdown(SUBMISSION_TEXT)
26
+ zip_file = gr.File(label="Submission zip", file_count="single", file_types=[".zip"])
27
+ submit_button = gr.Button("Submit")
28
+ submission_status = gr.Markdown()
29
+ submit_button.click(fn=submit_zip, inputs=zip_file, outputs=submission_status)
30
+
31
+ with gr.Tab("About"):
32
+ gr.Markdown(ABOUT_TEXT)
33
+
34
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=5.0.0
2
+ huggingface_hub>=0.30.0
3
+ pandas>=2.2.0
utils.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib
2
+ import io
3
+ import json
4
+ import os
5
+ import zipfile
6
+ from datetime import datetime, timezone
7
+ from typing import Any
8
+
9
+ import pandas as pd
10
+ from huggingface_hub import HfApi, hf_hub_download
11
+
12
+ API = HfApi()
13
+
14
+ SUBMISSIONS_REPO = os.environ.get("SUBMISSIONS_REPO", "your-org/the-well-submissions")
15
+ RESULTS_REPO = os.environ.get("RESULTS_REPO", "your-org/the-well-results")
16
+ HF_TOKEN = os.environ.get("HF_TOKEN")
17
+ MAX_SUBMISSION_MB = int(os.environ.get("MAX_SUBMISSION_MB", "200"))
18
+
19
+ EXPECTED_TASK = "turbulent_radiative_layer_2D_1step"
20
+ RESULT_COLUMNS = [
21
+ "rank",
22
+ "model_name",
23
+ "team_name",
24
+ "avg_vrmse",
25
+ "density_vrmse",
26
+ "pressure_vrmse",
27
+ "velocity_x_vrmse",
28
+ "velocity_y_vrmse",
29
+ "submitted_at",
30
+ "status",
31
+ ]
32
+
33
+
34
+ def _utc_now_iso() -> str:
35
+ return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
36
+
37
+
38
+ def _safe_slug(value: str) -> str:
39
+ cleaned = "".join(ch if ch.isalnum() or ch in "-_." else "_" for ch in value.strip())
40
+ return cleaned[:80] or "submission"
41
+
42
+
43
+ def _read_submission_manifest(zip_bytes: bytes) -> dict[str, Any]:
44
+ with zipfile.ZipFile(io.BytesIO(zip_bytes), "r") as zf:
45
+ names = sorted(zf.namelist())
46
+ if names != ["predictions.npz", "submission.json"]:
47
+ raise ValueError(
48
+ "The zip must contain exactly two root files: submission.json and predictions.npz."
49
+ )
50
+ with zf.open("submission.json") as f:
51
+ manifest = json.load(f)
52
+ if manifest.get("task_name") != EXPECTED_TASK:
53
+ raise ValueError(f"task_name must be '{EXPECTED_TASK}'.")
54
+ if not str(manifest.get("model_name", "")).strip():
55
+ raise ValueError("submission.json must include a non-empty model_name.")
56
+ if not str(manifest.get("team_name", "")).strip():
57
+ raise ValueError("submission.json must include a non-empty team_name.")
58
+ return manifest
59
+
60
+
61
+ def submit_zip(zip_file) -> str:
62
+ if zip_file is None:
63
+ return "Please upload a submission `.zip` file."
64
+
65
+ local_path = zip_file.name
66
+ if not local_path.lower().endswith(".zip"):
67
+ return "Invalid file type. Please upload a `.zip` file."
68
+
69
+ file_size = os.path.getsize(local_path)
70
+ if file_size > MAX_SUBMISSION_MB * 1024 * 1024:
71
+ return f"Submission too large. Limit is {MAX_SUBMISSION_MB} MB."
72
+
73
+ with open(local_path, "rb") as f:
74
+ zip_bytes = f.read()
75
+
76
+ try:
77
+ manifest = _read_submission_manifest(zip_bytes)
78
+ except Exception as exc:
79
+ return f"Submission rejected: {exc}"
80
+
81
+ submitted_at = _utc_now_iso()
82
+ base_name = _safe_slug(manifest["model_name"])
83
+ submission_id = f"{base_name}_{submitted_at}".replace(":", "-")
84
+ sha256 = hashlib.sha256(zip_bytes).hexdigest()
85
+
86
+ package_path = f"packages/{submission_id}.zip"
87
+ metadata_path = f"metadata/{submission_id}.json"
88
+
89
+ metadata = {
90
+ "submission_id": submission_id,
91
+ "task_name": manifest["task_name"],
92
+ "model_name": manifest["model_name"],
93
+ "team_name": manifest["team_name"],
94
+ "method_name": manifest.get("method_name", ""),
95
+ "submitted_at": submitted_at,
96
+ "package_path": package_path,
97
+ "sha256": sha256,
98
+ "status": "pending",
99
+ }
100
+
101
+ API.upload_file(
102
+ path_or_fileobj=zip_bytes,
103
+ path_in_repo=package_path,
104
+ repo_id=SUBMISSIONS_REPO,
105
+ repo_type="dataset",
106
+ token=HF_TOKEN,
107
+ )
108
+ API.upload_file(
109
+ path_or_fileobj=json.dumps(metadata, indent=2).encode("utf-8"),
110
+ path_in_repo=metadata_path,
111
+ repo_id=SUBMISSIONS_REPO,
112
+ repo_type="dataset",
113
+ token=HF_TOKEN,
114
+ )
115
+
116
+ return (
117
+ f"Submission received: `{submission_id}`\n\n"
118
+ "It was uploaded to the submissions dataset and will appear on the leaderboard "
119
+ "after the private evaluator processes it."
120
+ )
121
+
122
+
123
+ def _download_json_records(repo_id: str, prefix: str) -> list[dict[str, Any]]:
124
+ files = [
125
+ path
126
+ for path in API.list_repo_files(repo_id=repo_id, repo_type="dataset", token=HF_TOKEN)
127
+ if path.startswith(prefix) and path.endswith(".json")
128
+ ]
129
+ records = []
130
+ for path in files:
131
+ local_path = hf_hub_download(
132
+ repo_id=repo_id,
133
+ repo_type="dataset",
134
+ filename=path,
135
+ token=HF_TOKEN,
136
+ )
137
+ with open(local_path, "r", encoding="utf-8") as f:
138
+ records.append(json.load(f))
139
+ return records
140
+
141
+
142
+ def load_results_dataframe() -> pd.DataFrame:
143
+ try:
144
+ records = _download_json_records(RESULTS_REPO, "results/")
145
+ except Exception:
146
+ return pd.DataFrame(columns=RESULT_COLUMNS)
147
+
148
+ if not records:
149
+ return pd.DataFrame(columns=RESULT_COLUMNS)
150
+
151
+ df = pd.DataFrame.from_records(records)
152
+ if "status" in df.columns:
153
+ df = df[df["status"] == "succeeded"].copy()
154
+ if df.empty:
155
+ return pd.DataFrame(columns=RESULT_COLUMNS)
156
+
157
+ for column in [
158
+ "avg_vrmse",
159
+ "density_vrmse",
160
+ "pressure_vrmse",
161
+ "velocity_x_vrmse",
162
+ "velocity_y_vrmse",
163
+ ]:
164
+ df[column] = pd.to_numeric(df[column], errors="coerce")
165
+
166
+ df = df.sort_values("avg_vrmse", ascending=True).reset_index(drop=True)
167
+ df.insert(0, "rank", range(1, len(df) + 1))
168
+
169
+ for column in RESULT_COLUMNS:
170
+ if column not in df.columns:
171
+ df[column] = None
172
+ return df[RESULT_COLUMNS]