Skip to content

Commit 5a49e50

Browse files
Feature/pacemaker implementation (#485)
* Implement Pacemaker support: codes are all updated and unit tests passed * Implement Pacemaker support: error parsing and unit tests passed * Fix Pacemaker fitting: implementation of correct parameter passing * Fix P-ACE: extract hyperparameters from fit_kwargs in machine_learning_fit * fix hypers passing issue for pacemaker * implementation: Pacemaker ACE interface * fix: export extxyz file during pacemaker fitting * check: e0 reading * fix: species input * Update README with installation instructions for Pacemaker Added installation instructions for TensorFlow, TensorPotential, and pyace. * update: remove all unnecessary comments; No changes on codes * pre-commit auto-fixes * Fix SIM102: Combine nested if statements in jobs.py * fix an error in pacemaker fit unit test * Fix dependency conflicts and update installation docs for Pacemaker * fix: update for tensorpotential dependency * pre-commit auto-fixes * style: add noqa tags to bypass ruff PLC0415 for conditional imports * ci: fix git dubious ownership error in docker container for python 3.10 * ci: edit python-package.yml * chore: remove hardcoded dependencies to fully rely on pyproject.toml optional groups * fix space typo * ci: fix 3.10 version conflicts * ci: separate pacemaker tests from the standard ones * PACE test updated and dependencies modified --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 08472a0 commit 5a49e50

24 files changed

Lines changed: 7240 additions & 23 deletions

File tree

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"ms-azuretools.vscode-docker"]
1515
},
1616
},
17-
"postCreateCommand": "pip cache purge && uv pip install --prerelease=allow -e .[strict,docs] && pre-commit install",
17+
"postCreateCommand": "pip cache purge && uv pip install --prerelease=allow -e '.[strict,docs,tests]' && pre-commit install",
1818
}

.github/workflows/docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ jobs:
4242
uv pip install --prerelease=allow .[docs,strict,tests]
4343
uv pip install --upgrade monty
4444
uv pip install numpy==1.26.4
45+
4546
4647
- name: Copy tutorials
4748
run: |
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# This workflow will install pacemaker and its dependencies, run tests and lint with Python 3.10
2+
3+
name: Testing Pacemaker (Python 3.10)
4+
5+
on:
6+
workflow_dispatch:
7+
push:
8+
branches: [ main ]
9+
pull_request:
10+
branches: [ main ]
11+
12+
jobs:
13+
pacemaker-test:
14+
runs-on: ubuntu-latest
15+
env:
16+
PYTHON_VERSION: "3.10"
17+
18+
steps:
19+
- name: Fetch Most Recent Docker Image Tag
20+
run: |
21+
TAG=$(curl -H "Accept: application/vnd.github.v3+json" \
22+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
23+
https://api.github.com/orgs/autoatml/packages/container/autoplex%2Fautoplex-python-3.10/versions \
24+
| jq -r 'sort_by(.created_at) | reverse | .[0].metadata.container.tags[0]')
25+
echo "VERSION=$TAG" >> $GITHUB_ENV
26+
27+
- uses: actions/checkout@v4
28+
with:
29+
fetch-depth: 0
30+
31+
- name: Run Pacemaker Tests
32+
run: |
33+
docker pull ghcr.io/autoatml/autoplex/autoplex-python-3.10:${{ env.VERSION }}
34+
35+
docker run --rm \
36+
-v ${{ github.workspace }}:/workspace \
37+
-w /workspace \
38+
ghcr.io/autoatml/autoplex/autoplex-python-3.10:${{ env.VERSION }} \
39+
bash -c "
40+
git config --global --add safe.directory /workspace && \
41+
python -m pip install --upgrade pip && \
42+
python -m uv cache clean && \
43+
python -m uv pip install 'protobuf<3.20' tensorflow==2.8.0 && \
44+
python -m uv pip install --no-deps git+https://github.com/ICAMS/TensorPotential.git@1e44b2558356800ae070658c0bb856ff9bf74538 && \
45+
python -m uv pip install --no-deps git+https://github.com/ICAMS/python-ace.git@d1c213a7d9c5b809a3ae83b2e5a916be26d921f0 && \
46+
python -m uv pip install --prerelease=allow '.[strict,tests,aims]' && \
47+
OMP_NUM_THREADS=1 pytest --cache-clear -vv -k 'PACE'
48+
"

