Spaces:
Running
Running
github-actions[ci]
commited on
Commit
·
11ac28c
0
Parent(s):
Clean sync from main branch - 2025-07-05 06:02:08
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .devcontainer/devcontainer.json +3 -0
- .gitattributes +12 -0
- .github/README.md +198 -0
- .github/workflows/release.yaml +96 -0
- .github/workflows/sync-hf.yaml +39 -0
- .github/workflows/test.yaml +103 -0
- .gitignore +169 -0
- .streamlit/config.toml +2 -0
- CITATION.cff +23 -0
- LICENSE +201 -0
- README.md +14 -0
- benchmarks/bzo/dft.ipynb +0 -0
- benchmarks/bzo/pbe/mode-1.npy +0 -0
- benchmarks/bzo/pbe/phonopy_params.yaml +0 -0
- benchmarks/c2db/ALIGNN.parquet +3 -0
- benchmarks/c2db/CHGNet.parquet +3 -0
- benchmarks/c2db/M3GNet.parquet +3 -0
- benchmarks/c2db/MACE-MP(M).parquet +3 -0
- benchmarks/c2db/MACE-MPA.parquet +3 -0
- benchmarks/c2db/MatterSim.parquet +3 -0
- benchmarks/c2db/ORBv2.parquet +3 -0
- benchmarks/c2db/SevenNet.parquet +3 -0
- benchmarks/c2db/analysis.ipynb +408 -0
- benchmarks/c2db/c2db-confusion_matrices.pdf +3 -0
- benchmarks/c2db/c2db-f1_bar.pdf +3 -0
- benchmarks/c2db/c2db.db +3 -0
- benchmarks/c2db/copy.parquet +3 -0
- benchmarks/c2db/run.py +213 -0
- benchmarks/energy_conservation/run.py +214 -0
- benchmarks/eos_alloy/run_Fe-Ni-Cr.ipynb +0 -0
- benchmarks/eos_bulk/CHGNet.parquet +3 -0
- benchmarks/eos_bulk/CHGNet_processed.parquet +3 -0
- benchmarks/eos_bulk/M3GNet.parquet +3 -0
- benchmarks/eos_bulk/M3GNet_processed.parquet +3 -0
- benchmarks/eos_bulk/MACE-MP(M).parquet +3 -0
- benchmarks/eos_bulk/MACE-MP(M)_processed.parquet +3 -0
- benchmarks/eos_bulk/MACE-MPA.parquet +3 -0
- benchmarks/eos_bulk/MACE-MPA_processed.parquet +3 -0
- benchmarks/eos_bulk/MatterSim.parquet +3 -0
- benchmarks/eos_bulk/MatterSim_processed.parquet +3 -0
- benchmarks/eos_bulk/ORBv2.parquet +3 -0
- benchmarks/eos_bulk/ORBv2_processed.parquet +3 -0
- benchmarks/eos_bulk/SevenNet.parquet +3 -0
- benchmarks/eos_bulk/SevenNet_processed.parquet +3 -0
- benchmarks/eos_bulk/analyze.py +223 -0
- benchmarks/eos_bulk/eSEN.parquet +3 -0
- benchmarks/eos_bulk/eSEN_processed.parquet +3 -0
- benchmarks/eos_bulk/plot.py +119 -0
- benchmarks/eos_bulk/preprocessing.py +12 -0
- benchmarks/eos_bulk/run.py +170 -0
.devcontainer/devcontainer.json
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a525cdb835f1b6c36c5d09b1663e2dc0b2e5a40b97214fc9ee2fc0366b9df622
|
3 |
+
size 986
|
.gitattributes
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.json filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.db filter=lfs diff=lfs merge=lfs -text
|
4 |
+
examples/mof/classification/SevenNet.pkl filter=lfs diff=lfs merge=lfs -text
|
5 |
+
examples/mof/classification/input.pkl filter=lfs diff=lfs merge=lfs -text
|
6 |
+
examples/mof/classification/M3GNet.pkl filter=lfs diff=lfs merge=lfs -text
|
7 |
+
examples/mof/classification/MACE-MPA.pkl filter=lfs diff=lfs merge=lfs -text
|
8 |
+
examples/mof/classification/MACE-MP(M).pkl filter=lfs diff=lfs merge=lfs -text
|
9 |
+
examples/mof/classification/MatterSim.pkl filter=lfs diff=lfs merge=lfs -text
|
10 |
+
examples/mof/classification/ORBv2.pkl filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.pdf filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
.github/README.md
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div align="center">
|
2 |
+
<h1>⚔️ MLIP Arena ⚔️</h1>
|
3 |
+
<a href="https://openreview.net/forum?id=ysKfIavYQE#discussion"><img alt="Static Badge" src="https://img.shields.io/badge/ICLR-AI4Mat-blue"></a>
|
4 |
+
<a href="https://huggingface.co/spaces/atomind/mlip-arena"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Space-blue" alt="Hugging Face"></a>
|
5 |
+
<a href="https://github.com/atomind-ai/mlip-arena/actions"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/atomind-ai/mlip-arena/test.yaml"></a>
|
6 |
+
<a href="https://pypi.org/project/mlip-arena/"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/mlip-arena"></a>
|
7 |
+
<a href="https://pypi.org/project/mlip-arena/"><img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/mlip-arena"></a>
|
8 |
+
<a href="https://zenodo.org/doi/10.5281/zenodo.13704399"><img src="https://zenodo.org/badge/776930320.svg" alt="DOI"></a>
|
9 |
+
<!-- <a href="https://discord.gg/W8WvdQtT8T"><img alt="Discord" src="https://img.shields.io/discord/1299613474820984832?logo=discord"> -->
|
10 |
+
</a>
|
11 |
+
</div>
|
12 |
+
|
13 |
+
Foundation machine learning interatomic potentials (MLIPs), trained on extensive databases containing millions of density functional theory (DFT) calculations, have revolutionized molecular and materials modeling, but existing benchmarks suffer from data leakage, limited transferability, and an over-reliance on error-based metrics tied to specific density functional theory (DFT) references.
|
14 |
+
|
15 |
+
We introduce MLIP Arena, a unified benchmark platform for evaluating foundation MLIP performance beyond conventional error metrics. It focuses on revealing the physical soundness learned by MLIPs and assessing their utilitarian performance agnostic to underlying model architecture and training dataset.
|
16 |
+
|
17 |
+
***By moving beyond static DFT references and revealing the important failure modes*** of current foundation MLIPs in real-world settings, MLIP Arena provides a reproducible framework to guide the next-generation MLIP development toward improved predictive accuracy and runtime efficiency while maintaining physical consistency.
|
18 |
+
|
19 |
+
MLIP Arena leverages modern pythonic workflow orchestrator 💙
|
20 |
+
[Prefect](https://www.prefect.io/) 💙
|
21 |
+
to enable advanced task/flow chaining and caching.
|
22 |
+
|
23 |
+

|
24 |
+
|
25 |
+
> [!NOTE]
|
26 |
+
> Contributions of new tasks through PRs are very welcome! If you're interested in joining the effort, please reach out to Yuan at [[email protected]](mailto:[email protected]). See [project page](https://github.com/orgs/atomind-ai/projects/1) for some outstanding tasks, or propose new feature requests in [Discussion](https://github.com/atomind-ai/mlip-arena/discussions/new?category=ideas).
|
27 |
+
|
28 |
+
## Announcement
|
29 |
+
|
30 |
+
- **[April 8, 2025]** [🎉 **MLIP Arena is accepted as an ICLR AI4Mat Spotlight!** 🎉](https://openreview.net/forum?id=ysKfIavYQE#discussion) Huge thanks to all co-authors for their contributions!
|
31 |
+
|
32 |
+
|
33 |
+
## Installation
|
34 |
+
|
35 |
+
### From PyPI (prefect workflow only, without pretrained models)
|
36 |
+
|
37 |
+
```bash
|
38 |
+
pip install mlip-arena
|
39 |
+
```
|
40 |
+
|
41 |
+
### From source (with integrated pretrained models, advanced)
|
42 |
+
|
43 |
+
> [!CAUTION]
|
44 |
+
> We strongly recommend clean build in a new virtual environment due to the compatibility issues between multiple popular MLIPs. We provide a single installation script using `uv` for minimal package conflicts and fast installation!
|
45 |
+
|
46 |
+
> [!CAUTION]
|
47 |
+
> To automatically download farichem OMat24 checkpoint, please make sure you have gained downloading access to their HuggingFace [***model repo***](https://huggingface.co/facebook/OMAT24) (not dataset repo), and login locally on your machine through `huggginface-cli login` (see [HF hub authentication](https://huggingface.co/docs/huggingface_hub/en/quick-start#authentication))
|
48 |
+
|
49 |
+
**Linux**
|
50 |
+
|
51 |
+
```bash
|
52 |
+
# (Optional) Install uv, way faster than pip, why not? :)
|
53 |
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
54 |
+
source $HOME/.local/bin/env
|
55 |
+
|
56 |
+
git clone https://github.com/atomind-ai/mlip-arena.git
|
57 |
+
cd mlip-arena
|
58 |
+
|
59 |
+
# One script uv pip installation
|
60 |
+
bash scripts/install.sh
|
61 |
+
```
|
62 |
+
|
63 |
+
> [!TIP]
|
64 |
+
> Sometimes installing all compiled models takes all the available local storage. Optional pip flag `--no-cache` could be uesed. `uv cache clean` will be helpful too.
|
65 |
+
|
66 |
+
**Mac**
|
67 |
+
|
68 |
+
```bash
|
69 |
+
# (Optional) Install uv
|
70 |
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
71 |
+
source $HOME/.local/bin/env
|
72 |
+
# One script uv pip installation
|
73 |
+
bash scripts/install-macosx.sh
|
74 |
+
```
|
75 |
+
|
76 |
+
## Quickstart
|
77 |
+
|
78 |
+
### The first example: Molecular Dynamics
|
79 |
+
|
80 |
+
Arena provides a unified interface to run all the compiled MLIPs. This can be achieved simply by looping through `MLIPEnum`:
|
81 |
+
|
82 |
+
```python
|
83 |
+
from mlip_arena.models import MLIPEnum
|
84 |
+
from mlip_arena.tasks import MD
|
85 |
+
from mlip_arena.tasks.utils import get_calculator
|
86 |
+
|
87 |
+
from ase import units
|
88 |
+
from ase.build import bulk
|
89 |
+
|
90 |
+
atoms = bulk("Cu", "fcc", a=3.6) * (5, 5, 5)
|
91 |
+
|
92 |
+
results = []
|
93 |
+
|
94 |
+
for model in MLIPEnum:
|
95 |
+
result = MD(
|
96 |
+
atoms=atoms,
|
97 |
+
calculator=get_calculator(
|
98 |
+
model,
|
99 |
+
calculator_kwargs=dict(), # passing into calculator
|
100 |
+
dispersion=True,
|
101 |
+
dispersion_kwargs=dict(
|
102 |
+
damping='bj', xc='pbe', cutoff=40.0 * units.Bohr
|
103 |
+
), # passing into TorchDFTD3Calculator
|
104 |
+
), # compatible with custom ASE Calculator
|
105 |
+
ensemble="nve", # nvt, nvt available
|
106 |
+
dynamics="velocityverlet", # compatible with any ASE Dynamics objects and their class names
|
107 |
+
total_time=1e3, # 1 ps = 1e3 fs
|
108 |
+
time_step=2, # fs
|
109 |
+
)
|
110 |
+
results.append(result)
|
111 |
+
```
|
112 |
+
|
113 |
+
### 🚀 Parallelize Benchmarks at Scale
|
114 |
+
|
115 |
+
To run multiple benchmarks in parallel, add `.submit` before the task function and wrap all the tasks into a flow to dispatch the tasks to worker for concurrent execution. See Prefect Doc on [tasks](https://docs.prefect.io/v3/develop/write-tasks) and [flow](https://docs.prefect.io/v3/develop/write-flows) for more details.
|
116 |
+
|
117 |
+
```python
|
118 |
+
...
|
119 |
+
from prefect import flow
|
120 |
+
|
121 |
+
@flow
|
122 |
+
def run_all_tasks:
|
123 |
+
|
124 |
+
futures = []
|
125 |
+
for model in MLIPEnum:
|
126 |
+
future = MD.submit(
|
127 |
+
atoms=atoms,
|
128 |
+
...
|
129 |
+
)
|
130 |
+
future.append(future)
|
131 |
+
|
132 |
+
return [f.result(raise_on_failure=False) for f in futures]
|
133 |
+
```
|
134 |
+
|
135 |
+
For a more practical example using HPC resources, please now refer to [MD stability benchmark](../benchmarks/stability/temperature.ipynb).
|
136 |
+
|
137 |
+
### List of implemented tasks
|
138 |
+
|
139 |
+
The implemented tasks are available under `mlip_arena.tasks.<module>.run` or `from mlip_arena.tasks import *` for convenient imports (currently doesn't work if [phonopy](https://phonopy.github.io/phonopy/install.html) is not installed).
|
140 |
+
|
141 |
+
- [OPT](../mlip_arena/tasks/optimize.py#L56): Structure optimization
|
142 |
+
- [EOS](../mlip_arena/tasks/eos.py#L42): Equation of state (energy-volume scan)
|
143 |
+
- [MD](../mlip_arena/tasks/md.py#L200): Molecular dynamics with flexible dynamics (NVE, NVT, NPT) and temperature/pressure scheduling (annealing, shearing, *etc*)
|
144 |
+
- [PHONON](../mlip_arena/tasks/phonon.py#L110): Phonon calculation driven by [phonopy](https://phonopy.github.io/phonopy/install.html)
|
145 |
+
- [NEB](../mlip_arena/tasks/neb.py#L96): Nudged elastic band
|
146 |
+
- [NEB_FROM_ENDPOINTS](../mlip_arena/tasks/neb.py#L164): Nudge elastic band with convenient image interpolation (linear or IDPP)
|
147 |
+
- [ELASTICITY](../mlip_arena/tasks/elasticity.py#L78): Elastic tensor calculation
|
148 |
+
|
149 |
+
### Contribute and Development
|
150 |
+
|
151 |
+
PRs are welcome. Please clone the repo and submit PRs with changes.
|
152 |
+
|
153 |
+
To make change to huggingface space, fetch large files from git lfs first and run streamlit:
|
154 |
+
|
155 |
+
```
|
156 |
+
git lfs fetch --all
|
157 |
+
git lfs pull
|
158 |
+
streamlit run serve/app.py
|
159 |
+
```
|
160 |
+
|
161 |
+
### Add new benchmark tasks (WIP)
|
162 |
+
|
163 |
+
> [!NOTE]
|
164 |
+
> Please reuse, extend, or chain the general tasks defined [above](#list-of-implemented-tasks)
|
165 |
+
|
166 |
+
### Add new MLIP models
|
167 |
+
|
168 |
+
If you have pretrained MLIP models that you would like to contribute to the MLIP Arena and show benchmark in real-time, there are two ways:
|
169 |
+
|
170 |
+
#### External ASE Calculator (easy)
|
171 |
+
|
172 |
+
1. Implement new ASE Calculator class in [mlip_arena/models/externals](../mlip_arena/models/externals).
|
173 |
+
2. Name your class with awesome model name and add the same name to [registry](../mlip_arena/models/registry.yaml) with metadata.
|
174 |
+
|
175 |
+
> [!CAUTION]
|
176 |
+
> Remove unneccessary outputs under `results` class attributes to avoid error for MD simulations. Please refer to [CHGNet](../mlip_arena/models/externals/chgnet.py) as an example.
|
177 |
+
|
178 |
+
#### Hugging Face Model (recommended, difficult)
|
179 |
+
|
180 |
+
0. Inherit Hugging Face [ModelHubMixin](https://huggingface.co/docs/huggingface_hub/en/package_reference/mixins) class to your awesome model class definition. We recommend [PytorchModelHubMixin](https://huggingface.co/docs/huggingface_hub/en/package_reference/mixins#huggingface_hub.PyTorchModelHubMixin).
|
181 |
+
1. Create a new [Hugging Face Model](https://huggingface.co/new) repository and upload the model file using [push_to_hub function](https://huggingface.co/docs/huggingface_hub/en/package_reference/mixins#huggingface_hub.ModelHubMixin.push_to_hub).
|
182 |
+
2. Follow the template to code the I/O interface for your model [here](../mlip_arena/models/README.md).
|
183 |
+
3. Update model [registry](../mlip_arena/models/registry.yaml) with metadata
|
184 |
+
|
185 |
+
## Citation
|
186 |
+
|
187 |
+
If you find the work useful, please consider citing the following:
|
188 |
+
|
189 |
+
```bibtex
|
190 |
+
@inproceedings{
|
191 |
+
chiang2025mlip,
|
192 |
+
title={{MLIP} Arena: Advancing Fairness and Transparency in Machine Learning Interatomic Potentials through an Open and Accessible Benchmark Platform},
|
193 |
+
author={Yuan Chiang and Tobias Kreiman and Elizabeth Weaver and Ishan Amin and Matthew Kuner and Christine Zhang and Aaron Kaplan and Daryl Chrzan and Samuel M Blau and Aditi S. Krishnapriyan and Mark Asta},
|
194 |
+
booktitle={AI for Accelerated Materials Design - ICLR 2025},
|
195 |
+
year={2025},
|
196 |
+
url={https://openreview.net/forum?id=ysKfIavYQE}
|
197 |
+
}
|
198 |
+
```
|
.github/workflows/release.yaml
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Publish Release
|
2 |
+
|
3 |
+
on:
|
4 |
+
workflow_dispatch:
|
5 |
+
|
6 |
+
permissions:
|
7 |
+
contents: write # Ensure write access to push tags
|
8 |
+
|
9 |
+
jobs:
|
10 |
+
pypi:
|
11 |
+
name: Publish to PyPI
|
12 |
+
runs-on: ubuntu-latest
|
13 |
+
|
14 |
+
steps:
|
15 |
+
# Step 1: Checkout the code
|
16 |
+
- name: Checkout code
|
17 |
+
uses: actions/checkout@v3
|
18 |
+
|
19 |
+
# Step 2: Set up Python
|
20 |
+
- name: Set up Python
|
21 |
+
uses: actions/setup-python@v4
|
22 |
+
with:
|
23 |
+
python-version: '3.x'
|
24 |
+
|
25 |
+
# Step 3: Install dependencies
|
26 |
+
- name: Install dependencies
|
27 |
+
run: pip install toml requests
|
28 |
+
|
29 |
+
# Step 4: Extract current version from pyproject.toml
|
30 |
+
- name: Extract current version
|
31 |
+
id: get_version
|
32 |
+
run: |
|
33 |
+
VERSION=$(python -c "import toml; print(toml.load('pyproject.toml')['project']['version'])")
|
34 |
+
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
35 |
+
|
36 |
+
# Step 5: Get latest version from PyPI
|
37 |
+
- name: Get latest version from PyPI
|
38 |
+
id: get_pypi_version
|
39 |
+
run: |
|
40 |
+
LATEST_PYPI_VERSION=$(python -c "import toml; import requests; PACKAGE_NAME = toml.load('pyproject.toml')['project']['name']; response = requests.get(f'https://pypi.org/pypi/{PACKAGE_NAME}/json'); print(response.json()['info']['version'])")
|
41 |
+
echo "LATEST_PYPI_VERSION=$LATEST_PYPI_VERSION" >> $GITHUB_ENV
|
42 |
+
|
43 |
+
# Step 6: Compare current version with the latest tag
|
44 |
+
- name: Check if version is bumped
|
45 |
+
id: check_version
|
46 |
+
run: |
|
47 |
+
if [ "${{ env.VERSION }}" = "${{ env.LATEST_PYPI_VERSION }}" ]; then
|
48 |
+
echo "Version not bumped. Exiting."
|
49 |
+
echo "version_bumped=false" >> $GITHUB_ENV
|
50 |
+
else
|
51 |
+
echo "Version bumped. Proceeding."
|
52 |
+
echo "version_bumped=true" >> $GITHUB_ENV
|
53 |
+
fi
|
54 |
+
|
55 |
+
# Step 5: Remove problematic optional dependencies
|
56 |
+
- name: Strip problematic optional dependencies
|
57 |
+
run: |
|
58 |
+
python - <<EOF
|
59 |
+
import toml
|
60 |
+
from pathlib import Path
|
61 |
+
|
62 |
+
pyproject_path = Path("pyproject.toml")
|
63 |
+
data = toml.loads(pyproject_path.read_text())
|
64 |
+
|
65 |
+
# Process optional dependencies
|
66 |
+
optional_deps = data.get("project", {}).get("optional-dependencies", {})
|
67 |
+
for key, deps in optional_deps.items():
|
68 |
+
new_deps = []
|
69 |
+
for dep in deps:
|
70 |
+
if "@git" in dep:
|
71 |
+
dep = dep.split("@git")[0].strip() # Remove everything after "@git"
|
72 |
+
new_deps.append(dep)
|
73 |
+
optional_deps[key] = new_deps
|
74 |
+
|
75 |
+
pyproject_path.write_text(toml.dumps(data))
|
76 |
+
EOF
|
77 |
+
|
78 |
+
# Step 7: Install Flit (only if version bumped)
|
79 |
+
- name: Install Flit
|
80 |
+
if: env.version_bumped == 'true'
|
81 |
+
run: pip install flit
|
82 |
+
|
83 |
+
# Step 8: Create .pypirc file (only if version bumped)
|
84 |
+
- name: Create .pypirc file
|
85 |
+
if: env.version_bumped == 'true'
|
86 |
+
run: |
|
87 |
+
echo "[pypi]" > ~/.pypirc
|
88 |
+
echo "username = __token__" >> ~/.pypirc
|
89 |
+
echo "password = ${{ secrets.PYPI_API_TOKEN }}" >> ~/.pypirc
|
90 |
+
|
91 |
+
# Step 9: Build and publish package (only if version bumped)
|
92 |
+
- name: Build and Publish Package
|
93 |
+
if: env.version_bumped == 'true'
|
94 |
+
run: |
|
95 |
+
flit build
|
96 |
+
flit publish
|
.github/workflows/sync-hf.yaml
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Sync to Hugging Face hub
|
2 |
+
|
3 |
+
on:
|
4 |
+
workflow_run:
|
5 |
+
workflows: [Python Test]
|
6 |
+
branches: [main]
|
7 |
+
types: [completed]
|
8 |
+
workflow_dispatch:
|
9 |
+
|
10 |
+
jobs:
|
11 |
+
sync-to-hub:
|
12 |
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
13 |
+
runs-on: ubuntu-latest
|
14 |
+
steps:
|
15 |
+
- uses: actions/checkout@v4
|
16 |
+
with:
|
17 |
+
fetch-depth: 0
|
18 |
+
lfs: true
|
19 |
+
|
20 |
+
- name: Push to hub
|
21 |
+
env:
|
22 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
23 |
+
run: |
|
24 |
+
# Configure Git user identity
|
25 |
+
git config user.name "github-actions[ci]"
|
26 |
+
git config user.email "github-actions[ci]@users.noreply.github.com"
|
27 |
+
|
28 |
+
# Configure LFS tracking
|
29 |
+
git lfs track "*.pdf"
|
30 |
+
git lfs track "*.png"
|
31 |
+
|
32 |
+
# Create a new orphan branch (no history)
|
33 |
+
git checkout --orphan hf-clean
|
34 |
+
|
35 |
+
git add .
|
36 |
+
git commit -m "Clean sync from main branch - $(date '+%Y-%m-%d %H:%M:%S')"
|
37 |
+
|
38 |
+
# Force push to Hugging Face main branch
|
39 |
+
git push -f https://HF_USERNAME:[email protected]/spaces/atomind/mlip-arena hf-clean:main
|
.github/workflows/test.yaml
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Python Test
|
2 |
+
|
3 |
+
on:
|
4 |
+
push:
|
5 |
+
branches: [main]
|
6 |
+
pull_request:
|
7 |
+
branches: [main]
|
8 |
+
|
9 |
+
env:
|
10 |
+
UV_SYSTEM_PYTHON: 1
|
11 |
+
|
12 |
+
jobs:
|
13 |
+
test:
|
14 |
+
runs-on: ubuntu-latest
|
15 |
+
|
16 |
+
strategy:
|
17 |
+
matrix:
|
18 |
+
python-version: ["3.10", "3.11", "3.12"]
|
19 |
+
|
20 |
+
steps:
|
21 |
+
- name: Checkout PR with full history
|
22 |
+
uses: actions/checkout@v4
|
23 |
+
with:
|
24 |
+
fetch-depth: 0
|
25 |
+
|
26 |
+
- name: Install uv
|
27 |
+
uses: astral-sh/setup-uv@v6
|
28 |
+
with:
|
29 |
+
enable-cache: true
|
30 |
+
cache-dependency-glob: "pyproject.toml"
|
31 |
+
|
32 |
+
- name: Set up Python ${{ matrix.python-version }}
|
33 |
+
uses: actions/setup-python@v5
|
34 |
+
with:
|
35 |
+
python-version: ${{ matrix.python-version }}
|
36 |
+
|
37 |
+
- name: Install dependencies
|
38 |
+
run: bash scripts/install-linux.sh
|
39 |
+
|
40 |
+
- name: List dependencies
|
41 |
+
run: pip list
|
42 |
+
|
43 |
+
- name: Login to Hugging Face
|
44 |
+
env:
|
45 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN_READ_ONLY }}
|
46 |
+
run: huggingface-cli login --token $HF_TOKEN
|
47 |
+
|
48 |
+
- name: Run tests
|
49 |
+
env:
|
50 |
+
PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }}
|
51 |
+
PREFECT_API_URL: ${{ secrets.PREFECT_API_URL }}
|
52 |
+
run: pytest -vra -n 5 --dist=loadscope tests
|
53 |
+
|
54 |
+
- name: Squash commits and trial push to Hugging Face
|
55 |
+
if: github.event_name == 'pull_request'
|
56 |
+
id: trial_push
|
57 |
+
env:
|
58 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
59 |
+
TRIAL_BRANCH: trial-sync-${{ github.sha }}-${{ matrix.python-version }}
|
60 |
+
run: |
|
61 |
+
# Configure Git user identity
|
62 |
+
git config user.name "github-actions[ci]"
|
63 |
+
git config user.email "github-actions[ci]@users.noreply.github.com"
|
64 |
+
|
65 |
+
# Install Git LFS
|
66 |
+
sudo apt-get update
|
67 |
+
sudo apt-get install -y git-lfs
|
68 |
+
git lfs install
|
69 |
+
|
70 |
+
# Configure LFS tracking for binary files (only for HF push)
|
71 |
+
git lfs track "*.pdf"
|
72 |
+
git lfs track "*.png"
|
73 |
+
|
74 |
+
git add .gitattributes
|
75 |
+
|
76 |
+
# Setup LFS for the remote
|
77 |
+
git lfs fetch
|
78 |
+
git lfs checkout
|
79 |
+
|
80 |
+
# Rebase and squash all PR commits into one
|
81 |
+
BASE=$(git merge-base origin/main HEAD)
|
82 |
+
git reset --soft $BASE
|
83 |
+
|
84 |
+
# Re-add all files (binary files will now be tracked by LFS)
|
85 |
+
git add .
|
86 |
+
git commit -m "Squashed commit from PR #${{ github.event.pull_request.number }}"
|
87 |
+
|
88 |
+
# Create a new orphan branch (no history)
|
89 |
+
git checkout --orphan hf-clean
|
90 |
+
|
91 |
+
git add .
|
92 |
+
git commit -m "Clean sync from main branch - $(date '+%Y-%m-%d %H:%M:%S')"
|
93 |
+
|
94 |
+
# Push to temporary branch on Hugging Face
|
95 |
+
git push -f https://HF_USERNAME:[email protected]/spaces/atomind/mlip-arena HEAD:refs/heads/$TRIAL_BRANCH
|
96 |
+
|
97 |
+
- name: Delete trial branch from Hugging Face
|
98 |
+
if: steps.trial_push.outcome == 'success'
|
99 |
+
env:
|
100 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
101 |
+
TRIAL_BRANCH: trial-sync-${{ github.sha }}-${{ matrix.python-version }}
|
102 |
+
run: |
|
103 |
+
git push https://HF_USERNAME:[email protected]/spaces/atomind/mlip-arena --delete $TRIAL_BRANCH || true
|
.gitignore
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.out
|
2 |
+
*.extxyz
|
3 |
+
*.traj
|
4 |
+
mlip_arena/tasks/*/
|
5 |
+
benchmarks/
|
6 |
+
lab/
|
7 |
+
manuscripts/
|
8 |
+
datasets/
|
9 |
+
|
10 |
+
# Byte-compiled / optimized / DLL files
|
11 |
+
__pycache__/
|
12 |
+
*.py[cod]
|
13 |
+
*$py.class
|
14 |
+
|
15 |
+
# C extensions
|
16 |
+
*.so
|
17 |
+
|
18 |
+
# Distribution / packaging
|
19 |
+
.Python
|
20 |
+
build/
|
21 |
+
develop-eggs/
|
22 |
+
dist/
|
23 |
+
downloads/
|
24 |
+
eggs/
|
25 |
+
.eggs/
|
26 |
+
lib/
|
27 |
+
lib64/
|
28 |
+
parts/
|
29 |
+
sdist/
|
30 |
+
var/
|
31 |
+
wheels/
|
32 |
+
share/python-wheels/
|
33 |
+
*.egg-info/
|
34 |
+
.installed.cfg
|
35 |
+
*.egg
|
36 |
+
MANIFEST
|
37 |
+
|
38 |
+
# PyInstaller
|
39 |
+
# Usually these files are written by a python script from a template
|
40 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
41 |
+
*.manifest
|
42 |
+
*.spec
|
43 |
+
|
44 |
+
# Installer logs
|
45 |
+
pip-log.txt
|
46 |
+
pip-delete-this-directory.txt
|
47 |
+
|
48 |
+
# Unit test / coverage reports
|
49 |
+
htmlcov/
|
50 |
+
.tox/
|
51 |
+
.nox/
|
52 |
+
.coverage
|
53 |
+
.coverage.*
|
54 |
+
.cache
|
55 |
+
nosetests.xml
|
56 |
+
coverage.xml
|
57 |
+
*.cover
|
58 |
+
*.py,cover
|
59 |
+
.hypothesis/
|
60 |
+
.pytest_cache/
|
61 |
+
cover/
|
62 |
+
|
63 |
+
# Translations
|
64 |
+
*.mo
|
65 |
+
*.pot
|
66 |
+
|
67 |
+
# Django stuff:
|
68 |
+
*.log
|
69 |
+
local_settings.py
|
70 |
+
db.sqlite3
|
71 |
+
db.sqlite3-journal
|
72 |
+
|
73 |
+
# Flask stuff:
|
74 |
+
instance/
|
75 |
+
.webassets-cache
|
76 |
+
|
77 |
+
# Scrapy stuff:
|
78 |
+
.scrapy
|
79 |
+
|
80 |
+
# Sphinx documentation
|
81 |
+
docs/_build/
|
82 |
+
|
83 |
+
# PyBuilder
|
84 |
+
.pybuilder/
|
85 |
+
target/
|
86 |
+
|
87 |
+
# Jupyter Notebook
|
88 |
+
.ipynb_checkpoints
|
89 |
+
|
90 |
+
# IPython
|
91 |
+
profile_default/
|
92 |
+
ipython_config.py
|
93 |
+
|
94 |
+
# pyenv
|
95 |
+
# For a library or package, you might want to ignore these files since the code is
|
96 |
+
# intended to run in multiple environments; otherwise, check them in:
|
97 |
+
# .python-version
|
98 |
+
|
99 |
+
# pipenv
|
100 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
101 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
102 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
103 |
+
# install all needed dependencies.
|
104 |
+
#Pipfile.lock
|
105 |
+
|
106 |
+
# poetry
|
107 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
108 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
109 |
+
# commonly ignored for libraries.
|
110 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
111 |
+
#poetry.lock
|
112 |
+
|
113 |
+
# pdm
|
114 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
115 |
+
#pdm.lock
|
116 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
117 |
+
# in version control.
|
118 |
+
# https://pdm.fming.dev/#use-with-ide
|
119 |
+
.pdm.toml
|
120 |
+
|
121 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
122 |
+
__pypackages__/
|
123 |
+
|
124 |
+
# Celery stuff
|
125 |
+
celerybeat-schedule
|
126 |
+
celerybeat.pid
|
127 |
+
|
128 |
+
# SageMath parsed files
|
129 |
+
*.sage.py
|
130 |
+
|
131 |
+
# Environments
|
132 |
+
.env
|
133 |
+
.venv
|
134 |
+
env/
|
135 |
+
venv/
|
136 |
+
ENV/
|
137 |
+
env.bak/
|
138 |
+
venv.bak/
|
139 |
+
|
140 |
+
# Spyder project settings
|
141 |
+
.spyderproject
|
142 |
+
.spyproject
|
143 |
+
|
144 |
+
# Rope project settings
|
145 |
+
.ropeproject
|
146 |
+
|
147 |
+
# mkdocs documentation
|
148 |
+
/site
|
149 |
+
|
150 |
+
# mypy
|
151 |
+
.mypy_cache/
|
152 |
+
.dmypy.json
|
153 |
+
dmypy.json
|
154 |
+
|
155 |
+
# Pyre type checker
|
156 |
+
.pyre/
|
157 |
+
|
158 |
+
# pytype static type analyzer
|
159 |
+
.pytype/
|
160 |
+
|
161 |
+
# Cython debug symbols
|
162 |
+
cython_debug/
|
163 |
+
|
164 |
+
# PyCharm
|
165 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
166 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
167 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
168 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
169 |
+
#.idea/
|
.streamlit/config.toml
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
[server]
|
2 |
+
fileWatcherType = "poll"
|
CITATION.cff
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This CITATION.cff file was generated with cffinit.
|
2 |
+
# Visit https://bit.ly/cffinit to generate yours today!
|
3 |
+
|
4 |
+
cff-version: 1.2.0
|
5 |
+
title: MLIP Arena
|
6 |
+
message: >-
|
7 |
+
If you use this software, please cite it using the
|
8 |
+
metadata from this file.
|
9 |
+
type: software
|
10 |
+
authors:
|
11 |
+
- given-names: Yuan
|
12 |
+
family-names: Chiang
|
13 |
+
email: [email protected]
|
14 |
+
affiliation: Lawrence Berkeley National Laboratory
|
15 |
+
orcid: 'https://orcid.org/0000-0002-4017-7084'
|
16 |
+
repository-code: 'https://github.com/atomind-ai/mlip-arena'
|
17 |
+
keywords:
|
18 |
+
- Quantum Chemistry
|
19 |
+
- Foundation Model
|
20 |
+
- Interatomic Potentials
|
21 |
+
- Machine Learning
|
22 |
+
- Force Fields
|
23 |
+
license: Apache-2.0
|
LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Apache License
|
2 |
+
Version 2.0, January 2004
|
3 |
+
http://www.apache.org/licenses/
|
4 |
+
|
5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
+
|
7 |
+
1. Definitions.
|
8 |
+
|
9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
+
|
12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
+
the copyright owner that is granting the License.
|
14 |
+
|
15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
+
other entities that control, are controlled by, or are under common
|
17 |
+
control with that entity. For the purposes of this definition,
|
18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
19 |
+
direction or management of such entity, whether by contract or
|
20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
+
|
23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
+
exercising permissions granted by this License.
|
25 |
+
|
26 |
+
"Source" form shall mean the preferred form for making modifications,
|
27 |
+
including but not limited to software source code, documentation
|
28 |
+
source, and configuration files.
|
29 |
+
|
30 |
+
"Object" form shall mean any form resulting from mechanical
|
31 |
+
transformation or translation of a Source form, including but
|
32 |
+
not limited to compiled object code, generated documentation,
|
33 |
+
and conversions to other media types.
|
34 |
+
|
35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
36 |
+
Object form, made available under the License, as indicated by a
|
37 |
+
copyright notice that is included in or attached to the work
|
38 |
+
(an example is provided in the Appendix below).
|
39 |
+
|
40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
+
form, that is based on (or derived from) the Work and for which the
|
42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
44 |
+
of this License, Derivative Works shall not include works that remain
|
45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
+
the Work and Derivative Works thereof.
|
47 |
+
|
48 |
+
"Contribution" shall mean any work of authorship, including
|
49 |
+
the original version of the Work and any modifications or additions
|
50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
+
means any form of electronic, verbal, or written communication sent
|
55 |
+
to the Licensor or its representatives, including but not limited to
|
56 |
+
communication on electronic mailing lists, source code control systems,
|
57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
59 |
+
excluding communication that is conspicuously marked or otherwise
|
60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
+
|
62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
64 |
+
subsequently incorporated within the Work.
|
65 |
+
|
66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
71 |
+
Work and such Derivative Works in Source or Object form.
|
72 |
+
|
73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
+
(except as stated in this section) patent license to make, have made,
|
77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
+
where such license applies only to those patent claims licensable
|
79 |
+
by such Contributor that are necessarily infringed by their
|
80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
82 |
+
institute patent litigation against any entity (including a
|
83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
+
or a Contribution incorporated within the Work constitutes direct
|
85 |
+
or contributory patent infringement, then any patent licenses
|
86 |
+
granted to You under this License for that Work shall terminate
|
87 |
+
as of the date such litigation is filed.
|
88 |
+
|
89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
+
Work or Derivative Works thereof in any medium, with or without
|
91 |
+
modifications, and in Source or Object form, provided that You
|
92 |
+
meet the following conditions:
|
93 |
+
|
94 |
+
(a) You must give any other recipients of the Work or
|
95 |
+
Derivative Works a copy of this License; and
|
96 |
+
|
97 |
+
(b) You must cause any modified files to carry prominent notices
|
98 |
+
stating that You changed the files; and
|
99 |
+
|
100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
101 |
+
that You distribute, all copyright, patent, trademark, and
|
102 |
+
attribution notices from the Source form of the Work,
|
103 |
+
excluding those notices that do not pertain to any part of
|
104 |
+
the Derivative Works; and
|
105 |
+
|
106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
+
distribution, then any Derivative Works that You distribute must
|
108 |
+
include a readable copy of the attribution notices contained
|
109 |
+
within such NOTICE file, excluding those notices that do not
|
110 |
+
pertain to any part of the Derivative Works, in at least one
|
111 |
+
of the following places: within a NOTICE text file distributed
|
112 |
+
as part of the Derivative Works; within the Source form or
|
113 |
+
documentation, if provided along with the Derivative Works; or,
|
114 |
+
within a display generated by the Derivative Works, if and
|
115 |
+
wherever such third-party notices normally appear. The contents
|
116 |
+
of the NOTICE file are for informational purposes only and
|
117 |
+
do not modify the License. You may add Your own attribution
|
118 |
+
notices within Derivative Works that You distribute, alongside
|
119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
120 |
+
that such additional attribution notices cannot be construed
|
121 |
+
as modifying the License.
|
122 |
+
|
123 |
+
You may add Your own copyright statement to Your modifications and
|
124 |
+
may provide additional or different license terms and conditions
|
125 |
+
for use, reproduction, or distribution of Your modifications, or
|
126 |
+
for any such Derivative Works as a whole, provided Your use,
|
127 |
+
reproduction, and distribution of the Work otherwise complies with
|
128 |
+
the conditions stated in this License.
|
129 |
+
|
130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
132 |
+
by You to the Licensor shall be under the terms and conditions of
|
133 |
+
this License, without any additional terms or conditions.
|
134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
+
the terms of any separate license agreement you may have executed
|
136 |
+
with Licensor regarding such Contributions.
|
137 |
+
|
138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
140 |
+
except as required for reasonable and customary use in describing the
|
141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
+
|
143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
+
agreed to in writing, Licensor provides the Work (and each
|
145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
+
implied, including, without limitation, any warranties or conditions
|
148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
+
appropriateness of using or redistributing the Work and assume any
|
151 |
+
risks associated with Your exercise of permissions under this License.
|
152 |
+
|
153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
+
whether in tort (including negligence), contract, or otherwise,
|
155 |
+
unless required by applicable law (such as deliberate and grossly
|
156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
+
liable to You for damages, including any direct, indirect, special,
|
158 |
+
incidental, or consequential damages of any character arising as a
|
159 |
+
result of this License or out of the use or inability to use the
|
160 |
+
Work (including but not limited to damages for loss of goodwill,
|
161 |
+
work stoppage, computer failure or malfunction, or any and all
|
162 |
+
other commercial damages or losses), even if such Contributor
|
163 |
+
has been advised of the possibility of such damages.
|
164 |
+
|
165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
+
or other liability obligations and/or rights consistent with this
|
169 |
+
License. However, in accepting such obligations, You may act only
|
170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
+
of any other Contributor, and only if You agree to indemnify,
|
172 |
+
defend, and hold each Contributor harmless for any liability
|
173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
174 |
+
of your accepting any such warranty or additional liability.
|
175 |
+
|
176 |
+
END OF TERMS AND CONDITIONS
|
177 |
+
|
178 |
+
APPENDIX: How to apply the Apache License to your work.
|
179 |
+
|
180 |
+
To apply the Apache License to your work, attach the following
|
181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
+
replaced with your own identifying information. (Don't include
|
183 |
+
the brackets!) The text should be enclosed in the appropriate
|
184 |
+
comment syntax for the file format. We also recommend that a
|
185 |
+
file or class name and description of purpose be included on the
|
186 |
+
same "printed page" as the copyright notice for easier
|
187 |
+
identification within third-party archives.
|
188 |
+
|
189 |
+
Copyright [yyyy] [name of copyright owner]
|
190 |
+
|
191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
+
you may not use this file except in compliance with the License.
|
193 |
+
You may obtain a copy of the License at
|
194 |
+
|
195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
+
|
197 |
+
Unless required by applicable law or agreed to in writing, software
|
198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
+
See the License for the specific language governing permissions and
|
201 |
+
limitations under the License.
|
README.md
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: MLIP Arena
|
3 |
+
emoji: ⚛
|
4 |
+
sdk: streamlit
|
5 |
+
sdk_version: 1.43.2 # The latest supported version
|
6 |
+
python_version: 3.11
|
7 |
+
app_file: serve/app.py
|
8 |
+
colorFrom: indigo
|
9 |
+
colorTo: yellow
|
10 |
+
pinned: true
|
11 |
+
short_description: Benchmark machine learning interatomic potential at scale
|
12 |
+
---
|
13 |
+
|
14 |
+
|
benchmarks/bzo/dft.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
benchmarks/bzo/pbe/mode-1.npy
ADDED
Binary file (248 Bytes). View file
|
|
benchmarks/bzo/pbe/phonopy_params.yaml
ADDED
The diff for this file is too large to render.
See raw diff
|
|
benchmarks/c2db/ALIGNN.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ce4d250afce0a7ef62dd27c5531b1e3a91f761035cc595e64ff6aae225e4ad73
|
3 |
+
size 272171
|
benchmarks/c2db/CHGNet.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a6063fa72efb16a5255b79f5e1a03bd13409ed129016496ff1f494c6f83b98be
|
3 |
+
size 292909
|
benchmarks/c2db/M3GNet.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:32e1517a85a1b64f12fb262a0948a95be58c69edde133ce7ddf683154b8f2a95
|
3 |
+
size 290358
|
benchmarks/c2db/MACE-MP(M).parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f722eac6799bfecaa02188d59475862895a639cc596fa8b7d1e9d2b96cfb415b
|
3 |
+
size 293633
|
benchmarks/c2db/MACE-MPA.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c3ea679b5f6c9940358a2121a496544be91ba01ed8383509c65773f9fc69b9ec
|
3 |
+
size 293820
|
benchmarks/c2db/MatterSim.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d150c1b31b99ddcbbf21401189289aead13791c683aa379d75163b8bc4dbc6b4
|
3 |
+
size 293177
|
benchmarks/c2db/ORBv2.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c2496f96d4aff1536936e58e65c1d608cc1953d41006221ba62ea2daab23f30b
|
3 |
+
size 293012
|
benchmarks/c2db/SevenNet.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0c2ee18ce70f24f70e65d70c2e54151e86dd0ccb3e412b8fbbc572e44e8bf5e8
|
3 |
+
size 293973
|
benchmarks/c2db/analysis.ipynb
ADDED
@@ -0,0 +1,408 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": null,
|
6 |
+
"id": "0625f0a1",
|
7 |
+
"metadata": {},
|
8 |
+
"outputs": [],
|
9 |
+
"source": [
|
10 |
+
"import random\n",
|
11 |
+
"from pathlib import Path\n",
|
12 |
+
"\n",
|
13 |
+
"import numpy as np\n",
|
14 |
+
"from ase.db import connect\n",
|
15 |
+
"\n",
|
16 |
+
"random.seed(0)\n",
|
17 |
+
"\n",
|
18 |
+
"DATA_DIR = Path(\".\")\n",
|
19 |
+
"\n",
|
20 |
+
"db = connect(DATA_DIR / \"c2db.db\")\n",
|
21 |
+
"random_indices = random.sample(range(1, len(db) + 1), 1000)\n"
|
22 |
+
]
|
23 |
+
},
|
24 |
+
{
|
25 |
+
"cell_type": "code",
|
26 |
+
"execution_count": null,
|
27 |
+
"id": "005708b9",
|
28 |
+
"metadata": {},
|
29 |
+
"outputs": [],
|
30 |
+
"source": [
|
31 |
+
"import itertools\n",
|
32 |
+
"\n",
|
33 |
+
"import pandas as pd\n",
|
34 |
+
"import phonopy\n",
|
35 |
+
"from tqdm.auto import tqdm\n",
|
36 |
+
"\n",
|
37 |
+
"from mlip_arena.models import MLIPEnum\n",
|
38 |
+
"\n",
|
39 |
+
"for row, model in tqdm(\n",
|
40 |
+
" itertools.product(db.select(filter=lambda r: r[\"id\"] in random_indices), MLIPEnum)\n",
|
41 |
+
"):\n",
|
42 |
+
" uid = row[\"uid\"]\n",
|
43 |
+
"\n",
|
44 |
+
" if Path(f\"{model.name}.parquet\").exists():\n",
|
45 |
+
" df = pd.read_parquet(f\"{model.name}.parquet\")\n",
|
46 |
+
" if uid in df[\"uid\"].unique():\n",
|
47 |
+
" continue\n",
|
48 |
+
" else:\n",
|
49 |
+
" df = pd.DataFrame(columns=[\"model\", \"uid\", \"eigenvalues\", \"frequencies\"])\n",
|
50 |
+
"\n",
|
51 |
+
" try:\n",
|
52 |
+
" path = Path(model.name) / uid\n",
|
53 |
+
" phonon = phonopy.load(path / \"phonopy.yaml\")\n",
|
54 |
+
" frequencies = phonon.get_frequencies(q=(0, 0, 0))\n",
|
55 |
+
"\n",
|
56 |
+
" data = np.load(path / \"elastic.npz\")\n",
|
57 |
+
"\n",
|
58 |
+
" eigenvalues = data[\"eigenvalues\"]\n",
|
59 |
+
"\n",
|
60 |
+
" new_row = pd.DataFrame(\n",
|
61 |
+
" [\n",
|
62 |
+
" {\n",
|
63 |
+
" \"model\": model.name,\n",
|
64 |
+
" \"uid\": uid,\n",
|
65 |
+
" \"eigenvalues\": eigenvalues,\n",
|
66 |
+
" \"frequencies\": frequencies,\n",
|
67 |
+
" }\n",
|
68 |
+
" ]\n",
|
69 |
+
" )\n",
|
70 |
+
"\n",
|
71 |
+
" df = pd.concat([df, new_row], ignore_index=True)\n",
|
72 |
+
" df.drop_duplicates(subset=[\"model\", \"uid\"], keep=\"last\", inplace=True)\n",
|
73 |
+
"\n",
|
74 |
+
" df.to_parquet(f\"{model.name}.parquet\", index=False)\n",
|
75 |
+
" except Exception:\n",
|
76 |
+
" pass\n"
|
77 |
+
]
|
78 |
+
},
|
79 |
+
{
|
80 |
+
"cell_type": "code",
|
81 |
+
"execution_count": 6,
|
82 |
+
"id": "b8d87638",
|
83 |
+
"metadata": {},
|
84 |
+
"outputs": [],
|
85 |
+
"source": [
|
86 |
+
"uids = []\n",
|
87 |
+
"stabilities = []\n",
|
88 |
+
"for row in db.select(filter=lambda r: r[\"id\"] in random_indices):\n",
|
89 |
+
" stable = row.key_value_pairs[\"dyn_stab\"]\n",
|
90 |
+
" if stable.lower() == \"unknown\":\n",
|
91 |
+
" stable = None\n",
|
92 |
+
" else:\n",
|
93 |
+
" stable = True if stable.lower() == \"yes\" else False\n",
|
94 |
+
" uids.append(row.key_value_pairs[\"uid\"])\n",
|
95 |
+
" stabilities.append(stable)\n",
|
96 |
+
"\n",
|
97 |
+
"\n",
|
98 |
+
"stabilities = np.array(stabilities)\n",
|
99 |
+
"\n",
|
100 |
+
"(stabilities == True).sum(), (stabilities == False).sum(), (stabilities == None).sum()"
|
101 |
+
]
|
102 |
+
},
|
103 |
+
{
|
104 |
+
"cell_type": "markdown",
|
105 |
+
"id": "a3c516a7",
|
106 |
+
"metadata": {},
|
107 |
+
"source": []
|
108 |
+
},
|
109 |
+
{
|
110 |
+
"cell_type": "code",
|
111 |
+
"execution_count": 104,
|
112 |
+
"id": "0052d0ff",
|
113 |
+
"metadata": {},
|
114 |
+
"outputs": [],
|
115 |
+
"source": [
|
116 |
+
"%matplotlib inline\n",
|
117 |
+
"\n",
|
118 |
+
"from pathlib import Path\n",
|
119 |
+
"\n",
|
120 |
+
"import numpy as np\n",
|
121 |
+
"import pandas as pd\n",
|
122 |
+
"from matplotlib import pyplot as plt\n",
|
123 |
+
"from sklearn.metrics import (\n",
|
124 |
+
" ConfusionMatrixDisplay,\n",
|
125 |
+
" classification_report,\n",
|
126 |
+
" confusion_matrix,\n",
|
127 |
+
")\n",
|
128 |
+
"\n",
|
129 |
+
"from mlip_arena.models import MLIPEnum\n",
|
130 |
+
"\n",
|
131 |
+
"thres = -1e-7\n",
|
132 |
+
"\n",
|
133 |
+
"select_models = [\n",
|
134 |
+
" \"ALIGNN\",\n",
|
135 |
+
" \"CHGNet\",\n",
|
136 |
+
" \"M3GNet\",\n",
|
137 |
+
" \"MACE-MP(M)\",\n",
|
138 |
+
" \"MACE-MPA\",\n",
|
139 |
+
" \"MatterSim\",\n",
|
140 |
+
" \"ORBv2\",\n",
|
141 |
+
" \"SevenNet\",\n",
|
142 |
+
"]\n",
|
143 |
+
"\n",
|
144 |
+
"with plt.style.context(\"default\"):\n",
|
145 |
+
" # plt.rcParams.update({\n",
|
146 |
+
" # # \"title.fontsize\": 10,\n",
|
147 |
+
" # \"axes.titlesize\": 10,\n",
|
148 |
+
" # \"axes.labelsize\": 8,\n",
|
149 |
+
" # })\n",
|
150 |
+
"\n",
|
151 |
+
" SMALL_SIZE = 8\n",
|
152 |
+
" MEDIUM_SIZE = 10\n",
|
153 |
+
" BIGGER_SIZE = 12\n",
|
154 |
+
" plt.rcParams.update(\n",
|
155 |
+
" {\n",
|
156 |
+
" \"font.size\": SMALL_SIZE,\n",
|
157 |
+
" \"axes.titlesize\": MEDIUM_SIZE,\n",
|
158 |
+
" \"axes.labelsize\": MEDIUM_SIZE,\n",
|
159 |
+
" \"xtick.labelsize\": MEDIUM_SIZE,\n",
|
160 |
+
" \"ytick.labelsize\": MEDIUM_SIZE,\n",
|
161 |
+
" \"legend.fontsize\": SMALL_SIZE,\n",
|
162 |
+
" \"figure.titlesize\": BIGGER_SIZE,\n",
|
163 |
+
" }\n",
|
164 |
+
" )\n",
|
165 |
+
"\n",
|
166 |
+
" fig, axs = plt.subplots(\n",
|
167 |
+
" nrows=int(np.ceil(len(MLIPEnum) / 4)),\n",
|
168 |
+
" ncols=4,\n",
|
169 |
+
" figsize=(6, 3 * int(np.ceil(len(select_models) / 4))),\n",
|
170 |
+
" sharey=True,\n",
|
171 |
+
" sharex=True,\n",
|
172 |
+
" layout=\"constrained\",\n",
|
173 |
+
" )\n",
|
174 |
+
" axs = axs.flatten()\n",
|
175 |
+
" plot_idx = 0\n",
|
176 |
+
"\n",
|
177 |
+
" for model in MLIPEnum:\n",
|
178 |
+
" fpath = DATA_DIR / f\"{model.name}.parquet\"\n",
|
179 |
+
" if not fpath.exists():\n",
|
180 |
+
" continue\n",
|
181 |
+
"\n",
|
182 |
+
" if model.name not in select_models:\n",
|
183 |
+
" continue\n",
|
184 |
+
"\n",
|
185 |
+
" df = pd.read_parquet(fpath)\n",
|
186 |
+
" df[\"eigval_min\"] = df[\"eigenvalues\"].apply(\n",
|
187 |
+
" lambda x: x.min() if np.isreal(x).all() else thres\n",
|
188 |
+
" )\n",
|
189 |
+
" df[\"freq_min\"] = df[\"frequencies\"].apply(\n",
|
190 |
+
" lambda x: x.min() if np.isreal(x).all() else thres\n",
|
191 |
+
" )\n",
|
192 |
+
" df[\"dyn_stab\"] = ~np.logical_or(\n",
|
193 |
+
" df[\"eigval_min\"] < thres, df[\"freq_min\"] < thres\n",
|
194 |
+
" )\n",
|
195 |
+
"\n",
|
196 |
+
" arg = np.argsort(uids)\n",
|
197 |
+
" uids_sorted = np.array(uids)[arg]\n",
|
198 |
+
" stabilities_sorted = stabilities[arg]\n",
|
199 |
+
"\n",
|
200 |
+
" sorted_df = (\n",
|
201 |
+
" df[df[\"uid\"].isin(uids_sorted)].set_index(\"uid\").reindex(uids_sorted)\n",
|
202 |
+
" )\n",
|
203 |
+
" mask = ~(stabilities_sorted == None)\n",
|
204 |
+
"\n",
|
205 |
+
" y_true = stabilities_sorted[mask].astype(\"int\")\n",
|
206 |
+
" y_pred = sorted_df[\"dyn_stab\"][mask].fillna(-1).astype(\"int\")\n",
|
207 |
+
" cm = confusion_matrix(y_true, y_pred, labels=[1, 0, -1])\n",
|
208 |
+
"\n",
|
209 |
+
" ax = axs[plot_idx]\n",
|
210 |
+
" ConfusionMatrixDisplay(\n",
|
211 |
+
" cm, display_labels=[\"stable\", \"unstable\", \"missing\"]\n",
|
212 |
+
" ).plot(ax=ax, cmap=\"Blues\", colorbar=False)\n",
|
213 |
+
"\n",
|
214 |
+
" ax.set_title(model.name)\n",
|
215 |
+
" ax.set_xlabel(\"Predicted\")\n",
|
216 |
+
" ax.set_ylabel(\"True\")\n",
|
217 |
+
" ax.set_xticks([0, 1, 2])\n",
|
218 |
+
" ax.set_xticklabels([\"stable\", \"unstable\", \"missing\"])\n",
|
219 |
+
" ax.set_yticks([0, 1, 2])\n",
|
220 |
+
" ax.set_yticklabels([\"stable\", \"unstable\", \"missing\"])\n",
|
221 |
+
"\n",
|
222 |
+
" plot_idx += 1\n",
|
223 |
+
"\n",
|
224 |
+
" # Hide unused subplots\n",
|
225 |
+
" for i in range(plot_idx, len(axs)):\n",
|
226 |
+
" fig.delaxes(axs[i])\n",
|
227 |
+
"\n",
|
228 |
+
" # plt.tight_layout()\n",
|
229 |
+
" plt.savefig(\"c2db-confusion_matrices.pdf\", bbox_inches=\"tight\")\n",
|
230 |
+
" plt.show()\n"
|
231 |
+
]
|
232 |
+
},
|
233 |
+
{
|
234 |
+
"cell_type": "code",
|
235 |
+
"execution_count": 52,
|
236 |
+
"id": "573b3c38",
|
237 |
+
"metadata": {},
|
238 |
+
"outputs": [],
|
239 |
+
"source": [
|
240 |
+
"import pandas as pd\n",
|
241 |
+
"from sklearn.metrics import confusion_matrix\n",
|
242 |
+
"\n",
|
243 |
+
"from mlip_arena.models import MLIPEnum\n",
|
244 |
+
"\n",
|
245 |
+
"thres = -1e-7\n",
|
246 |
+
"\n",
|
247 |
+
"summary_df = pd.DataFrame(columns=[\"Model\", \"Stable F1\", \"Unstable F1\", \"Weighted F1\"])\n",
|
248 |
+
"\n",
|
249 |
+
"for model in MLIPEnum:\n",
|
250 |
+
" fpath = DATA_DIR / f\"{model.name}.parquet\"\n",
|
251 |
+
"\n",
|
252 |
+
" if not fpath.exists() or model.name not in select_models:\n",
|
253 |
+
" # print(f\"File {fpath} does not exist\")\n",
|
254 |
+
" continue\n",
|
255 |
+
" df = pd.read_parquet(fpath)\n",
|
256 |
+
"\n",
|
257 |
+
" df[\"eigval_min\"] = df[\"eigenvalues\"].apply(\n",
|
258 |
+
" lambda x: x.min() if np.isreal(x).all() else thres\n",
|
259 |
+
" )\n",
|
260 |
+
" df[\"freq_min\"] = df[\"frequencies\"].apply(\n",
|
261 |
+
" lambda x: x.min() if np.isreal(x).all() else thres\n",
|
262 |
+
" )\n",
|
263 |
+
" df[\"dyn_stab\"] = ~np.logical_or(df[\"eigval_min\"] < thres, df[\"freq_min\"] < thres)\n",
|
264 |
+
"\n",
|
265 |
+
" arg = np.argsort(uids)\n",
|
266 |
+
" uids = np.array(uids)[arg]\n",
|
267 |
+
" stabilities = stabilities[arg]\n",
|
268 |
+
"\n",
|
269 |
+
" sorted_df = df[df[\"uid\"].isin(uids)].sort_values(by=\"uid\")\n",
|
270 |
+
"\n",
|
271 |
+
" # sorted_df = sorted_df.reindex(uids).reset_index()\n",
|
272 |
+
" sorted_df = sorted_df.set_index(\"uid\").reindex(uids) # .loc[uids].reset_index()\n",
|
273 |
+
"\n",
|
274 |
+
" sorted_df = sorted_df.loc[uids]\n",
|
275 |
+
" # mask = ~np.logical_or(sorted_df['dyn_stab'].isna().values, stabilities == None)\n",
|
276 |
+
" mask = ~(stabilities == None)\n",
|
277 |
+
"\n",
|
278 |
+
" y_true = stabilities[mask].astype(\"int\")\n",
|
279 |
+
" y_pred = sorted_df[\"dyn_stab\"][mask].fillna(-1).astype(\"int\")\n",
|
280 |
+
" cm = confusion_matrix(y_true, y_pred, labels=[1, 0, -1])\n",
|
281 |
+
" # print(model)\n",
|
282 |
+
" # print(cm)\n",
|
283 |
+
" # print(classification_report(y_true, y_pred, labels=[1, 0], target_names=['stable', 'unstable'], digits=3, output_dict=False))\n",
|
284 |
+
"\n",
|
285 |
+
" report = classification_report(\n",
|
286 |
+
" y_true,\n",
|
287 |
+
" y_pred,\n",
|
288 |
+
" labels=[1, 0],\n",
|
289 |
+
" target_names=[\"stable\", \"unstable\"],\n",
|
290 |
+
" digits=3,\n",
|
291 |
+
" output_dict=True,\n",
|
292 |
+
" )\n",
|
293 |
+
"\n",
|
294 |
+
" summary_df = pd.concat(\n",
|
295 |
+
" [\n",
|
296 |
+
" summary_df,\n",
|
297 |
+
" pd.DataFrame(\n",
|
298 |
+
" [\n",
|
299 |
+
" {\n",
|
300 |
+
" \"Model\": model.name,\n",
|
301 |
+
" \"Stable F1\": report[\"stable\"][\"f1-score\"],\n",
|
302 |
+
" \"Unstable F1\": report[\"unstable\"][\"f1-score\"],\n",
|
303 |
+
" \"Macro F1\": report[\"macro avg\"][\"f1-score\"],\n",
|
304 |
+
" # 'Micro F1': report['micro avg']['f1-score'],\n",
|
305 |
+
" \"Weighted F1\": report[\"weighted avg\"][\"f1-score\"],\n",
|
306 |
+
" }\n",
|
307 |
+
" ]\n",
|
308 |
+
" ),\n",
|
309 |
+
" ],\n",
|
310 |
+
" ignore_index=True,\n",
|
311 |
+
" )\n",
|
312 |
+
"\n",
|
313 |
+
" # break"
|
314 |
+
]
|
315 |
+
},
|
316 |
+
{
|
317 |
+
"cell_type": "code",
|
318 |
+
"execution_count": 85,
|
319 |
+
"id": "df660870",
|
320 |
+
"metadata": {},
|
321 |
+
"outputs": [],
|
322 |
+
"source": [
|
323 |
+
"summary_df = summary_df.sort_values(by=[\"Macro F1\", \"Weighted F1\"], ascending=False)\n",
|
324 |
+
"summary_df.to_latex(\"c2db_summary_table.tex\", index=False, float_format=\"%.3f\")"
|
325 |
+
]
|
326 |
+
},
|
327 |
+
{
|
328 |
+
"cell_type": "code",
|
329 |
+
"execution_count": 103,
|
330 |
+
"id": "18f4a59b",
|
331 |
+
"metadata": {},
|
332 |
+
"outputs": [],
|
333 |
+
"source": [
|
334 |
+
"from matplotlib import cm\n",
|
335 |
+
"\n",
|
336 |
+
"# Metrics and bar settings\n",
|
337 |
+
"metrics = [\"Stable F1\", \"Unstable F1\", \"Macro F1\", \"Weighted F1\"]\n",
|
338 |
+
"bar_width = 0.2\n",
|
339 |
+
"x = np.arange(len(summary_df))\n",
|
340 |
+
"\n",
|
341 |
+
"# Get Set2 colormap (as RGBA)\n",
|
342 |
+
"cmap = plt.get_cmap(\"tab20\")\n",
|
343 |
+
"colors = {metric: cmap(i) for i, metric in enumerate(metrics)}\n",
|
344 |
+
"\n",
|
345 |
+
"with plt.style.context(\"default\"):\n",
|
346 |
+
" plt.rcParams.update(\n",
|
347 |
+
" {\n",
|
348 |
+
" \"font.size\": SMALL_SIZE,\n",
|
349 |
+
" \"axes.titlesize\": MEDIUM_SIZE,\n",
|
350 |
+
" \"axes.labelsize\": MEDIUM_SIZE,\n",
|
351 |
+
" \"xtick.labelsize\": MEDIUM_SIZE,\n",
|
352 |
+
" \"ytick.labelsize\": MEDIUM_SIZE,\n",
|
353 |
+
" \"legend.fontsize\": SMALL_SIZE,\n",
|
354 |
+
" \"figure.titlesize\": BIGGER_SIZE,\n",
|
355 |
+
" }\n",
|
356 |
+
" )\n",
|
357 |
+
"\n",
|
358 |
+
" fig, ax = plt.subplots(figsize=(4, 3), layout=\"constrained\")\n",
|
359 |
+
"\n",
|
360 |
+
" # Bar positions\n",
|
361 |
+
" positions = {\n",
|
362 |
+
" \"Stable F1\": x - 1.5 * bar_width,\n",
|
363 |
+
" \"Unstable F1\": x - 0.5 * bar_width,\n",
|
364 |
+
" \"Macro F1\": x + 0.5 * bar_width,\n",
|
365 |
+
" \"Weighted F1\": x + 1.5 * bar_width,\n",
|
366 |
+
" }\n",
|
367 |
+
"\n",
|
368 |
+
" # Plot each metric with assigned color\n",
|
369 |
+
" for metric, pos in positions.items():\n",
|
370 |
+
" ax.bar(\n",
|
371 |
+
" pos, summary_df[metric], width=bar_width, label=metric, color=colors[metric]\n",
|
372 |
+
" )\n",
|
373 |
+
"\n",
|
374 |
+
" ax.set_xlabel(\"Model\")\n",
|
375 |
+
" ax.set_ylabel(\"F1 Score\")\n",
|
376 |
+
" # ax.set_title('F1 Scores by Model and Class')\n",
|
377 |
+
" ax.set_xticks(x)\n",
|
378 |
+
" ax.set_xticklabels(summary_df[\"Model\"], rotation=45, ha=\"right\")\n",
|
379 |
+
" ax.legend(ncols=2, bbox_to_anchor=(0.5, 1), loc=\"upper center\", fontsize=SMALL_SIZE)\n",
|
380 |
+
" # ax.legend(ncols=2, fontsize=SMALL_SIZE)\n",
|
381 |
+
" ax.spines[[\"top\", \"right\"]].set_visible(False)\n",
|
382 |
+
" plt.tight_layout()\n",
|
383 |
+
" plt.ylim(0, 0.9)\n",
|
384 |
+
" plt.grid(axis=\"y\", linestyle=\"--\", alpha=0.6)\n",
|
385 |
+
"\n",
|
386 |
+
" plt.savefig(\"c2db_f1_bar.pdf\", bbox_inches=\"tight\")\n",
|
387 |
+
" plt.show()"
|
388 |
+
]
|
389 |
+
},
|
390 |
+
{
|
391 |
+
"cell_type": "code",
|
392 |
+
"execution_count": null,
|
393 |
+
"id": "1c50f705",
|
394 |
+
"metadata": {},
|
395 |
+
"outputs": [],
|
396 |
+
"source": []
|
397 |
+
}
|
398 |
+
],
|
399 |
+
"metadata": {
|
400 |
+
"kernelspec": {
|
401 |
+
"display_name": "mlip-arena",
|
402 |
+
"language": "python",
|
403 |
+
"name": "mlip-arena"
|
404 |
+
}
|
405 |
+
},
|
406 |
+
"nbformat": 4,
|
407 |
+
"nbformat_minor": 5
|
408 |
+
}
|
benchmarks/c2db/c2db-confusion_matrices.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:463968f63e87ca0a7acd2e719cc481d0e3c5f5dd69ccf8f8659bddf6aa3b1e93
|
3 |
+
size 21238
|
benchmarks/c2db/c2db-f1_bar.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3d0c862d4efa2d9c83ac4fbe26eeef66a8f8017b37d955b70e414fdbea94aabd
|
3 |
+
size 17883
|
benchmarks/c2db/c2db.db
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:caf58205692de480e06149ac43a437385f18e14582e7d9a8dab8b3cb5d4bd678
|
3 |
+
size 70762496
|
benchmarks/c2db/copy.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7fdc16667361b10bfb032862d5d0610c242d75cb88f7f3883c43a406b245e991
|
3 |
+
size 21349
|
benchmarks/c2db/run.py
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from itertools import product
|
2 |
+
from pathlib import Path
|
3 |
+
|
4 |
+
import numpy as np
|
5 |
+
import pandas as pd
|
6 |
+
from dask.distributed import Client
|
7 |
+
from dask_jobqueue import SLURMCluster
|
8 |
+
from mlip_arena.models import MLIPEnum
|
9 |
+
from mlip_arena.tasks import ELASTICITY, OPT, PHONON
|
10 |
+
from mlip_arena.tasks.optimize import run as OPT
|
11 |
+
from mlip_arena.tasks.utils import get_calculator
|
12 |
+
from numpy import linalg as LA
|
13 |
+
from prefect import flow, task
|
14 |
+
from prefect_dask import DaskTaskRunner
|
15 |
+
from tqdm.auto import tqdm
|
16 |
+
|
17 |
+
from ase.db import connect
|
18 |
+
|
19 |
+
select_models = [
|
20 |
+
"ALIGNN",
|
21 |
+
"CHGNet",
|
22 |
+
"M3GNet",
|
23 |
+
"MACE-MP(M)",
|
24 |
+
"MACE-MPA",
|
25 |
+
"MatterSim",
|
26 |
+
"ORBv2",
|
27 |
+
"SevenNet",
|
28 |
+
]
|
29 |
+
|
30 |
+
def elastic_tensor_to_voigt(C):
|
31 |
+
"""
|
32 |
+
Convert a rank-4 (3x3x3x3) elastic tensor into a rank-2 (6x6) tensor using Voigt notation.
|
33 |
+
|
34 |
+
Parameters:
|
35 |
+
C (numpy.ndarray): A 3x3x3x3 elastic tensor.
|
36 |
+
|
37 |
+
Returns:
|
38 |
+
numpy.ndarray: A 6x6 elastic tensor in Voigt notation.
|
39 |
+
"""
|
40 |
+
# voigt_map = {
|
41 |
+
# (0, 0): 0, (1, 1): 1, (2, 2): 2, # Normal components
|
42 |
+
# (1, 2): 3, (2, 1): 3, # Shear components
|
43 |
+
# (0, 2): 4, (2, 0): 4,
|
44 |
+
# (0, 1): 5, (1, 0): 5
|
45 |
+
# }
|
46 |
+
voigt_map = {
|
47 |
+
(0, 0): 0,
|
48 |
+
(1, 1): 1,
|
49 |
+
(2, 2): -1, # Normal components
|
50 |
+
(1, 2): -1,
|
51 |
+
(2, 1): -1, # Shear components
|
52 |
+
(0, 2): -1,
|
53 |
+
(2, 0): -1,
|
54 |
+
(0, 1): 2,
|
55 |
+
(1, 0): 2,
|
56 |
+
}
|
57 |
+
|
58 |
+
C_voigt = np.zeros((3, 3))
|
59 |
+
|
60 |
+
for i in range(3):
|
61 |
+
for j in range(3):
|
62 |
+
for k in range(3):
|
63 |
+
for l in range(3):
|
64 |
+
alpha = voigt_map[(i, j)]
|
65 |
+
beta = voigt_map[(k, l)]
|
66 |
+
|
67 |
+
if alpha == -1 or beta == -1:
|
68 |
+
continue
|
69 |
+
|
70 |
+
factor = 1
|
71 |
+
# if alpha in [3, 4, 5]:
|
72 |
+
if alpha == 2:
|
73 |
+
factor = factor * (2**0.5)
|
74 |
+
if beta == 2:
|
75 |
+
factor = factor * (2**0.5)
|
76 |
+
|
77 |
+
C_voigt[alpha, beta] = C[i, j, k, l] * factor
|
78 |
+
|
79 |
+
return C_voigt
|
80 |
+
|
81 |
+
|
82 |
+
# -
|
83 |
+
|
84 |
+
|
85 |
+
@task
|
86 |
+
def run_one(model, row):
|
87 |
+
if Path(f"{model.name}.pkl").exists():
|
88 |
+
df = pd.read_pickle(f"{model.name}.pkl")
|
89 |
+
|
90 |
+
# if row.key_value_pairs.get('uid', None) in df['uid'].unique():
|
91 |
+
# pass
|
92 |
+
else:
|
93 |
+
df = pd.DataFrame(columns=["model", "uid", "eigenvalues", "frequencies"])
|
94 |
+
|
95 |
+
atoms = row.toatoms()
|
96 |
+
# print(data := row.key_value_pairs)
|
97 |
+
|
98 |
+
calc = get_calculator(model)
|
99 |
+
|
100 |
+
result_opt = OPT(
|
101 |
+
atoms,
|
102 |
+
calc,
|
103 |
+
optimizer="FIRE",
|
104 |
+
criterion=dict(fmax=0.05, steps=500),
|
105 |
+
symmetry=True,
|
106 |
+
)
|
107 |
+
|
108 |
+
atoms = result_opt["atoms"]
|
109 |
+
|
110 |
+
result_elastic = ELASTICITY(
|
111 |
+
atoms,
|
112 |
+
calc,
|
113 |
+
optimizer="FIRE",
|
114 |
+
criterion=dict(fmax=0.05, steps=500),
|
115 |
+
pre_relax=False,
|
116 |
+
)
|
117 |
+
|
118 |
+
elastic_tensor = elastic_tensor_to_voigt(result_elastic["elastic_tensor"])
|
119 |
+
eigenvalues, eigenvectors = LA.eig(elastic_tensor)
|
120 |
+
|
121 |
+
outdir = Path(f"{model.name}") / row.key_value_pairs.get(
|
122 |
+
"uid", atoms.get_chemical_formula()
|
123 |
+
)
|
124 |
+
outdir.mkdir(parents=True, exist_ok=True)
|
125 |
+
|
126 |
+
np.savez(outdir / "elastic.npz", tensor=elastic_tensor, eigenvalues=eigenvalues)
|
127 |
+
|
128 |
+
result_phonon = PHONON(
|
129 |
+
atoms,
|
130 |
+
calc,
|
131 |
+
supercell_matrix=(2, 2, 1),
|
132 |
+
outdir=outdir,
|
133 |
+
)
|
134 |
+
|
135 |
+
frequencies = result_phonon["phonon"].get_frequencies(q=(0, 0, 0))
|
136 |
+
|
137 |
+
new_row = pd.DataFrame(
|
138 |
+
[
|
139 |
+
{
|
140 |
+
"model": model.name,
|
141 |
+
"uid": row.key_value_pairs.get("uid", None),
|
142 |
+
"eigenvalues": eigenvalues,
|
143 |
+
"frequencies": frequencies,
|
144 |
+
}
|
145 |
+
]
|
146 |
+
)
|
147 |
+
|
148 |
+
df = pd.concat([df, new_row], ignore_index=True)
|
149 |
+
df.drop_duplicates(subset=["model", "uid"], keep="last", inplace=True)
|
150 |
+
|
151 |
+
df.to_pickle(f"{model.name}.pkl")
|
152 |
+
|
153 |
+
|
154 |
+
@flow
|
155 |
+
def run_all():
|
156 |
+
import random
|
157 |
+
|
158 |
+
random.seed(0)
|
159 |
+
|
160 |
+
futures = []
|
161 |
+
with connect("c2db.db") as db:
|
162 |
+
random_indices = random.sample(range(1, len(db) + 1), 1000)
|
163 |
+
for row, model in tqdm(
|
164 |
+
product(db.select(filter=lambda r: r["id"] in random_indices), MLIPEnum)
|
165 |
+
):
|
166 |
+
if model.name not in select_models:
|
167 |
+
continue
|
168 |
+
future = run_one.submit(model, row)
|
169 |
+
futures.append(future)
|
170 |
+
return [f.result(raise_on_failure=False) for f in futures]
|
171 |
+
|
172 |
+
|
173 |
+
# +
|
174 |
+
|
175 |
+
|
176 |
+
if __name__ == "__main__":
|
177 |
+
nodes_per_alloc = 1
|
178 |
+
gpus_per_alloc = 1
|
179 |
+
ntasks = 1
|
180 |
+
|
181 |
+
cluster_kwargs = dict(
|
182 |
+
cores=1,
|
183 |
+
memory="64 GB",
|
184 |
+
processes=1,
|
185 |
+
shebang="#!/bin/bash",
|
186 |
+
account="matgen",
|
187 |
+
walltime="00:30:00",
|
188 |
+
# job_cpu=128,
|
189 |
+
job_mem="0",
|
190 |
+
job_script_prologue=[
|
191 |
+
"source ~/.bashrc",
|
192 |
+
"module load python",
|
193 |
+
"source activate /pscratch/sd/c/cyrusyc/.conda/dev",
|
194 |
+
],
|
195 |
+
job_directives_skip=["-n", "--cpus-per-task", "-J"],
|
196 |
+
job_extra_directives=[
|
197 |
+
"-J c2db",
|
198 |
+
"-q regular",
|
199 |
+
f"-N {nodes_per_alloc}",
|
200 |
+
"-C gpu",
|
201 |
+
f"-G {gpus_per_alloc}",
|
202 |
+
],
|
203 |
+
)
|
204 |
+
|
205 |
+
cluster = SLURMCluster(**cluster_kwargs)
|
206 |
+
print(cluster.job_script())
|
207 |
+
cluster.adapt(minimum_jobs=25, maximum_jobs=50)
|
208 |
+
client = Client(cluster)
|
209 |
+
# -
|
210 |
+
|
211 |
+
run_all.with_options(
|
212 |
+
task_runner=DaskTaskRunner(address=client.scheduler.address), log_prints=True
|
213 |
+
)()
|
benchmarks/energy_conservation/run.py
ADDED
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Task for running MD simulations and computing the differential entropy
|
3 |
+
of the simulated structures with respect to a reference dataset.
|
4 |
+
|
5 |
+
See https://github.com/dskoda/quests for differential entropy details.
|
6 |
+
"""
|
7 |
+
|
8 |
+
from __future__ import annotations
|
9 |
+
|
10 |
+
import os
|
11 |
+
from datetime import datetime
|
12 |
+
|
13 |
+
import numpy as np
|
14 |
+
from ase.io import read
|
15 |
+
from prefect import task
|
16 |
+
from prefect.cache_policies import INPUTS, TASK_SOURCE
|
17 |
+
from prefect.runtime import task_run
|
18 |
+
|
19 |
+
from mlip_arena.models import MLIPEnum
|
20 |
+
from mlip_arena.tasks.md import run as MD
|
21 |
+
from mlip_arena.tasks.utils import logger
|
22 |
+
|
23 |
+
try:
|
24 |
+
from quests.descriptor import get_descriptors
|
25 |
+
from quests.entropy import delta_entropy
|
26 |
+
except ImportError as e:
|
27 |
+
logger.warning(e)
|
28 |
+
logger.warning(
|
29 |
+
"quests is not installed. Please install it using `pip install quests` or following the instructions at https://github.com/dskoda/quests to use this module."
|
30 |
+
)
|
31 |
+
|
32 |
+
|
33 |
+
def get_entropy_from_path(
|
34 |
+
subset_path, dataset_path, dataset_desc_out_path, k=32, cutoff=5.0, h=0.015
|
35 |
+
):
|
36 |
+
"""
|
37 |
+
Computes the differential entropy of a subset of structures with respect
|
38 |
+
to a reference dataset.
|
39 |
+
|
40 |
+
Arguments:
|
41 |
+
subset_path (str): Path to the file containing the subset of structures.
|
42 |
+
dataset_path (str): Path to the file containing the full dataset of structures without the subset.
|
43 |
+
dataset_desc_out_path (str): Path to save the descriptors of the full dataset.
|
44 |
+
k (int, optional): Number of nearest neighbors used for descriptor calculation. Default is 32.
|
45 |
+
cutoff (float, optional): Cutoff distance for descriptor calculation. Default is 5.0.
|
46 |
+
h (float, optional): Bandwidth for the Gaussian kernel. Default is 0.015.
|
47 |
+
|
48 |
+
Returns:
|
49 |
+
np.ndarray: The differential entropy of the subset with respect to the dataset.
|
50 |
+
"""
|
51 |
+
|
52 |
+
x_structures = read(dataset_path, index=":")
|
53 |
+
x_desc = get_descriptors(x_structures, k=k, cutoff=cutoff)
|
54 |
+
np.save(dataset_desc_out_path, x_desc)
|
55 |
+
|
56 |
+
y_structures = read(subset_path, index=":")
|
57 |
+
y_desc = get_descriptors(y_structures, k=k, cutoff=cutoff)
|
58 |
+
|
59 |
+
dH = delta_entropy(y_desc, x_desc, h=h)
|
60 |
+
return dH
|
61 |
+
|
62 |
+
|
63 |
+
def get_trajectory_entropy(
|
64 |
+
trajectory_dir,
|
65 |
+
start_idx,
|
66 |
+
end_idx,
|
67 |
+
step,
|
68 |
+
dataset_desc_path,
|
69 |
+
k=32,
|
70 |
+
cutoff=5.0,
|
71 |
+
h=0.015,
|
72 |
+
):
|
73 |
+
"""
|
74 |
+
Computes the differential entropy of a subset of structures in a trajectory with respect
|
75 |
+
to a reference dataset.
|
76 |
+
|
77 |
+
Arguments:
|
78 |
+
trajectory_dir (str): Path to the directory containing the trajectory files.
|
79 |
+
start_idx (int): Starting index of the subset of structures to select from each trajectory.
|
80 |
+
end_idx (int): Ending index of the subset of structures to select from each trajectory.
|
81 |
+
step (int): Step size of the subset of structures to select from each trajectory.
|
82 |
+
dataset_desc_path (str): Path to the file containing the descriptors of the full dataset of structures without the subset.
|
83 |
+
k (int, optional): Number of nearest neighbors used for descriptor calculation. Default is 32.
|
84 |
+
cutoff (float, optional): Cutoff distance for descriptor calculation. Default is 5.0.
|
85 |
+
h (float, optional): Bandwidth for the Gaussian kernel. Default is 0.015.
|
86 |
+
|
87 |
+
Choose start_idx, end_idx, step to select which structures to compute the differential entropy for, based on what sliding window is chosen.
|
88 |
+
e.g. window of size 5 with stride 2 means we select every other structure starting at index 2 (middle of the first window) to the -2 index (middle of the last window)
|
89 |
+
|
90 |
+
Returns:
|
91 |
+
np.ndarray: The differential entropy of the subset of structures in the trajectory with respect to the dataset.
|
92 |
+
"""
|
93 |
+
structures = []
|
94 |
+
for traj_file in sorted(os.listdir(trajectory_dir)):
|
95 |
+
traj = read(os.path.join(trajectory_dir, traj_file), index=":")
|
96 |
+
every_other = traj[start_idx:end_idx:step]
|
97 |
+
structures.extend(every_other)
|
98 |
+
|
99 |
+
desc = get_descriptors(structures, k=k, cutoff=cutoff)
|
100 |
+
x_desc = np.load(dataset_desc_path)
|
101 |
+
dH = delta_entropy(desc, x_desc, h=h)
|
102 |
+
return dH
|
103 |
+
|
104 |
+
|
105 |
+
def run_simulations(model_names, structures, out_dir):
|
106 |
+
"""
|
107 |
+
Runs simulations on a list of structures.
|
108 |
+
|
109 |
+
Parameters:
|
110 |
+
model_names (list[str]): List of models to use.
|
111 |
+
structures (list[ase.Atoms]): List of structures to simulate.
|
112 |
+
out_dir (str): Directory to save the simulation trajectories to.
|
113 |
+
|
114 |
+
Notes:
|
115 |
+
Structures are replicated to have at least 100 atoms and at most 500 atoms.
|
116 |
+
Structures are simulated with NVE MD at 1000 K for 5 ps.
|
117 |
+
Simulation trajectories are saved to files in out_dir, with each file named according to the index of the structure in the list.
|
118 |
+
"""
|
119 |
+
min_atoms = 100
|
120 |
+
max_atoms = 500
|
121 |
+
|
122 |
+
futures = []
|
123 |
+
|
124 |
+
for model_name in model_names:
|
125 |
+
os.makedirs(out_dir, exist_ok=True)
|
126 |
+
model = MLIPEnum[model_name]
|
127 |
+
calc = model.value()
|
128 |
+
|
129 |
+
for i, atoms in enumerate(structures):
|
130 |
+
logger.info(
|
131 |
+
f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Running {model_name} on structure number {i}"
|
132 |
+
)
|
133 |
+
|
134 |
+
# Replicate the structure
|
135 |
+
n_atoms = len(atoms)
|
136 |
+
rep_factor = int(
|
137 |
+
np.ceil((min_atoms / n_atoms) ** (1 / 3))
|
138 |
+
) # cube root since it's a 3D replication
|
139 |
+
supercell_atoms = atoms.repeat((rep_factor, rep_factor, rep_factor))
|
140 |
+
if len(supercell_atoms) > max_atoms:
|
141 |
+
logger.info(
|
142 |
+
f"Skipping structure {i} because it has too many atoms ({len(supercell_atoms)} > {max_atoms})"
|
143 |
+
)
|
144 |
+
continue # skip if it becomes too large
|
145 |
+
|
146 |
+
# Run NVE MD @ 1000K for 5 ps
|
147 |
+
future = MD.submit(
|
148 |
+
supercell_atoms,
|
149 |
+
calculator=calc,
|
150 |
+
ensemble="nve",
|
151 |
+
dynamics="velocityverlet",
|
152 |
+
time_step=1.0, # fs
|
153 |
+
total_time=5000, # 5 ps = 5000 fs
|
154 |
+
temperature=1000.0,
|
155 |
+
traj_file=f"{out_dir}/{i}.traj",
|
156 |
+
traj_interval=100,
|
157 |
+
zero_linear_momentum=True,
|
158 |
+
zero_angular_momentum=True,
|
159 |
+
)
|
160 |
+
futures.append(future)
|
161 |
+
|
162 |
+
return [f.result(raise_on_failure=False) for f in futures]
|
163 |
+
|
164 |
+
|
165 |
+
def _generate_task_run_name():
|
166 |
+
task_name = task_run.task_name
|
167 |
+
parameters = task_run.parameters
|
168 |
+
|
169 |
+
trajectory_dir = parameters["trajectory_dir"]
|
170 |
+
dataset_desc_path = parameters["dataset_desc_path"]
|
171 |
+
|
172 |
+
return f"{task_name}: {trajectory_dir} - {dataset_desc_path}"
|
173 |
+
|
174 |
+
|
175 |
+
@task(
|
176 |
+
name="Entropy along trajectory",
|
177 |
+
task_run_name=_generate_task_run_name,
|
178 |
+
cache_policy=TASK_SOURCE + INPUTS,
|
179 |
+
)
|
180 |
+
def run(
|
181 |
+
dataset_path,
|
182 |
+
model_names,
|
183 |
+
structures,
|
184 |
+
trajectory_dir,
|
185 |
+
start_idx,
|
186 |
+
end_idx,
|
187 |
+
step,
|
188 |
+
dataset_desc_path,
|
189 |
+
dH_out_path,
|
190 |
+
k=32,
|
191 |
+
cutoff=5.0,
|
192 |
+
h=0.015,
|
193 |
+
):
|
194 |
+
# Get descriptors for the dataset. This should exclude the subset of structures used for simulations.
|
195 |
+
# This may take a while if the dataset is large - in that case, would recommend splitting the structures into separate chunks.
|
196 |
+
x_structures = read(dataset_path, index=":")
|
197 |
+
x_desc = get_descriptors(x_structures, k=k, cutoff=cutoff)
|
198 |
+
np.save(dataset_desc_path, x_desc)
|
199 |
+
|
200 |
+
# Run simulations
|
201 |
+
run_simulations(model_names, structures, trajectory_dir)
|
202 |
+
|
203 |
+
# Get entropy for structures along trajectories
|
204 |
+
dH = get_trajectory_entropy(
|
205 |
+
trajectory_dir,
|
206 |
+
start_idx,
|
207 |
+
end_idx,
|
208 |
+
step,
|
209 |
+
dataset_desc_path,
|
210 |
+
k=k,
|
211 |
+
cutoff=cutoff,
|
212 |
+
h=h,
|
213 |
+
)
|
214 |
+
np.save(dH_out_path, dH)
|
benchmarks/eos_alloy/run_Fe-Ni-Cr.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
benchmarks/eos_bulk/CHGNet.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:68871d694e93a3c3e7e272b9cbd87d3757e3bc689f30f3189db232d76e629c07
|
3 |
+
size 429910
|
benchmarks/eos_bulk/CHGNet_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:bfde7530e6b0d2df5a30e1b7e3ec124fb2a86f6da8e35d2548d37d10a1eff1b1
|
3 |
+
size 387425
|
benchmarks/eos_bulk/M3GNet.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:53dde465b5e10edd677f131f8a531e3dfc36303dd7ec7b9df0060c19847494d9
|
3 |
+
size 427419
|
benchmarks/eos_bulk/M3GNet_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:eb43a3c74f3340100b1adb21b3f2d075451e1ffe88ac6d6662741bc4a0576eb8
|
3 |
+
size 397450
|
benchmarks/eos_bulk/MACE-MP(M).parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ff9769eeb83042129767aeff975eb04dee8efae12e96fbd46cd3039eeda26705
|
3 |
+
size 427896
|
benchmarks/eos_bulk/MACE-MP(M)_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7e5507cdc5fe558b5d3fe2ea8f1dd577ac444e82c5347b5fbe738a4f855dffcb
|
3 |
+
size 397379
|
benchmarks/eos_bulk/MACE-MPA.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:53fcd188baddd4d5e797c5aa3de1b4368db711ebd29b7877cfe224856ba9d171
|
3 |
+
size 428888
|
benchmarks/eos_bulk/MACE-MPA_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0f3032d5a156febdd9580fa3d86cb1a84236374bcac6ccb22d18a948767db502
|
3 |
+
size 394748
|
benchmarks/eos_bulk/MatterSim.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e6717650b97782de6f90e4473075410fe4540279eb39338d2234d3c9399079b3
|
3 |
+
size 389586
|
benchmarks/eos_bulk/MatterSim_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:fb1f10a60495f5e88ea8cf737fd7b47d1c471fda422374ee519d14f531c732f8
|
3 |
+
size 290191
|
benchmarks/eos_bulk/ORBv2.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ae13c9af1ae7fafe2a42ed4c47e2ba0f036abfa64a87ca517b92d89c62fcbfd9
|
3 |
+
size 427105
|
benchmarks/eos_bulk/ORBv2_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7eb0a3060b8a2d3541b8fb1083176c88aae0a8be0008e84d5770998b01742216
|
3 |
+
size 402554
|
benchmarks/eos_bulk/SevenNet.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:64be88ec2632cdabf79daa01acb2cf2ef19fef0557813df5502c4f71ec566f4e
|
3 |
+
size 428341
|
benchmarks/eos_bulk/SevenNet_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9f484928f5086e8d1411a198ac69bfe44313597909b72c8676e9131cce1660f1
|
3 |
+
size 398295
|
benchmarks/eos_bulk/analyze.py
ADDED
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
|
3 |
+
import numpy as np
|
4 |
+
import pandas as pd
|
5 |
+
from ase.db import connect
|
6 |
+
from scipy import stats
|
7 |
+
|
8 |
+
from mlip_arena.models import REGISTRY, MLIPEnum
|
9 |
+
|
10 |
+
DATA_DIR = Path(__file__).parent.absolute()
|
11 |
+
|
12 |
+
|
13 |
+
def load_wbm_structures():
|
14 |
+
"""
|
15 |
+
Load the WBM structures from a ASE DB file.
|
16 |
+
"""
|
17 |
+
with connect(DATA_DIR.parent / "wbm_structures.db") as db:
|
18 |
+
for row in db.select():
|
19 |
+
yield row.toatoms(add_additional_information=True)
|
20 |
+
|
21 |
+
def gather_results():
|
22 |
+
for model in MLIPEnum:
|
23 |
+
if "eos_bulk" not in REGISTRY[model.name].get("gpu-tasks", []):
|
24 |
+
continue
|
25 |
+
|
26 |
+
if (DATA_DIR / f"{model.name}.parquet").exists():
|
27 |
+
continue
|
28 |
+
|
29 |
+
all_data = []
|
30 |
+
|
31 |
+
for atoms in load_wbm_structures():
|
32 |
+
fpath = Path(model.name) / f"{atoms.info['key_value_pairs']['wbm_id']}.pkl"
|
33 |
+
if not fpath.exists():
|
34 |
+
continue
|
35 |
+
|
36 |
+
all_data.append(pd.read_pickle(fpath))
|
37 |
+
|
38 |
+
df = pd.concat(all_data, ignore_index=True)
|
39 |
+
df.to_parquet(DATA_DIR / f"{model.name}.parquet")
|
40 |
+
|
41 |
+
|
42 |
+
def summarize():
|
43 |
+
summary_table = pd.DataFrame(
|
44 |
+
columns=[
|
45 |
+
"model",
|
46 |
+
"energy-diff-flip-times",
|
47 |
+
"tortuosity",
|
48 |
+
"spearman-compression-energy",
|
49 |
+
"spearman-compression-derivative",
|
50 |
+
"spearman-tension-energy",
|
51 |
+
"missing",
|
52 |
+
]
|
53 |
+
)
|
54 |
+
|
55 |
+
|
56 |
+
for model in MLIPEnum:
|
57 |
+
fpath = DATA_DIR / f"{model.name}.parquet"
|
58 |
+
if not fpath.exists():
|
59 |
+
continue
|
60 |
+
df_raw_results = pd.read_parquet(fpath)
|
61 |
+
|
62 |
+
df_analyzed = pd.DataFrame(
|
63 |
+
columns=[
|
64 |
+
"model",
|
65 |
+
"structure",
|
66 |
+
"formula",
|
67 |
+
"volume-ratio",
|
68 |
+
"energy-delta-per-atom",
|
69 |
+
"energy-diff-flip-times",
|
70 |
+
"energy-delta-per-volume-b0",
|
71 |
+
"tortuosity",
|
72 |
+
"spearman-compression-energy",
|
73 |
+
"spearman-compression-derivative",
|
74 |
+
"spearman-tension-energy",
|
75 |
+
"missing",
|
76 |
+
]
|
77 |
+
)
|
78 |
+
|
79 |
+
for wbm_struct in load_wbm_structures():
|
80 |
+
structure_id = wbm_struct.info["key_value_pairs"]["wbm_id"]
|
81 |
+
|
82 |
+
try:
|
83 |
+
results = df_raw_results.loc[df_raw_results["id"] == structure_id]
|
84 |
+
b0 = results["b0"].values[0]
|
85 |
+
# vol0 = results["v0"].values[0]
|
86 |
+
results = results["eos"].values[0]
|
87 |
+
es = np.array(results["energies"])
|
88 |
+
vols = np.array(results["volumes"])
|
89 |
+
|
90 |
+
indices = np.argsort(vols)
|
91 |
+
vols = vols[indices]
|
92 |
+
es = es[indices]
|
93 |
+
|
94 |
+
imine = len(es) // 2
|
95 |
+
# min_center_val = np.min(es[imid - 1 : imid + 2])
|
96 |
+
# imine = np.where(es == min_center_val)[0][0]
|
97 |
+
emin = es[imine]
|
98 |
+
vol0 = vols[imine]
|
99 |
+
|
100 |
+
interpolated_volumes = [
|
101 |
+
(vols[i] + vols[i + 1]) / 2 for i in range(len(vols) - 1)
|
102 |
+
]
|
103 |
+
ediff = np.diff(es)
|
104 |
+
ediff_sign = np.sign(ediff)
|
105 |
+
mask = ediff_sign != 0
|
106 |
+
ediff = ediff[mask]
|
107 |
+
ediff_sign = ediff_sign[mask]
|
108 |
+
ediff_flip = np.diff(ediff_sign) != 0
|
109 |
+
|
110 |
+
etv = np.sum(np.abs(np.diff(es)))
|
111 |
+
|
112 |
+
data = {
|
113 |
+
"model": model.name,
|
114 |
+
"structure": structure_id,
|
115 |
+
"formula": wbm_struct.get_chemical_formula(),
|
116 |
+
"missing": False,
|
117 |
+
"volume-ratio": vols / vol0,
|
118 |
+
"energy-delta-per-atom": (es - emin) / len(wbm_struct),
|
119 |
+
"energy-diff-flip-times": np.sum(ediff_flip).astype(int),
|
120 |
+
"energy-delta-per-volume-b0": (es - emin) / (b0*vol0),
|
121 |
+
"tortuosity": etv / (abs(es[0] - emin) + abs(es[-1] - emin)),
|
122 |
+
"spearman-compression-energy": stats.spearmanr(
|
123 |
+
vols[:imine], es[:imine]
|
124 |
+
).statistic,
|
125 |
+
"spearman-compression-derivative": stats.spearmanr(
|
126 |
+
interpolated_volumes[:imine], ediff[:imine]
|
127 |
+
).statistic,
|
128 |
+
"spearman-tension-energy": stats.spearmanr(
|
129 |
+
vols[imine:], es[imine:]
|
130 |
+
).statistic,
|
131 |
+
}
|
132 |
+
|
133 |
+
except Exception as e:
|
134 |
+
print(e)
|
135 |
+
data = {
|
136 |
+
"model": model.name,
|
137 |
+
"structure": structure_id,
|
138 |
+
"formula": wbm_struct.get_chemical_formula(),
|
139 |
+
"missing": True,
|
140 |
+
"volume-ratio": None,
|
141 |
+
"energy-delta-per-atom": None,
|
142 |
+
"energy-delta-per-volume-b0": None,
|
143 |
+
"energy-diff-flip-times": None,
|
144 |
+
"tortuosity": None,
|
145 |
+
"spearman-compression-energy": None,
|
146 |
+
"spearman-compression-derivative": None,
|
147 |
+
"spearman-tension-energy": None,
|
148 |
+
}
|
149 |
+
|
150 |
+
df_analyzed = pd.concat([df_analyzed, pd.DataFrame([data])], ignore_index=True)
|
151 |
+
|
152 |
+
df_analyzed.to_parquet(DATA_DIR / f"{model.name}_processed.parquet")
|
153 |
+
# json_fpath = DATA_DIR / f"EV_scan_analyzed_{model.name}.json"
|
154 |
+
|
155 |
+
# df_analyzed.to_json(json_fpath, orient="records")
|
156 |
+
|
157 |
+
valid_results = df_analyzed[df_analyzed["missing"] == False]
|
158 |
+
|
159 |
+
analysis_summary = {
|
160 |
+
"model": model.name,
|
161 |
+
"energy-diff-flip-times": valid_results["energy-diff-flip-times"].mean(),
|
162 |
+
"energy-diff-flip-times-std": valid_results["energy-diff-flip-times"].std(),
|
163 |
+
"tortuosity": valid_results["tortuosity"].mean(),
|
164 |
+
"tortuosity-std": valid_results["tortuosity"].std(),
|
165 |
+
"spearman-compression-energy": valid_results[
|
166 |
+
"spearman-compression-energy"
|
167 |
+
].mean(),
|
168 |
+
"spearman-compression-energy-std": valid_results["spearman-compression-energy"].std(),
|
169 |
+
"spearman-compression-derivative": valid_results[
|
170 |
+
"spearman-compression-derivative"
|
171 |
+
].mean(),
|
172 |
+
"spearman-compression-derivative-std": valid_results[
|
173 |
+
"spearman-compression-derivative"
|
174 |
+
].std(),
|
175 |
+
"spearman-tension-energy": valid_results["spearman-tension-energy"].mean(),
|
176 |
+
"spearman-tension-energy-std": valid_results["spearman-tension-energy"].std(),
|
177 |
+
"missing": len(df_analyzed[df_analyzed["missing"] == True]),
|
178 |
+
}
|
179 |
+
summary_table = pd.concat(
|
180 |
+
[summary_table, pd.DataFrame([analysis_summary])], ignore_index=True
|
181 |
+
)
|
182 |
+
|
183 |
+
|
184 |
+
flip_rank = (
|
185 |
+
(summary_table["energy-diff-flip-times"] - 1)
|
186 |
+
.abs()
|
187 |
+
.rank(ascending=True, method="min")
|
188 |
+
)
|
189 |
+
tortuosity_rank = summary_table["tortuosity"].rank(ascending=True, method="min")
|
190 |
+
spearman_compression_energy_rank = summary_table["spearman-compression-energy"].rank(
|
191 |
+
method="min"
|
192 |
+
)
|
193 |
+
spearman_compression_derivative_rank = summary_table[
|
194 |
+
"spearman-compression-derivative"
|
195 |
+
].rank(ascending=False, method="min")
|
196 |
+
spearman_tension_energy_rank = summary_table["spearman-tension-energy"].rank(
|
197 |
+
ascending=False, method="min"
|
198 |
+
)
|
199 |
+
missing_rank = summary_table["missing"].rank(ascending=True, method="min")
|
200 |
+
|
201 |
+
rank_aggr = (
|
202 |
+
flip_rank
|
203 |
+
+ tortuosity_rank
|
204 |
+
+ spearman_compression_energy_rank
|
205 |
+
+ spearman_compression_derivative_rank
|
206 |
+
+ spearman_tension_energy_rank
|
207 |
+
+ missing_rank
|
208 |
+
)
|
209 |
+
rank = rank_aggr.rank(method="min")
|
210 |
+
|
211 |
+
summary_table.insert(1, "rank", rank.astype(int))
|
212 |
+
summary_table.insert(2, "rank-aggregation", rank_aggr.astype(int))
|
213 |
+
summary_table = summary_table.sort_values(by="rank", ascending=True)
|
214 |
+
summary_table = summary_table.reset_index(drop=True)
|
215 |
+
|
216 |
+
summary_table.to_csv(DATA_DIR / "summary.csv", index=False)
|
217 |
+
summary_table.to_latex(DATA_DIR / "summary.tex", index=False, float_format="%.3f")
|
218 |
+
|
219 |
+
return summary_table
|
220 |
+
|
221 |
+
if __name__ == "__main__":
|
222 |
+
gather_results()
|
223 |
+
summarize()
|
benchmarks/eos_bulk/eSEN.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:4503e17151b7376bbd88dc8c4767747e7290e8eae898e050b0a231a5c447e3e6
|
3 |
+
size 427652
|
benchmarks/eos_bulk/eSEN_processed.parquet
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d7f754d8e18f645c1608e86286245c11611d5af34f3bd0bbc4a5b63b851a0dee
|
3 |
+
size 393790
|
benchmarks/eos_bulk/plot.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
|
3 |
+
import matplotlib.pyplot as plt
|
4 |
+
import numpy as np
|
5 |
+
import pandas as pd
|
6 |
+
from ase.db import connect
|
7 |
+
|
8 |
+
from mlip_arena.models import REGISTRY as MODELS
|
9 |
+
|
10 |
+
DATA_DIR = Path(__file__).parent.absolute()
|
11 |
+
|
12 |
+
# Use a qualitative color palette from matplotlib
|
13 |
+
palette_name = "tab10" # Better for distinguishing multiple lines
|
14 |
+
color_sequence = plt.get_cmap(palette_name).colors
|
15 |
+
|
16 |
+
valid_models = [
|
17 |
+
model
|
18 |
+
for model, metadata in MODELS.items()
|
19 |
+
if "eos_bulk" in metadata.get("gpu-tasks", [])
|
20 |
+
]
|
21 |
+
|
22 |
+
def load_wbm_structures():
|
23 |
+
"""
|
24 |
+
Load the WBM structures from a ASE DB file.
|
25 |
+
"""
|
26 |
+
with connect(DATA_DIR.parent / "wbm_structures.db") as db:
|
27 |
+
for row in db.select():
|
28 |
+
yield row.toatoms(add_additional_information=True)
|
29 |
+
|
30 |
+
# # Collect valid models first
|
31 |
+
# valid_models = []
|
32 |
+
# for model_name in valid_models:
|
33 |
+
# fpath = DATA_DIR / f"{model_name}_processed.parquet"
|
34 |
+
# if fpath.exists():
|
35 |
+
# df = pd.read_parquet(fpath)
|
36 |
+
# if len(df) > 0:
|
37 |
+
# valid_models.append(model)
|
38 |
+
|
39 |
+
# # Ensure we're showing all 8 models
|
40 |
+
# if len(valid_models) < 8:
|
41 |
+
# print(f"Warning: Only found {len(valid_models)} valid models instead of 8")
|
42 |
+
|
43 |
+
# Set up the grid layout
|
44 |
+
n_models = len(valid_models)
|
45 |
+
n_cols = 4 # Use 4 columns
|
46 |
+
n_rows = (n_models + n_cols - 1) // n_cols # Ceiling division to get required rows
|
47 |
+
|
48 |
+
# Create figure with enough space for all subplots
|
49 |
+
fig = plt.figure(
|
50 |
+
figsize=(6, 1.25 * n_rows), # Wider for better readability
|
51 |
+
constrained_layout=True, # Better than tight_layout for this case
|
52 |
+
)
|
53 |
+
|
54 |
+
# Create grid of subplots
|
55 |
+
axes = []
|
56 |
+
for i in range(n_models):
|
57 |
+
ax = plt.subplot(n_rows, n_cols, i+1)
|
58 |
+
axes.append(ax)
|
59 |
+
|
60 |
+
SMALL_SIZE = 6
|
61 |
+
MEDIUM_SIZE = 8
|
62 |
+
LARGE_SIZE = 10
|
63 |
+
|
64 |
+
# Fill in the subplots with data
|
65 |
+
for i, model_name in enumerate(valid_models):
|
66 |
+
fpath = DATA_DIR / f"{model_name}_processed.parquet"
|
67 |
+
df = pd.read_parquet(fpath)
|
68 |
+
|
69 |
+
ax = axes[i]
|
70 |
+
valid_structures = []
|
71 |
+
|
72 |
+
for j, (_, row) in enumerate(df.iterrows()):
|
73 |
+
structure_id = row["structure"]
|
74 |
+
formula = row.get("formula", "")
|
75 |
+
if isinstance(row["volume-ratio"], (list, np.ndarray)) and isinstance(
|
76 |
+
row["energy-delta-per-volume-b0"], (list, np.ndarray)
|
77 |
+
):
|
78 |
+
vol_strain = row["volume-ratio"]
|
79 |
+
energy_delta = row["energy-delta-per-volume-b0"]
|
80 |
+
color = color_sequence[j % len(color_sequence)]
|
81 |
+
ax.plot(
|
82 |
+
vol_strain,
|
83 |
+
energy_delta,
|
84 |
+
color=color,
|
85 |
+
linewidth=1,
|
86 |
+
alpha=0.9,
|
87 |
+
)
|
88 |
+
valid_structures.append(structure_id)
|
89 |
+
|
90 |
+
# Set subplot title
|
91 |
+
ax.set_title(f"{model_name} ({len(valid_structures)})", fontsize=MEDIUM_SIZE)
|
92 |
+
|
93 |
+
# Only add y-label to leftmost plots (those with index divisible by n_cols)
|
94 |
+
if i % n_cols == 0:
|
95 |
+
ax.set_ylabel("$\\frac{\\Delta E}{B V_0}$", fontsize=MEDIUM_SIZE)
|
96 |
+
else:
|
97 |
+
ax.set_ylabel("")
|
98 |
+
|
99 |
+
# Only add x-label to bottom row plots
|
100 |
+
# Check if this plot is in the bottom row
|
101 |
+
is_bottom_row = (i // n_cols) == (n_rows - 1) or (i >= n_models - n_cols)
|
102 |
+
if is_bottom_row:
|
103 |
+
ax.set_xlabel("$V/V_0$", fontsize=MEDIUM_SIZE)
|
104 |
+
else:
|
105 |
+
ax.set_xlabel("")
|
106 |
+
|
107 |
+
ax.set_ylim(-0.02, 0.1) # Consistent y-limits
|
108 |
+
ax.axvline(x=1, linestyle="--", color="gray", alpha=0.7)
|
109 |
+
ax.tick_params(axis="both", which="major", labelsize=MEDIUM_SIZE)
|
110 |
+
|
111 |
+
# Make sure all subplots share the x and y limits
|
112 |
+
for ax in axes:
|
113 |
+
ax.set_xlim(0.8, 1.2) # Adjust these as needed
|
114 |
+
ax.set_ylim(-0.02, 0.1)
|
115 |
+
|
116 |
+
# Save the figure with all plots
|
117 |
+
plt.savefig(DATA_DIR / "eos-bulk-grid.png", dpi=300, bbox_inches="tight")
|
118 |
+
plt.savefig(DATA_DIR / "eos-bulk-grid.pdf", bbox_inches="tight")
|
119 |
+
# plt.show()
|
benchmarks/eos_bulk/preprocessing.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
|
3 |
+
from ase.db import connect
|
4 |
+
from pymatgen.core import Structure
|
5 |
+
|
6 |
+
with open("wbm_structures.json") as f:
|
7 |
+
structs = json.load(f)
|
8 |
+
|
9 |
+
with connect("wbm_structures.db") as db:
|
10 |
+
for id, s in structs.items():
|
11 |
+
atoms = Structure.from_dict(s).to_ase_atoms(msonable=False)
|
12 |
+
db.write(atoms, wbm_id=id)
|
benchmarks/eos_bulk/run.py
ADDED
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import functools
|
2 |
+
from pathlib import Path
|
3 |
+
|
4 |
+
import pandas as pd
|
5 |
+
from ase import Atoms
|
6 |
+
from ase.db import connect
|
7 |
+
from dask.distributed import Client
|
8 |
+
from dask_jobqueue import SLURMCluster
|
9 |
+
from prefect import flow, task
|
10 |
+
from prefect.cache_policies import INPUTS, TASK_SOURCE
|
11 |
+
from prefect.runtime import task_run
|
12 |
+
from prefect_dask import DaskTaskRunner
|
13 |
+
|
14 |
+
from mlip_arena.models import REGISTRY, MLIPEnum
|
15 |
+
from mlip_arena.tasks.utils import get_calculator
|
16 |
+
|
17 |
+
|
18 |
+
@task
|
19 |
+
def load_wbm_structures():
|
20 |
+
"""
|
21 |
+
Load the WBM structures from an ASE database file.
|
22 |
+
|
23 |
+
Reads structures from 'wbm_structures.db' and yields them as ASE Atoms objects
|
24 |
+
with additional metadata preserved from the database.
|
25 |
+
|
26 |
+
Yields:
|
27 |
+
ase.Atoms: Individual atomic structures from the WBM database with preserved
|
28 |
+
metadata in the .info dictionary.
|
29 |
+
"""
|
30 |
+
with connect("../wbm_structures.db") as db:
|
31 |
+
for row in db.select():
|
32 |
+
yield row.toatoms(add_additional_information=True)
|
33 |
+
|
34 |
+
|
35 |
+
# def save_result(
|
36 |
+
# tsk: Task,
|
37 |
+
# run: TaskRun,
|
38 |
+
# state: State,
|
39 |
+
# model_name: str,
|
40 |
+
# id: str,
|
41 |
+
# ):
|
42 |
+
# result = run.state.result()
|
43 |
+
|
44 |
+
# assert isinstance(result, dict)
|
45 |
+
|
46 |
+
# result["method"] = model_name
|
47 |
+
# result["id"] = id
|
48 |
+
# result.pop("atoms", None)
|
49 |
+
|
50 |
+
# fpath = Path(f"{model_name}")
|
51 |
+
# fpath.mkdir(exist_ok=True)
|
52 |
+
|
53 |
+
# fpath = fpath / f"{result['id']}.pkl"
|
54 |
+
|
55 |
+
# df = pd.DataFrame([result])
|
56 |
+
# df.to_pickle(fpath)
|
57 |
+
|
58 |
+
|
59 |
+
@task(
|
60 |
+
name="EOS bulk - WBM",
|
61 |
+
task_run_name=lambda: f"{task_run.task_name}: {task_run.parameters['atoms'].get_chemical_formula()} - {task_run.parameters['model'].name}",
|
62 |
+
cache_policy=TASK_SOURCE + INPUTS,
|
63 |
+
)
|
64 |
+
def eos_bulk(atoms: Atoms, model: MLIPEnum):
|
65 |
+
|
66 |
+
from mlip_arena.tasks.eos import run as EOS
|
67 |
+
from mlip_arena.tasks.optimize import run as OPT
|
68 |
+
|
69 |
+
calculator = get_calculator(
|
70 |
+
model
|
71 |
+
) # avoid sending entire model over prefect and select freer GPU
|
72 |
+
|
73 |
+
result = OPT.with_options(
|
74 |
+
refresh_cache=True,
|
75 |
+
)(
|
76 |
+
atoms,
|
77 |
+
calculator,
|
78 |
+
optimizer="FIRE",
|
79 |
+
criterion=dict(
|
80 |
+
fmax=0.1,
|
81 |
+
),
|
82 |
+
)
|
83 |
+
result = EOS.with_options(
|
84 |
+
refresh_cache=True,
|
85 |
+
# on_completion=[functools.partial(
|
86 |
+
# save_result,
|
87 |
+
# model_name=model.name,
|
88 |
+
# id=atoms.info["key_value_pairs"]["wbm_id"],
|
89 |
+
# )],
|
90 |
+
)(
|
91 |
+
atoms=result["atoms"],
|
92 |
+
calculator=calculator,
|
93 |
+
optimizer="FIRE",
|
94 |
+
npoints=21,
|
95 |
+
max_abs_strain=0.2,
|
96 |
+
concurrent=False
|
97 |
+
)
|
98 |
+
|
99 |
+
result["method"] = model.name
|
100 |
+
result["id"] = atoms.info["key_value_pairs"]["wbm_id"]
|
101 |
+
result.pop("atoms", None)
|
102 |
+
|
103 |
+
fpath = Path(f"{model.name}")
|
104 |
+
fpath.mkdir(exist_ok=True)
|
105 |
+
|
106 |
+
fpath = fpath / f"{result['id']}.pkl"
|
107 |
+
|
108 |
+
df = pd.DataFrame([result])
|
109 |
+
df.to_pickle(fpath)
|
110 |
+
|
111 |
+
return df
|
112 |
+
|
113 |
+
|
114 |
+
@flow
|
115 |
+
def submit_tasks():
|
116 |
+
futures = []
|
117 |
+
for atoms in load_wbm_structures():
|
118 |
+
model = MLIPEnum["eSEN"]
|
119 |
+
# for model in MLIPEnum:
|
120 |
+
if "eos_bulk" not in REGISTRY[model.name].get("gpu-tasks", []):
|
121 |
+
continue
|
122 |
+
try:
|
123 |
+
result = eos_bulk.with_options(
|
124 |
+
refresh_cache=True
|
125 |
+
).submit(atoms, model)
|
126 |
+
futures.append(result)
|
127 |
+
except Exception:
|
128 |
+
# print(f"Failed to submit task for {model.name}: {e}")
|
129 |
+
continue
|
130 |
+
return [f.result(raise_on_failure=False) for f in futures]
|
131 |
+
|
132 |
+
|
133 |
+
if __name__ == "__main__":
|
134 |
+
nodes_per_alloc = 1
|
135 |
+
gpus_per_alloc = 1
|
136 |
+
ntasks = 1
|
137 |
+
|
138 |
+
cluster_kwargs = dict(
|
139 |
+
cores=1,
|
140 |
+
memory="64 GB",
|
141 |
+
shebang="#!/bin/bash",
|
142 |
+
account="m3828",
|
143 |
+
walltime="00:30:00",
|
144 |
+
job_mem="0",
|
145 |
+
job_script_prologue=[
|
146 |
+
"source ~/.bashrc",
|
147 |
+
"module load python",
|
148 |
+
"module load cudatoolkit/12.4",
|
149 |
+
"source activate /pscratch/sd/c/cyrusyc/.conda/dev",
|
150 |
+
],
|
151 |
+
job_directives_skip=["-n", "--cpus-per-task", "-J"],
|
152 |
+
job_extra_directives=[
|
153 |
+
"-J eos_bulk",
|
154 |
+
"-q regular",
|
155 |
+
f"-N {nodes_per_alloc}",
|
156 |
+
"-C gpu",
|
157 |
+
f"-G {gpus_per_alloc}",
|
158 |
+
# "--exclusive",
|
159 |
+
],
|
160 |
+
)
|
161 |
+
|
162 |
+
cluster = SLURMCluster(**cluster_kwargs)
|
163 |
+
print(cluster.job_script())
|
164 |
+
cluster.adapt(minimum_jobs=50, maximum_jobs=50)
|
165 |
+
client = Client(cluster)
|
166 |
+
|
167 |
+
submit_tasks.with_options(
|
168 |
+
task_runner=DaskTaskRunner(address=client.scheduler.address),
|
169 |
+
log_prints=True,
|
170 |
+
)()
|