Repository navigation
Include combustion in submission flow. Add more NequIP-OAM-L benchmark results
#92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
090fb20
a59e0da
39ec509
f20a3fe
a043916
2d09081
625b555
7331d4a
44b124f
d185a5c
feaf982
1c3709d
10ef31e
cc970f0
3eb96aa
b1ee4bd
1613b12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # MLIP Arena Benchmarks | ||
|
|
||
| This directory contains scripts and configurations for running benchmarks on machine learning interatomic potentials (MLIPs). | ||
|
|
||
| ## Running and Submitting Benchmark Jobs | ||
|
|
||
| To run or submit benchmark jobs at scale (such as on HPC environments like NERSC Perlmutter), follow these steps: | ||
|
|
||
| ### 1. Register the Model | ||
| Ensure your model is registered under `mlip_arena/models/registry.yaml` with the appropriate metadata (such as class, family, module name, and the list of supported `cpu-tasks` and `gpu-tasks`). | ||
|
|
||
| ### 2. Configure the Submission Script | ||
| Open [benchmarks/submit.py](./submit.py) and configure the following parameters: | ||
|
|
||
| - **Model/Calculator selection**: Set the `calculator` variable to the name of your model registered in the registry. | ||
| ```python | ||
| calculator = "NequIP-OAM-L" | ||
| ``` | ||
| - **SLURM Cluster Config**: In the `SLURM_CONFIG` dictionary, set details corresponding to your HPC account: | ||
| - `account`: HPC charge account. | ||
| - `qos`/`partition`: Target queue. | ||
| - `walltime`: Job time limit. | ||
| - `job_script_prologue`: Bash commands to activate your environment (e.g. `source activate /path/to/conda/env`). | ||
|
|
||
| ### 3. Select Benchmark Tasks | ||
| At the bottom of [benchmarks/submit.py](./submit.py), uncomment the flow execution blocks you want to run: | ||
| - `asymptotic_behaviors`: Homonuclear diatomics, EOS bulk, E-V scan. | ||
| - `distribution_shifts`: NVE trajectory entropy. | ||
| - `stability`: Linear heating and compression. | ||
| - `combustion`: Hydrogen combustion MD simulation. | ||
|
|
||
| ### 4. Execute Submission | ||
| Run the submission script from this directory: | ||
| ```bash | ||
| python submit.py | ||
| ``` | ||
| This script will spin up a Dask cluster over SLURM workers, schedule tasks across the GPU/CPU nodes, execute them, write trajectory/metric logs, and run `report.py` to aggregate the results. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ | |
| "DATA_DIR = Path(\".\")\n", | ||
| "\n", | ||
| "db = connect(DATA_DIR / \"c2db.db\")\n", | ||
| "random_indices = random.sample(range(1, len(db) + 1), 1000)\n" | ||
| "random_indices = random.sample(range(1, len(db) + 1), 1000)" | ||
| ] | ||
| }, | ||
| { | ||
|
|
@@ -36,9 +36,7 @@ | |
| "\n", | ||
| "from mlip_arena.models import MLIPEnum\n", | ||
| "\n", | ||
| "for row, model in tqdm(\n", | ||
| " itertools.product(db.select(filter=lambda r: r[\"id\"] in random_indices), MLIPEnum)\n", | ||
| "):\n", | ||
| "for row, model in tqdm(itertools.product(db.select(filter=lambda r: r[\"id\"] in random_indices), MLIPEnum)):\n", | ||
| " uid = row[\"uid\"]\n", | ||
| "\n", | ||
| " if Path(f\"{model.name}.parquet\").exists():\n", | ||
|
|
@@ -73,7 +71,7 @@ | |
| "\n", | ||
| " df.to_parquet(f\"{model.name}.parquet\", index=False)\n", | ||
| " except Exception:\n", | ||
| " pass\n" | ||
| " pass" | ||
| ] | ||
| }, | ||
| { | ||
|
|
@@ -97,7 +95,7 @@ | |
| "\n", | ||
| "stabilities = np.array(stabilities)\n", | ||
| "\n", | ||
| "(stabilities == True).sum(), (stabilities == False).sum(), (stabilities == None).sum()" | ||
| "(stabilities).sum(), (not stabilities).sum(), (stabilities is None).sum()" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path
path = Path('benchmarks/c2db/analysis.ipynb')
print('exists', path.exists(), 'size', path.stat().st_size)
nb = json.loads(path.read_text())
for i, cell in enumerate(nb.get('cells', [])):
src = ''.join(cell.get('source', []))
if 'stabilities' in src or 'None' in src or 'confusion' in src or 'f1' in src:
print(f'\n--- cell {i} ({cell.get("cell_type")}) ---')
print(src)
PYRepository: atomind-ai/mlip-arena Length of output: 7572 Fix the NumPy mask logic. 🤖 Prompt for AI Agents |
||
| ] | ||
| }, | ||
| { | ||
|
|
@@ -142,11 +140,10 @@ | |
| "]\n", | ||
| "\n", | ||
| "with plt.style.context(\"default\"):\n", | ||
| "\n", | ||
| " SMALL_SIZE = 8\n", | ||
| " MEDIUM_SIZE = 10\n", | ||
| " BIGGER_SIZE = 12\n", | ||
| " \n", | ||
| "\n", | ||
| " plt.rcParams.update(\n", | ||
| " {\n", | ||
| " \"font.size\": SMALL_SIZE,\n", | ||
|
|
@@ -179,33 +176,25 @@ | |
| " continue\n", | ||
| "\n", | ||
| " df = pd.read_parquet(fpath)\n", | ||
| " df[\"eigval_min\"] = df[\"eigenvalues\"].apply(\n", | ||
| " lambda x: x.min() if np.isreal(x).all() else thres\n", | ||
| " )\n", | ||
| " df[\"freq_min\"] = df[\"frequencies\"].apply(\n", | ||
| " lambda x: x.min() if np.isreal(x).all() else thres\n", | ||
| " )\n", | ||
| " df[\"dyn_stab\"] = ~np.logical_or(\n", | ||
| " df[\"eigval_min\"] < thres, df[\"freq_min\"] < thres\n", | ||
| " )\n", | ||
| " df[\"eigval_min\"] = df[\"eigenvalues\"].apply(lambda x: x.min() if np.isreal(x).all() else thres)\n", | ||
| " df[\"freq_min\"] = df[\"frequencies\"].apply(lambda x: x.min() if np.isreal(x).all() else thres)\n", | ||
| " df[\"dyn_stab\"] = ~np.logical_or(df[\"eigval_min\"] < thres, df[\"freq_min\"] < thres)\n", | ||
| "\n", | ||
| " arg = np.argsort(uids)\n", | ||
| " uids_sorted = np.array(uids)[arg]\n", | ||
| " stabilities_sorted = stabilities[arg]\n", | ||
| "\n", | ||
| " sorted_df = (\n", | ||
| " df[df[\"uid\"].isin(uids_sorted)].set_index(\"uid\").reindex(uids_sorted)\n", | ||
| " )\n", | ||
| " mask = ~(stabilities_sorted == None)\n", | ||
| " sorted_df = df[df[\"uid\"].isin(uids_sorted)].set_index(\"uid\").reindex(uids_sorted)\n", | ||
| " mask = ~(stabilities_sorted is None)\n", | ||
| "\n", | ||
| " y_true = stabilities_sorted[mask].astype(\"int\")\n", | ||
| " y_pred = sorted_df[\"dyn_stab\"][mask].fillna(-1).astype(\"int\")\n", | ||
|
Comment on lines
+188
to
191
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: python3 - <<'PY'
import json, pathlib, re
path = pathlib.Path("benchmarks/c2db/analysis.ipynb")
nb = json.loads(path.read_text())
for i, cell in enumerate(nb.get("cells", [])):
if cell.get("cell_type") != "code":
continue
src = "".join(cell.get("source", []))
if "stabilities_sorted" in src or "stabilities" in src:
print(f"\n=== cell {i} ===")
for j, line in enumerate(src.splitlines(), 1):
if "stabilities_sorted" in line or "stabilities" in line or "mask =" in line:
print(f"{j:03d}: {line}")
PYRepository: atomind-ai/mlip-arena Length of output: 767 🏁 Script executed: python3 - <<'PY'
import json, pathlib
path = pathlib.Path("benchmarks/c2db/analysis.ipynb")
nb = json.loads(path.read_text())
for idx in [4, 5]:
cell = nb["cells"][idx]
print(f"\n=== cell {idx} ===")
for j, line in enumerate("".join(cell.get("source", [])).splitlines(), 1):
print(f"{j:03d}: {line}")
PYRepository: atomind-ai/mlip-arena Length of output: 6197 Use a real boolean mask here. 🤖 Prompt for AI Agents |
||
| " cm = confusion_matrix(y_true, y_pred, labels=[1, 0, -1])\n", | ||
| "\n", | ||
| " ax = axs[plot_idx]\n", | ||
| " ConfusionMatrixDisplay(\n", | ||
| " cm, display_labels=[\"stable\", \"unstable\", \"missing\"]\n", | ||
| " ).plot(ax=ax, cmap=\"Blues\", colorbar=False)\n", | ||
| " ConfusionMatrixDisplay(cm, display_labels=[\"stable\", \"unstable\", \"missing\"]).plot(\n", | ||
| " ax=ax, cmap=\"Blues\", colorbar=False\n", | ||
| " )\n", | ||
| "\n", | ||
| " ax.set_title(model.name)\n", | ||
| " ax.set_xlabel(\"Predicted\")\n", | ||
|
|
@@ -223,7 +212,7 @@ | |
| "\n", | ||
| " # plt.tight_layout()\n", | ||
| " plt.savefig(\"c2db-confusion_matrices.pdf\", bbox_inches=\"tight\")\n", | ||
| " plt.show()\n" | ||
| " plt.show()" | ||
| ] | ||
| }, | ||
| { | ||
|
|
@@ -250,12 +239,8 @@ | |
| " continue\n", | ||
| " df = pd.read_parquet(fpath)\n", | ||
| "\n", | ||
| " df[\"eigval_min\"] = df[\"eigenvalues\"].apply(\n", | ||
| " lambda x: x.min() if np.isreal(x).all() else thres\n", | ||
| " )\n", | ||
| " df[\"freq_min\"] = df[\"frequencies\"].apply(\n", | ||
| " lambda x: x.min() if np.isreal(x).all() else thres\n", | ||
| " )\n", | ||
| " df[\"eigval_min\"] = df[\"eigenvalues\"].apply(lambda x: x.min() if np.isreal(x).all() else thres)\n", | ||
| " df[\"freq_min\"] = df[\"frequencies\"].apply(lambda x: x.min() if np.isreal(x).all() else thres)\n", | ||
| " df[\"dyn_stab\"] = ~np.logical_or(df[\"eigval_min\"] < thres, df[\"freq_min\"] < thres)\n", | ||
| "\n", | ||
| " arg = np.argsort(uids)\n", | ||
|
|
@@ -269,7 +254,7 @@ | |
| "\n", | ||
| " sorted_df = sorted_df.loc[uids]\n", | ||
| " # mask = ~np.logical_or(sorted_df['dyn_stab'].isna().values, stabilities == None)\n", | ||
| " mask = ~(stabilities == None)\n", | ||
| " mask = ~(stabilities is None)\n", | ||
| "\n", | ||
| " y_true = stabilities[mask].astype(\"int\")\n", | ||
| " y_pred = sorted_df[\"dyn_stab\"][mask].fillna(-1).astype(\"int\")\n", | ||
|
|
@@ -361,9 +346,7 @@ | |
| "\n", | ||
| " # Plot each metric with assigned color\n", | ||
| " for metric, pos in positions.items():\n", | ||
| " ax.bar(\n", | ||
| " pos, summary_df[metric], width=bar_width, label=metric, color=colors[metric]\n", | ||
| " )\n", | ||
| " ax.bar(pos, summary_df[metric], width=bar_width, label=metric, color=colors[metric])\n", | ||
| "\n", | ||
| " ax.set_xlabel(\"Model\")\n", | ||
| " ax.set_ylabel(\"F1 Score\")\n", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,15 @@ | ||
| # MD reactivity with hydrogen combustion (A.7) | ||
| # MD Reactivity with Hydrogen Combustion (A.7) | ||
|
|
||
| The workflow and analysis are defined in a single Prefect flow, which can be imported by | ||
| The workflow and analysis are defined in a single Prefect flow, which can be imported by: | ||
|
|
||
| ```python | ||
| from mlip_arena.tasks.combustion.flow import hydrogen_combustion | ||
| from mlip_arena.flows.combustion import hydrogen_combustion | ||
|
|
||
| # Run the flow | ||
| hydrogen_combustion( | ||
| run_dir=run_dir, | ||
| calculator=model, # MLIPEnum name, model name string, or ASE calculator object | ||
| ) | ||
| ``` | ||
| . See [run.ipynb](./run.ipynb) for detials. | ||
|
|
||
| See the [run.ipynb](./run.ipynb) notebook for details. | ||
|
Comment on lines
5
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make the example self-contained.
🛠 Suggested fix+from pathlib import Path
+
+run_dir = Path("./combustion")
+model = "NequIP-OAM-L"
+
from mlip_arena.flows.combustion import hydrogen_combustion🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
These negated patterns will not re-include files under
benchmarks/.Once
benchmarks/is ignored, Git stops descending into it, so the later!benchmarks/...rules never make the combustion/parquet artifacts trackable. That breaks the new artifact contract used by report generation.Suggested fix
Based on learnings, the broad
benchmarks/ignore is intentional; the issue here is that Git will not unignore descendants of an ignored parent unless the directories are reopened first.📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Learnings