.github/workflows/python-package.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
- name: Run tests using Docker image for Python ${{ matrix.python-version }}
4141
run: |
4242
docker pull ghcr.io/autoatml/autoplex/autoplex-python-${{ matrix.python-version }}:${{ env.VERSION }}
43+
4344
docker run --rm \
4445
-v ${{ github.workspace }}:/workspace \
4546
-w /workspace \
@@ -48,9 +49,10 @@ jobs:
4849
git config --global --add safe.directory /workspace && \
4950
python -m pip install --upgrade pip && \
5051
python -m uv cache clean && \
51-
python -m uv pip install --prerelease=allow .[strict,tests,aims] && \
52-
OMP_NUM_THREADS=1 pytest --cache-clear --cov=autoplex --cov-report term-missing --cov-append --splits 5 --group ${{ matrix.split }} -vv --durations-path /workspace/tests/test_data/.pytest-split-durations --store-durations
52+
python -m uv pip install --prerelease=allow '.[strict,tests,aims]' && \
53+
OMP_NUM_THREADS=1 pytest --cache-clear --cov=autoplex --cov-report term-missing --cov-append --splits 5 --group ${{ matrix.split }} -vv --durations-path /workspace/tests/test_data/.pytest-split-durations --store-durations -k 'not PACE'
5354
"
55+
5456
5557
- name: Upload test durations artifact
5658
if: matrix.python-version == '3.10'

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,21 @@ Once installed in the terminal, run the following commands to get Julia ACEpoten
9191
julia -e 'using Pkg; Pkg.Registry.add("General"); Pkg.Registry.add(Pkg.Registry.RegistrySpec(url="https://github.com/ACEsuit/ACEregistry")); Pkg.add(Pkg.PackageSpec(;name="ACEpotentials", version="0.6.7")); Pkg.add("DataFrames"); Pkg.add("CSV")'
9292
```
9393

94+
> ℹ️ To fit and validate `Pacemaker ACE` potentials, one also needs to install `tensorflow`, `tensorpotential`, and `python-ace`.
95+
> Please note that Pacemaker ACE fitting can be run on both CPU and GPU.
96+
> ⚠️ Please also note on versioning: to prevent dependency conflicts (e.g., with `pandas` versions) and ensure stability, please install the exact commit hashes listed below using the `--no-deps` flag. These specific versions have been fully tested and validated for use with Autoplex.
97+
98+
```bash
99+
pip install autoplex[pacemaker]
100+
pip install tensorflow==2.8.0
101+
pip install --no-deps git+https://github.com/ICAMS/TensorPotential.git@1e44b2558356800ae070658c0bb856ff9bf74538
102+
# Ensure CMake is available before running this:
103+
pip install --no-deps git+https://github.com/ICAMS/python-ace.git@d1c213a7d9c5b809a3ae83b2e5a916be26d921f0
104+
```
105+
94106
> ℹ️ To fit and validate NEP potentials, one requires an Nvidia GPU card with compute capability no less than 3.5 and CUDA toolkit 9.0 or newer. This potential can only be trained on GPU only and currently interface to NEP potential training is provided via [calorine](https://calorine.materialsmodeling.org/) package that uses `nep` executable from the [GPUMD](https://gpumd.org/index.html) package. To get this executable please follow the compilation instructions [here](https://gpumd.org/installation.html) and add this executable to the system path.
95107
108+
96109
## Enabling RSS workflows
97110

98111
Additionally, `buildcell` as a part of `AIRSS` needs to be installed if one wants to use the RSS functionality:

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ strict = [
9090
dev = ["pre-commit>=2.12.1"]
9191
tests = ["pytest", "pytest-mock", "pytest-split", "pytest-cov", "types-setuptools", "nbmake"]
9292
aims = ["pyfhiaims"]
93+
pacemaker = [
94+
"numpy==1.26.4",
95+
"pandas>=2.2",
96+
"scipy",
97+
"ruamel.yaml",
98+
"psutil",
99+
"scikit-learn==1.4.2",
100+
]
93101

94102
[tool.setuptools_scm]
95103

src/autoplex/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
MLIPHypers,
1717
NEPSettings,
1818
NEQUIPSettings,
19+
PacemakerSettings,
1920
RssConfig,
2021
)
2122

@@ -27,3 +28,4 @@
2728
MACE_HYPERS = MACESettings()
2829
NEQUIP_HYPERS = NEQUIPSettings()
2930
NEP_HYPERS = NEPSettings()
31+
PACEMAKER_HYPERS = PacemakerSettings()

src/autoplex/auto/rss/flows.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def make(self, **kwargs):
222222
pre_database_dir: str | None
223223
Directory where the previous database was saved.
224224
mlip_type: str
225-
Choose one specific MLIP type to be fitted: 'GAP' | 'J-ACE' | 'NEQUIP' | 'M3GNET' | 'MACE'.
225+
Choose one specific MLIP type to be fitted: 'GAP' | 'J-ACE' | 'P-ACE' | 'NEQUIP' | 'M3GNET' | 'MACE'.
226226
Default is 'GAP'.
227227
ref_energy_name: str
228228
Reference energy name. Default is 'REF_energy'.

src/autoplex/auto/rss/jobs.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ def initial_rss(
9595
force_max: float | None = None,
9696
force_label: str = "REF_forces",
9797
pre_database_dir: str | None = None,
98-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"] = "GAP",
98+
mlip_type: Literal[
99+
"GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"
100+
] = "GAP",
99101
ref_energy_name: str = "REF_energy",
100102
ref_force_name: str = "REF_forces",
101103
ref_virial_name: str = "REF_virial",
@@ -197,7 +199,7 @@ def initial_rss(
197199
The label of force values to use for distillation. Default is 'REF_forces'.
198200
pre_database_dir: str | None
199201
Directory where the previous database was saved. Default is None.
200-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
202+
mlip_type: Literal["GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
201203
Choose one specific MLIP type to be fitted. Default is 'GAP'.
202204
ref_energy_name: str
203205
Reference energy name. Default is 'REF_energy'.
@@ -366,7 +368,9 @@ def do_rss_iterations(
366368
distillation: bool = True,
367369
force_max: float = 200,
368370
force_label: str = "REF_forces",
369-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"] = "GAP",
371+
mlip_type: Literal[
372+
"GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"
373+
] = "GAP",
370374
ref_energy_name: str = "REF_energy",
371375
ref_force_name: str = "REF_forces",
372376
ref_virial_name: str = "REF_virial",
@@ -508,7 +512,7 @@ def do_rss_iterations(
508512
Maximum force value to exclude structures. Default is 200.
509513
force_label: str
510514
The label of force values to use for distillation. Default is 'REF_forces'.
511-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
515+
mlip_type: Literal["GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
512516
Choose one specific MLIP type to be fitted. Default is 'GAP'.
513517
ref_energy_name: str
514518
Reference energy name. Default is 'REF_energy'.

src/autoplex/data/rss/jobs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ def _parallel_process(
424424

425425
@job
426426
def do_rss_single_node(
427-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"],
427+
mlip_type: Literal["GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"],
428428
mlip_path: str | Path,
429429
iteration_index: str,
430430
structures: list[Structure],
@@ -451,7 +451,7 @@ def do_rss_single_node(
451451
452452
Parameters
453453
----------
454-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
454+
mlip_type: Literal["GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
455455
Choose one specific MLIP type to be fitted.
456456
mlip_path: str | Path
457457
Path to the MLIP model.
@@ -527,7 +527,7 @@ def do_rss_single_node(
527527

528528
@job
529529
def do_rss_multi_node(
530-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"],
530+
mlip_type: Literal["GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"],
531531
mlip_path: str | Path,
532532
iteration_index: str,
533533
structure: list[Structure] | list[list[Structure]] | None = None,
@@ -555,7 +555,7 @@ def do_rss_multi_node(
555555
556556
Parameters
557557
----------
558-
mlip_type: Literal["GAP", "J-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
558+
mlip_type: Literal["GAP", "J-ACE", "P-ACE", "NEP", "NEQUIP", "M3GNET", "MACE"]
559559
Choose one specific MLIP type to be fitted.
560560
mlip_path: str | Path
561561
Path to the MLIP model.

0 commit comments

Comments
 (0)