alomancy 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alomancy-0.2.0/.coverage +0 -0
- alomancy-0.2.0/.github/workflows/ci.yml +214 -0
- alomancy-0.2.0/.gitignore +17 -0
- alomancy-0.2.0/.pre-commit-config.yaml +42 -0
- alomancy-0.2.0/.readthedocs.yaml +22 -0
- alomancy-0.2.0/CHANGELOG.md +103 -0
- alomancy-0.2.0/CLAUDE.md +143 -0
- alomancy-0.2.0/PKG-INFO +353 -0
- alomancy-0.2.0/README.md +300 -0
- alomancy-0.2.0/TESTING.md +340 -0
- alomancy-0.2.0/__init__.py +0 -0
- alomancy-0.2.0/docs/api/configs.rst +10 -0
- alomancy-0.2.0/docs/api/core.rst +25 -0
- alomancy-0.2.0/docs/api/database.rst +6 -0
- alomancy-0.2.0/docs/api/high_accuracy_evaluation.rst +23 -0
- alomancy-0.2.0/docs/api/index.rst +15 -0
- alomancy-0.2.0/docs/api/mlip.rst +14 -0
- alomancy-0.2.0/docs/api/structure_generation.rst +18 -0
- alomancy-0.2.0/docs/api/utils.rst +22 -0
- alomancy-0.2.0/docs/conf.py +62 -0
- alomancy-0.2.0/docs/contributing.md +37 -0
- alomancy-0.2.0/docs/examples.md +225 -0
- alomancy-0.2.0/docs/index.md +32 -0
- alomancy-0.2.0/docs/installation.md +39 -0
- alomancy-0.2.0/docs/quickstart.md +85 -0
- alomancy-0.2.0/docs/requirements.txt +4 -0
- alomancy-0.2.0/docs/usage.rst +6 -0
- alomancy-0.2.0/examples/basic_use/basic_use.py +36 -0
- alomancy-0.2.0/examples/basic_use/input_files/hpc_config.yaml +61 -0
- alomancy-0.2.0/examples/basic_use/input_files/standard_config.yaml +78 -0
- alomancy-0.2.0/pyproject.toml +237 -0
- alomancy-0.2.0/pytest.ini +23 -0
- alomancy-0.2.0/run_tests.py +82 -0
- alomancy-0.2.0/setup.cfg +4 -0
- alomancy-0.2.0/src/alomancy/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/analysis/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/analysis/example_committee_al_loop_mae_plot.png +0 -0
- alomancy-0.2.0/src/alomancy/analysis/plotting.py +120 -0
- alomancy-0.2.0/src/alomancy/configs/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/configs/config_dictionaries.py +56 -0
- alomancy-0.2.0/src/alomancy/configs/remote_info.py +138 -0
- alomancy-0.2.0/src/alomancy/core/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/core/base_active_learning.py +319 -0
- alomancy-0.2.0/src/alomancy/core/standard_active_learning.py +682 -0
- alomancy-0.2.0/src/alomancy/database/__init__.py +3 -0
- alomancy-0.2.0/src/alomancy/database/global_database.py +229 -0
- alomancy-0.2.0/src/alomancy/high_accuracy_evaluation/dft/__init__.py +51 -0
- alomancy-0.2.0/src/alomancy/high_accuracy_evaluation/dft/run_qe.py +166 -0
- alomancy-0.2.0/src/alomancy/high_accuracy_evaluation/dft/run_vasp.py +90 -0
- alomancy-0.2.0/src/alomancy/initialize/__init__.pyu +0 -0
- alomancy-0.2.0/src/alomancy/initialize/amorphize.py +116 -0
- alomancy-0.2.0/src/alomancy/initialize/initialization_structure_list.py +277 -0
- alomancy-0.2.0/src/alomancy/initialize/mp_interface.py +77 -0
- alomancy-0.2.0/src/alomancy/initialize/singles_dimers_trimers.py +93 -0
- alomancy-0.2.0/src/alomancy/initialize/stretch_and_compress.py +65 -0
- alomancy-0.2.0/src/alomancy/mlip/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/mlip/get_mace_eval_info.py +38 -0
- alomancy-0.2.0/src/alomancy/mlip/mace_wfl.py +282 -0
- alomancy-0.2.0/src/alomancy/remote_submission/__init__.py +15 -0
- alomancy-0.2.0/src/alomancy/remote_submission/executor.py +158 -0
- alomancy-0.2.0/src/alomancy/remote_submission/submitters.py +147 -0
- alomancy-0.2.0/src/alomancy/structure_generation/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/structure_generation/find_high_sd_structures.py +250 -0
- alomancy-0.2.0/src/alomancy/structure_generation/md/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/structure_generation/md/generate_structures.py +155 -0
- alomancy-0.2.0/src/alomancy/structure_generation/md/md_wfl.py +220 -0
- alomancy-0.2.0/src/alomancy/structure_generation/select_initial_structures.py +194 -0
- alomancy-0.2.0/src/alomancy/utils/__init__.py +0 -0
- alomancy-0.2.0/src/alomancy/utils/clean_structures.py +61 -0
- alomancy-0.2.0/src/alomancy/utils/dft_utils.py +67 -0
- alomancy-0.2.0/src/alomancy/utils/file_saving_and_parsing.py +23 -0
- alomancy-0.2.0/src/alomancy/utils/logging_config.py +55 -0
- alomancy-0.2.0/src/alomancy/utils/test_train_manager.py +136 -0
- alomancy-0.2.0/src/alomancy/version.py +24 -0
- alomancy-0.2.0/src/alomancy.egg-info/PKG-INFO +353 -0
- alomancy-0.2.0/src/alomancy.egg-info/SOURCES.txt +100 -0
- alomancy-0.2.0/src/alomancy.egg-info/dependency_links.txt +1 -0
- alomancy-0.2.0/src/alomancy.egg-info/requires.txt +35 -0
- alomancy-0.2.0/src/alomancy.egg-info/scm_file_list.json +96 -0
- alomancy-0.2.0/src/alomancy.egg-info/scm_version.json +8 -0
- alomancy-0.2.0/src/alomancy.egg-info/top_level.txt +1 -0
- alomancy-0.2.0/tests/__init__.py +0 -0
- alomancy-0.2.0/tests/analysis_tests/__init__.py +0 -0
- alomancy-0.2.0/tests/analysis_tests/test_plotting.py +247 -0
- alomancy-0.2.0/tests/conftest.py +192 -0
- alomancy-0.2.0/tests/core_tests/test_base_active_learning.py +801 -0
- alomancy-0.2.0/tests/core_tests/test_standard_active_learning.py +1449 -0
- alomancy-0.2.0/tests/database_tests/__init__.py +0 -0
- alomancy-0.2.0/tests/database_tests/test_global_database.py +368 -0
- alomancy-0.2.0/tests/high_acc_tests/test_dft_registry.py +64 -0
- alomancy-0.2.0/tests/high_acc_tests/test_dft_utils.py +156 -0
- alomancy-0.2.0/tests/high_acc_tests/test_quantum_espresso.py +197 -0
- alomancy-0.2.0/tests/high_acc_tests/test_run_vasp.py +132 -0
- alomancy-0.2.0/tests/initialize_tests/__init__.py +0 -0
- alomancy-0.2.0/tests/initialize_tests/test_amorphize.py +95 -0
- alomancy-0.2.0/tests/initialize_tests/test_create_initialization.py +434 -0
- alomancy-0.2.0/tests/initialize_tests/test_initialization.py +235 -0
- alomancy-0.2.0/tests/initialize_tests/test_singles_dimers_trimers.py +183 -0
- alomancy-0.2.0/tests/initialize_tests/test_stretch_and_compress.py +135 -0
- alomancy-0.2.0/tests/mlip_train_tests/test_mace_training.py +319 -0
- alomancy-0.2.0/tests/struc_gen_tests/test_structure_generation.py +1299 -0
- alomancy-0.2.0/tests/utils_tests/test_utilities.py +636 -0
alomancy-0.2.0/.coverage
ADDED
|
Binary file
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
name: CI/CD Pipeline
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, master, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, master, develop ]
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.10"
|
|
21
|
+
|
|
22
|
+
- name: Cache pip packages
|
|
23
|
+
uses: actions/cache@v4
|
|
24
|
+
with:
|
|
25
|
+
path: ~/.cache/pip
|
|
26
|
+
key: ${{ runner.os }}-pip-lint-${{ hashFiles('**/pyproject.toml') }}
|
|
27
|
+
restore-keys: |
|
|
28
|
+
${{ runner.os }}-pip-lint-
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
pip install ruff mypy
|
|
34
|
+
pip install -e ".[dev]"
|
|
35
|
+
|
|
36
|
+
- name: Lint with ruff
|
|
37
|
+
run: ruff check src/alomancy/core src/alomancy/analysis src/alomancy/configs src/alomancy/utils src/alomancy/high_accuracy_evaluation src/alomancy/mlip src/alomancy/structure_generation tests
|
|
38
|
+
|
|
39
|
+
- name: Check formatting with ruff
|
|
40
|
+
run: ruff format --check src/alomancy/core src/alomancy/analysis src/alomancy/configs src/alomancy/utils src/alomancy/high_accuracy_evaluation src/alomancy/mlip src/alomancy/structure_generation tests
|
|
41
|
+
|
|
42
|
+
- name: Type check with mypy
|
|
43
|
+
run: mypy src/alomancy
|
|
44
|
+
continue-on-error: true # Allow to continue if mypy fails
|
|
45
|
+
|
|
46
|
+
test:
|
|
47
|
+
runs-on: ${{ matrix.os }}
|
|
48
|
+
needs: lint # Run tests after linting passes
|
|
49
|
+
strategy:
|
|
50
|
+
matrix:
|
|
51
|
+
os: [ubuntu-latest, macos-latest]
|
|
52
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
53
|
+
exclude:
|
|
54
|
+
# Exclude some combinations to reduce CI time
|
|
55
|
+
- os: macos-latest
|
|
56
|
+
python-version: "3.11"
|
|
57
|
+
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
|
|
61
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
62
|
+
uses: actions/setup-python@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: ${{ matrix.python-version }}
|
|
65
|
+
|
|
66
|
+
- name: Cache pip packages
|
|
67
|
+
uses: actions/cache@v4
|
|
68
|
+
with:
|
|
69
|
+
path: ~/.cache/pip
|
|
70
|
+
key: ${{ runner.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('**/pyproject.toml') }}
|
|
71
|
+
restore-keys: |
|
|
72
|
+
${{ runner.os }}-py${{ matrix.python-version }}-pip-
|
|
73
|
+
|
|
74
|
+
- name: Install dependencies
|
|
75
|
+
run: |
|
|
76
|
+
python -m pip install --upgrade pip
|
|
77
|
+
pip install -e ".[dev]"
|
|
78
|
+
|
|
79
|
+
- name: Test with pytest
|
|
80
|
+
run: |
|
|
81
|
+
pytest --cov=alomancy --cov-report=xml --cov-fail-under=70 -v tests/
|
|
82
|
+
env:
|
|
83
|
+
ALOMANCY_TEST_MODE: "1"
|
|
84
|
+
ALOMANCY_MOCK_EXTERNAL: "1"
|
|
85
|
+
|
|
86
|
+
- name: Upload coverage to Codecov
|
|
87
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
|
|
88
|
+
uses: codecov/codecov-action@v5
|
|
89
|
+
with:
|
|
90
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
91
|
+
file: ./coverage.xml
|
|
92
|
+
flags: unittests
|
|
93
|
+
name: codecov-umbrella
|
|
94
|
+
fail_ci_if_error: false
|
|
95
|
+
|
|
96
|
+
integration-test:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
needs: test
|
|
99
|
+
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
|
|
100
|
+
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
|
|
104
|
+
- name: Set up Python 3.10
|
|
105
|
+
uses: actions/setup-python@v5
|
|
106
|
+
with:
|
|
107
|
+
python-version: "3.10"
|
|
108
|
+
|
|
109
|
+
- name: Cache pip packages
|
|
110
|
+
uses: actions/cache@v4
|
|
111
|
+
with:
|
|
112
|
+
path: ~/.cache/pip
|
|
113
|
+
key: ${{ runner.os }}-integration-pip-${{ hashFiles('**/pyproject.toml') }}
|
|
114
|
+
|
|
115
|
+
- name: Install dependencies
|
|
116
|
+
run: |
|
|
117
|
+
python -m pip install --upgrade pip
|
|
118
|
+
pip install -e ".[dev]"
|
|
119
|
+
|
|
120
|
+
- name: Run integration tests
|
|
121
|
+
run: |
|
|
122
|
+
pytest -m integration tests/ -v
|
|
123
|
+
env:
|
|
124
|
+
ALOMANCY_TEST_MODE: "1"
|
|
125
|
+
ALOMANCY_MOCK_EXTERNAL: "1"
|
|
126
|
+
|
|
127
|
+
build:
|
|
128
|
+
runs-on: ubuntu-latest
|
|
129
|
+
needs: [test, lint]
|
|
130
|
+
if: github.event_name == 'release' || github.event_name == 'push'
|
|
131
|
+
|
|
132
|
+
steps:
|
|
133
|
+
- uses: actions/checkout@v4
|
|
134
|
+
with:
|
|
135
|
+
fetch-depth: 0 # Needed for setuptools_scm
|
|
136
|
+
|
|
137
|
+
- name: Set up Python
|
|
138
|
+
uses: actions/setup-python@v5
|
|
139
|
+
with:
|
|
140
|
+
python-version: "3.10"
|
|
141
|
+
|
|
142
|
+
- name: Install build dependencies
|
|
143
|
+
run: |
|
|
144
|
+
python -m pip install --upgrade pip
|
|
145
|
+
pip install build twine
|
|
146
|
+
|
|
147
|
+
- name: Build package
|
|
148
|
+
run: python -m build
|
|
149
|
+
|
|
150
|
+
- name: Check package
|
|
151
|
+
run: twine check dist/*
|
|
152
|
+
|
|
153
|
+
- name: Upload build artifacts
|
|
154
|
+
uses: actions/upload-artifact@v4
|
|
155
|
+
with:
|
|
156
|
+
name: dist
|
|
157
|
+
path: dist/
|
|
158
|
+
|
|
159
|
+
publish:
|
|
160
|
+
runs-on: ubuntu-latest
|
|
161
|
+
needs: build
|
|
162
|
+
if: github.event_name == 'release'
|
|
163
|
+
environment: release # Use environment protection
|
|
164
|
+
|
|
165
|
+
steps:
|
|
166
|
+
- name: Download build artifacts
|
|
167
|
+
uses: actions/download-artifact@v4
|
|
168
|
+
with:
|
|
169
|
+
name: dist
|
|
170
|
+
path: dist/
|
|
171
|
+
|
|
172
|
+
- name: Publish to PyPI
|
|
173
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
174
|
+
with:
|
|
175
|
+
user: __token__
|
|
176
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
177
|
+
|
|
178
|
+
documentation:
|
|
179
|
+
runs-on: ubuntu-latest
|
|
180
|
+
needs: test
|
|
181
|
+
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
|
|
182
|
+
permissions:
|
|
183
|
+
contents: write
|
|
184
|
+
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/checkout@v4
|
|
187
|
+
|
|
188
|
+
- name: Set up Python
|
|
189
|
+
uses: actions/setup-python@v5
|
|
190
|
+
with:
|
|
191
|
+
python-version: "3.10"
|
|
192
|
+
|
|
193
|
+
- name: Cache pip packages
|
|
194
|
+
uses: actions/cache@v4
|
|
195
|
+
with:
|
|
196
|
+
path: ~/.cache/pip
|
|
197
|
+
key: ${{ runner.os }}-docs-pip-${{ hashFiles('**/pyproject.toml') }}
|
|
198
|
+
|
|
199
|
+
- name: Install dependencies
|
|
200
|
+
run: |
|
|
201
|
+
python -m pip install --upgrade pip
|
|
202
|
+
pip install -e ".[docs]"
|
|
203
|
+
|
|
204
|
+
- name: Build documentation
|
|
205
|
+
id: build_docs
|
|
206
|
+
run: sphinx-build -b html docs docs/_build/html
|
|
207
|
+
continue-on-error: true
|
|
208
|
+
|
|
209
|
+
- name: Deploy to GitHub Pages
|
|
210
|
+
if: steps.build_docs.outcome == 'success'
|
|
211
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
212
|
+
with:
|
|
213
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
214
|
+
publish_dir: ./docs/_build/html
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Minimal pre-commit configuration
|
|
2
|
+
repos:
|
|
3
|
+
# General code quality
|
|
4
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
+
rev: v4.5.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: check-yaml
|
|
11
|
+
- id: check-toml
|
|
12
|
+
- id: debug-statements
|
|
13
|
+
|
|
14
|
+
# Python formatting and linting
|
|
15
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
+
rev: v0.1.15
|
|
17
|
+
hooks:
|
|
18
|
+
- id: ruff
|
|
19
|
+
args: [--fix]
|
|
20
|
+
- id: ruff-format
|
|
21
|
+
|
|
22
|
+
# Fast unit tests only
|
|
23
|
+
- repo: local
|
|
24
|
+
hooks:
|
|
25
|
+
- id: pytest-unit
|
|
26
|
+
name: Unit tests
|
|
27
|
+
entry: pytest
|
|
28
|
+
language: system
|
|
29
|
+
pass_filenames: false
|
|
30
|
+
always_run: true
|
|
31
|
+
args: [-m, "not integration and not slow", --tb=line, -q, tests/]
|
|
32
|
+
stages: [pre-commit]
|
|
33
|
+
|
|
34
|
+
exclude: |
|
|
35
|
+
(?x)^(
|
|
36
|
+
\.git/|
|
|
37
|
+
__pycache__/|
|
|
38
|
+
build/|
|
|
39
|
+
dist/|
|
|
40
|
+
\.venv/|
|
|
41
|
+
src/alomancy/_?version\.py
|
|
42
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
os: ubuntu-22.04
|
|
5
|
+
tools:
|
|
6
|
+
python: "3.11"
|
|
7
|
+
|
|
8
|
+
python:
|
|
9
|
+
install:
|
|
10
|
+
- method: pip
|
|
11
|
+
path: .
|
|
12
|
+
extra_requirements:
|
|
13
|
+
- docs
|
|
14
|
+
- requirements: docs/requirements.txt
|
|
15
|
+
|
|
16
|
+
sphinx:
|
|
17
|
+
configuration: docs/conf.py
|
|
18
|
+
fail_on_warning: false
|
|
19
|
+
|
|
20
|
+
formats:
|
|
21
|
+
- pdf
|
|
22
|
+
- htmlzip
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] - 2026-07-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Per-fit validation set carving**: `mace_fit` now carves a per-committee-member held-out validation set from the training data before calling MACE. Eligible structures are those with `config_type` in `mlip_committee.valid_config_types` (defaults to `initialization.test_config_types`) or `"high_sd"`. Fraction is controlled by `mlip_committee.valid_fraction` (default 0.05). When no eligible structures exist or the fraction rounds to zero, MACE is pointed directly at the shared `train_set.xyz` without writing a redundant copy. Each member uses seed `seed + fit_idx` for reproducible but distinct splits. The helper `_select_validation_split` is module-level for testability.
|
|
14
|
+
- **Log-scale MAE plots**: `mae_al_loop_plot` now renders the y-axis on a log scale, making error improvements across orders of magnitude visible across many AL loops.
|
|
15
|
+
- **Pluggable DFT backend**: `high_accuracy_evaluation` now supports multiple calculators via a `calculator` config key (default `"qe"`). The registry in `high_accuracy_evaluation/dft/__init__.py` resolves `(run_sp_fn, run_go_fn)` at runtime with lazy imports, so installing only one calculator does not break the other.
|
|
16
|
+
- **VASP backend** (`high_accuracy_evaluation/dft/run_vasp.py`): `run_sp_vasp` / `run_go_vasp` mirror the QE interface exactly. `create_vasp_calc_object` builds a `Vasp` calculator with sensible INCAR defaults (PBE, ENCUT=500, non-self-consistent geometry relaxation via BFGS). Monkhorst-Pack k-points derived from the cell via the shared `generate_kpts` utility.
|
|
17
|
+
- **Shared DFT utilities** (`utils/dft_utils.py`): `generate_kpts`, `_build_srun_command`, `_run_sp`, and `_run_go` extracted from `run_qe.py` and shared by both backends. Neither backend duplicates this logic.
|
|
18
|
+
- **Mismatched-kwargs warning**: `warn_mismatched_kwargs(calculator, job_dict)` logs a WARNING when config keys belonging to a different calculator (e.g. `vasp_input_kwargs` in a QE run) are present, so misconfigured YAMLs are caught early.
|
|
19
|
+
- **`high_sd` config_type for AL loop structures**: Structures returned by `high_accuracy_evaluation` are now tagged `config_type="high_sd"` with `atoms.info["al_loop"] = <loop>` metadata, replacing the previous `al_loop_N` per-loop types. This gives a stable, queryable label across all loops.
|
|
20
|
+
- **GlobalDatabase**: Persistent sage_lib Partition (hybrid HDF5+SQLite) storing all DFT-evaluated structures across AL loops. Deduplication by (config_type, formula) prevents double-adding IsolatedAtom and init_MP entries from multiple datasets. REF_forces arrays serialised into atoms.info for round-trip storage.
|
|
21
|
+
- **DB-aware initialization**: `compute_initialization_needs()` queries the GlobalDatabase to determine what structures still need to be generated before starting DFT. Replaces the old `d_t_s_a_ratio` + `target_non_mp_structures_to_add` API with explicit per-type counts: `num_dimers_per_combo`, `num_trimers_per_combo`, `num_amorphous`, `num_stretch_compress_per_mp`.
|
|
22
|
+
- **Structured logging**: All 108 `print()` calls replaced with Python `logging` module. `setup_logging(verbose, log_file)` called once at workflow construction. verbose=0 silences console; verbose=1 shows INFO progress; verbose=2 shows DEBUG per-job detail. File handler always captures DEBUG regardless of verbose level. ExPyRe job stdout/stderr captured at DEBUG.
|
|
23
|
+
- **Extra dataset seeding**: Extra datasets are now seeded into the GlobalDatabase *before* `initialize_training_set` is called, so `compute_initialization_needs` accounts for them and avoids regenerating already-provided structures.
|
|
24
|
+
- `log_file` parameter on `BaseActiveLearningWorkflow` (default: `"results/alomancy.log"`)
|
|
25
|
+
- `db_path` parameter on `BaseActiveLearningWorkflow` (default: `"results/global_database"`)
|
|
26
|
+
- Warning if structure generation could contain single-atom structures
|
|
27
|
+
- `"high_sd"` is automatically appended to `selectable_configs` in `select_initial_structures` so that structures from previous AL loops are always eligible for seeding MD, even when `selectable_configs` is explicitly set.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- `train_mlip` abstract method and `ActiveLearningStandardMACE` implementation now receive the full `job_dict` (all four top-level keys) instead of `mlip_committee_job_dict`. `mace_fit` likewise takes `job_dict` so it can read `initialization.test_config_types` as the default validation config types.
|
|
31
|
+
- `ase_remote_submitter` replaces `qe_remote_submitter`; output directories use `ase_output_` prefix instead of `qe_output_`. The interface is otherwise unchanged.
|
|
32
|
+
- `run_qe.py` imports `generate_kpts` and shared runner helpers from `utils/dft_utils.py` instead of defining them locally.
|
|
33
|
+
- AL loop structures now carry `config_type="high_sd"` (stable across loops) rather than `config_type="al_loop_N"` (loop-specific). The originating loop is preserved in `atoms.info["al_loop"]`.
|
|
34
|
+
- Initialization configuration now uses per-type counts instead of ratio-based approach.
|
|
35
|
+
- `initialize_training_set` now has a DB-aware path that only generates missing structures.
|
|
36
|
+
- Logging replaces all bare print() calls; no external API change.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- `select_initial_structures` no longer mutates `Atoms` objects in `train_atoms_list`; selected structures are `.copy()`-d before `mark_structures_for_dft` writes to them. Previously, marking corrupted `config_type` on shared references, causing the `selectable_configs` filter to find zero matches on subsequent AL loops.
|
|
40
|
+
- Batch numbering collision between GO (geometry-optimisation) and SP (single-point) DFT jobs.
|
|
41
|
+
|
|
42
|
+
### Dependencies
|
|
43
|
+
- Added: sage-lib (HDF5+SQLite storage backend for GlobalDatabase)
|
|
44
|
+
- Added: polars (Polars DataFrame used in structure generation std-dev calculations)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## [0.1.1] - 2025-08-14
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
- Initial changelog documentation
|
|
51
|
+
- Optional extra dictionaries to control function behaviour inside core funcitons
|
|
52
|
+
- CI/CD for precommit hooks
|
|
53
|
+
- read the docs documentation
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
- Improved package documentation and examples
|
|
57
|
+
- Improved testing
|
|
58
|
+
- Folded eval MLIP into the mlip_committee function
|
|
59
|
+
- mlip_committee now returns a pd.Dataframe of mae_e and mae_f for each loop
|
|
60
|
+
- ruff formatting for everything
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## [0.1.0] - 2025-08-13
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- Initial release of ALomancy package
|
|
67
|
+
- Standard MACE active learning workflow (`ActiveLearningStandardMACE`)
|
|
68
|
+
- Support for remote job execution via ExPyRe
|
|
69
|
+
- Structure generation using molecular dynamics
|
|
70
|
+
- MLIP committee training and evaluation
|
|
71
|
+
- High-accuracy DFT evaluation pipeline with Quantum Espresso
|
|
72
|
+
- Configuration management for HPC systems
|
|
73
|
+
- Example workflows and configuration files
|
|
74
|
+
|
|
75
|
+
### Features
|
|
76
|
+
- **Core Workflows**
|
|
77
|
+
- Base active learning framework
|
|
78
|
+
- MACE-specific implementation
|
|
79
|
+
- Configurable loop iteration
|
|
80
|
+
|
|
81
|
+
- **Structure Generation**
|
|
82
|
+
- Initial structure selection
|
|
83
|
+
- Molecular dynamics simulations
|
|
84
|
+
- High standard deviation structure identification
|
|
85
|
+
|
|
86
|
+
- **Machine Learning**
|
|
87
|
+
- MACE model training and committee evaluation
|
|
88
|
+
- Uncertainty quantification
|
|
89
|
+
- Model performance metrics
|
|
90
|
+
|
|
91
|
+
- **Remote Execution**
|
|
92
|
+
- HPC job submission and monitoring
|
|
93
|
+
- Queue system integration
|
|
94
|
+
- Automatic result collection
|
|
95
|
+
|
|
96
|
+
- **Configuration**
|
|
97
|
+
- YAML-based job configuration
|
|
98
|
+
- HPC system definitions
|
|
99
|
+
- Flexible parameter management
|
|
100
|
+
|
|
101
|
+
[Unreleased]: https://github.com/your-username/alomancy/compare/v0.1.1...HEAD
|
|
102
|
+
[0.1.1]: https://github.com/your-username/alomancy/compare/v0.1.0...v0.1.1
|
|
103
|
+
[0.1.0]: https://github.com/your-username/alomancy/releases/tag/v0.1.0
|
alomancy-0.2.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install in development mode (required before anything else)
|
|
9
|
+
pip install -e ".[dev]"
|
|
10
|
+
|
|
11
|
+
# Lint and format
|
|
12
|
+
ruff check src/ tests/
|
|
13
|
+
ruff format src/ tests/
|
|
14
|
+
|
|
15
|
+
# Type check
|
|
16
|
+
mypy src/alomancy
|
|
17
|
+
|
|
18
|
+
# Run all tests (fast — skips coverage overhead)
|
|
19
|
+
/home/jholl/.venvs/al_install/bin/pytest --no-cov
|
|
20
|
+
|
|
21
|
+
# Run only unit tests (no HPC/MACE/QE/MP required)
|
|
22
|
+
/home/jholl/.venvs/al_install/bin/pytest -m unit --no-cov
|
|
23
|
+
|
|
24
|
+
# Run a single test file
|
|
25
|
+
/home/jholl/.venvs/al_install/bin/pytest tests/core_tests/test_base_active_learning.py --no-cov
|
|
26
|
+
|
|
27
|
+
# Run by marker
|
|
28
|
+
pytest -m "not requires_external"
|
|
29
|
+
|
|
30
|
+
# Run with coverage (slow — pyproject.toml sets --cov-fail-under=80)
|
|
31
|
+
pytest --cov=alomancy --cov-report=html
|
|
32
|
+
|
|
33
|
+
# Run in CI mode (env vars disable real external calls)
|
|
34
|
+
ALOMANCY_TEST_MODE=1 ALOMANCY_MOCK_EXTERNAL=1 pytest tests/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Pre-commit hooks run `ruff` (with `--fix`) and unit tests on every commit. Install with `pre-commit install`.
|
|
38
|
+
|
|
39
|
+
## Logging
|
|
40
|
+
|
|
41
|
+
All output goes through Python's `logging` module. No bare `print()` calls exist in `src/`.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
# Single configuration point — called in BaseActiveLearningWorkflow.__init__
|
|
45
|
+
from alomancy.utils.logging_config import setup_logging
|
|
46
|
+
setup_logging(verbose=1, log_file="results/my_run.log")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| `verbose` | Console | File (`results/alomancy.log`) |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| 0 (default) | WARNING+ only | DEBUG always |
|
|
52
|
+
| 1 | INFO (step progress) | DEBUG always |
|
|
53
|
+
| 2+ | DEBUG (per-job detail) | DEBUG always |
|
|
54
|
+
|
|
55
|
+
The file handler always writes DEBUG so every run produces a complete timestamped record. ExPyRe job stdout/stderr are captured at DEBUG level.
|
|
56
|
+
|
|
57
|
+
Each module declares `logger = logging.getLogger(__name__)` — no per-module setup needed. The `"expyre"` logger is also routed through the same handlers.
|
|
58
|
+
|
|
59
|
+
**Testing logs:** pytest's `caplog` does not intercept alomancy logs because `propagate=False` is set on the root alomancy logger. Attach a `logging.Handler` directly to `logging.getLogger("alomancy")` in tests that need to assert on log output (see `test_seed_logs_message` in `test_base_active_learning.py`).
|
|
60
|
+
|
|
61
|
+
## Architecture
|
|
62
|
+
|
|
63
|
+
ALomancy implements active learning (AL) workflows for training machine-learned interatomic potentials (MLIPs). The core loop is: **train MLIP committee → MD structure generation → uncertainty-based selection → DFT evaluation → extend training set → repeat**.
|
|
64
|
+
|
|
65
|
+
### Core layer (`src/alomancy/core/`)
|
|
66
|
+
|
|
67
|
+
`BaseActiveLearningWorkflow` (abstract) owns the AL loop in its `run()` method. It calls four abstract methods that concrete subclasses must implement:
|
|
68
|
+
|
|
69
|
+
- `initialize_training_set(base_name)` — produce initial `(train_xyzs, test_xyzs)` atom lists
|
|
70
|
+
- `train_mlip(base_name, job_dict)` → `pd.DataFrame` of evaluation metrics
|
|
71
|
+
- `generate_structures(base_name, job_dict, train_atoms_list)` → `list[Atoms]` of high-uncertainty candidates
|
|
72
|
+
- `high_accuracy_evaluation(base_name, job_dict, structures)` → `list[Atoms]` with DFT results
|
|
73
|
+
|
|
74
|
+
`ActiveLearningStandardMACE` (in `standard_active_learning.py`) is the production implementation: MACE committee for the MLIP, ASE MD for structure generation, Quantum Espresso (QE) for DFT.
|
|
75
|
+
|
|
76
|
+
All results land under `results/<base_name>/` with a fixed subdirectory layout. The workflow has **idempotency logic** throughout: each step checks for existing output files and skips remote submission if they exist, enabling restart after failures.
|
|
77
|
+
|
|
78
|
+
### Configuration (`src/alomancy/configs/`)
|
|
79
|
+
|
|
80
|
+
`load_dictionaries(config_path)` reads a YAML file into a `jobs_dict` passed to the workflow constructor. Three top-level keys are required: `initialization`, `mlip_committee`, `structure_generation`, `high_accuracy_evaluation`. Each carries a `name`, `max_time`, and `hpc` sub-dict (`hpc_name`, `partitions`, `pre_cmds`).
|
|
81
|
+
|
|
82
|
+
`RemoteInfo` and `get_remote_info()` convert a job sub-dict into an Expyre `RemoteInfo` object. The `sys_name` field maps to an Expyre system name (defined in the user's Expyre config, not in this repo).
|
|
83
|
+
|
|
84
|
+
### Remote execution (`src/alomancy/utils/remote_job_executor.py`)
|
|
85
|
+
|
|
86
|
+
`RemoteJobExecutor` wraps Expyre's `ExPyRe`. Call pattern: `submit_multiple_jobs → start_all_jobs → wait_for_all_jobs → cleanup_jobs`. The convenience method `run_and_wait` does all four steps. **Note**: `wait_for_all_jobs` is intentionally called twice in `run_and_wait` to ensure results sync locally from the remote.
|
|
87
|
+
|
|
88
|
+
**Remote deployment**: ExPyRe serializes functions **by reference** (module path + function name). The remote machine imports the module to resolve the function at run time. When changing the signature or body of any function submitted remotely (e.g. `mace_fit`), you must reinstall the updated package on the remote machine (`pip install -e .` there) before restarting, or jobs will import the old code and fail with a `TypeError` at the call site — typically hours after submission.
|
|
89
|
+
|
|
90
|
+
### Module responsibilities
|
|
91
|
+
|
|
92
|
+
| Module | Purpose |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `database/global_database.py` | `GlobalDatabase` — persistent sage_lib `Partition` (hybrid HDF5+SQLite) storing all post-DFT structures; handles dedup by `(config_type, formula)` and forces round-trip via `atoms.info['_REF_forces']` |
|
|
95
|
+
| `initialize/initialization_structure_list.py` | `create_initialization_atoms_list()` generates initial structures (dimers, trimers, amorphous, MP); `compute_initialization_needs()` queries the DB and returns per-combo counts of what's still missing |
|
|
96
|
+
| `initialize/mp_interface.py` | Fetches structures from the Materials Project API (`mp_api`) |
|
|
97
|
+
| `mlip/committee_remote_submitter.py` | Submits N MACE training jobs (committee); each fit lands in `results/<base>/mlip_committee/fit_<i>/` |
|
|
98
|
+
| `mlip/mace_wfl.py` | Wraps the `mace_fit` CLI call for a single committee member |
|
|
99
|
+
| `mlip/get_mace_eval_info.py` | Reads trained MACE model error metrics into a DataFrame |
|
|
100
|
+
| `structure_generation/md/md_remote_submitter.py` | Submits MD runs (one per input structure) and force-evaluation jobs across all committee members |
|
|
101
|
+
| `structure_generation/find_high_sd_structures.py` | Selects structures with highest force standard-deviation across the committee (uncertainty metric); uses Polars DataFrames |
|
|
102
|
+
| `structure_generation/select_initial_structures.py` | Picks seed structures from training data for MD |
|
|
103
|
+
| `high_accuracy_evaluation/dft/qe_remote_submitter.py` | Batches structures and submits QE jobs; supports both single-point (`run_sp_qe`) and geometry-optimisation (`run_go_qe`) |
|
|
104
|
+
| `utils/clean_structures.py` | Validates/cleans ASE Atoms objects after DFT (sets `config_type`, removes bad structures) |
|
|
105
|
+
| `utils/test_train_manager.py` | Splits atom lists into train/test and merges extra datasets |
|
|
106
|
+
| `analysis/plotting.py` | Plots MAE vs AL loop number |
|
|
107
|
+
|
|
108
|
+
### Key conventions
|
|
109
|
+
|
|
110
|
+
- All file I/O uses `extxyz` format via ASE's `read`/`write`.
|
|
111
|
+
- Energy and force labels stored in `atoms.info["REF_energy"]` and `atoms.arrays["REF_forces"]`. **Never use bare `"energy"` as an info key** — ASE moves it to the calculator on extxyz read, losing it from `atoms.info`.
|
|
112
|
+
- Structures that need geometry optimisation carry `atoms.info["needs_relaxation"] = True`.
|
|
113
|
+
- `config_type` in `atoms.info` tracks provenance (e.g. `"initialization"`, `"init_dimer"`, `"high_sd"`). Structures selected by the AL loop uncertainty criterion get `config_type="high_sd"` (set by `clean_structures` in `base_active_learning.run()`); the per-loop index is stored separately in `atoms.info["al_loop"]` via `extra_metadata`. Do not use `"al_loop_N"` as a config_type — the loop number is not embedded in the type string.
|
|
114
|
+
- The `seed` parameter (default `803`) is used everywhere randomness appears for reproducibility.
|
|
115
|
+
- `verbose` is an int: `0` = silent, `>0` = progress prints.
|
|
116
|
+
- The global DB lives at `results/global_database/` (configurable via `db_path` in `BaseActiveLearningWorkflow.__init__`). Only DFT-evaluated structures (with `REF_energy`/`REF_forces`) are stored in it.
|
|
117
|
+
- Initialization config uses individual counts (`num_dimers_per_combo`, `num_trimers_per_combo`, `num_amorphous`, `num_stretch_compress_per_mp`) rather than the old `d_t_s_a_ratio` + `target_non_mp_structures_to_add`.
|
|
118
|
+
- `IsolatedAtom` and `init_MP` config_types are deduplicated by `(config_type, formula)` in `GlobalDatabase.add_structures()`; other config_types (dimers, trimers, amorphous, AL loop structures) are always added without exact dedup.
|
|
119
|
+
- In `initialize_training_set`, the DB is checked **first** (`compute_initialization_needs` against current DB state). `extra_datasets` are seeded only if the DB is still missing some initialization targets, then needs are re-checked before any structure generation. An already-populated DB is always honoured before consulting extra datasets.
|
|
120
|
+
- `initialize_training_set` has two paths: (1) fast path — if `initial_train_file_path` and `initial_test_file_path` exist on disk, load them directly; (2) DB path — call `compute_initialization_needs`, generate only missing structures, run DFT, build train/test from `db.get_all_as_atoms()`.
|
|
121
|
+
- `jobs_dict["high_accuracy_evaluation"]["name"]` must equal `"high_accuracy_evaluation"`. `ase_remote_submitter` always writes output to the hardcoded path `results/<base>/high_accuracy_evaluation/batch_N/`; the result-collection glob in `standard_active_learning.high_accuracy_evaluation` uses `output_name` (i.e. the `name` field) as the subdirectory. If they diverge the glob finds nothing, an empty sentinel is written, and the phase is permanently marked done with zero structures.
|
|
122
|
+
- Guard user config errors in functions that run remotely with `if`/`raise ValueError(...)`, not `assert`. Python's `-O` flag (common in HPC module environments) strips all asserts silently, deferring config failures to the remote job hours after submission.
|
|
123
|
+
- GO (geometry-optimisation) and SP (single-point) QE batches use different numbering ranges to avoid directory collision. `current_batches` = count of existing batch dirs (the directory-name offset). `n_new_batches` = new GO batches needed. GO dirs are numbered `[current_batches, current_batches + n_new_batches)`; SP dirs start at `current_batches + n_new_batches`. The loop index `i` (0-based) is used to slice the trimmed structures list; `batch_num = current_batches + i` is used for the directory name only.
|
|
124
|
+
|
|
125
|
+
### sage_lib / GlobalDatabase internals
|
|
126
|
+
|
|
127
|
+
`sage_lib.Partition` (hybrid HDF5+SQLite) only persists `atoms.info`, **not** `atoms.arrays`. `REF_forces` must be serialised into info before storage (`a.info["_REF_forces"] = forces.tolist()`) and restored on retrieval (`np.array(meta.pop("_REF_forces"))`). This is handled transparently by `GlobalDatabase._prepare_for_storage` / `_atoms_from_container`.
|
|
128
|
+
|
|
129
|
+
`count_all_by_config_type_and_formula()` does a single O(N) scan over all containers and returns `{config_type: {formula: count}}`. Use it instead of calling `count_by_config_type_and_formula()` multiple times.
|
|
130
|
+
|
|
131
|
+
### Test suite
|
|
132
|
+
|
|
133
|
+
Tests live in `tests/` with subdirectories mirroring the source layout. New directories added:
|
|
134
|
+
- `tests/database_tests/` — GlobalDatabase dedup and round-trip tests
|
|
135
|
+
- `tests/initialize_tests/` — `compute_initialization_needs` delta logic
|
|
136
|
+
|
|
137
|
+
All tests are marked `@pytest.mark.unit` **on each individual test method** (run without any external services). Do not apply the mark only at the class level — pytest's mark inheritance is configuration-dependent and `pytest -m unit` may miss class-level-only marks. The suite tests real code paths — no "mock-testing" (asserting that a mock returns what you told it to return).
|
|
138
|
+
|
|
139
|
+
Key test patterns:
|
|
140
|
+
- `GlobalDatabase` tests use real sage_lib via `GlobalDatabase(str(tmp_path / "db"))` for genuine round-trip verification.
|
|
141
|
+
- `compute_initialization_needs` tests use a `MagicMock` DB that returns controlled `count_all_by_config_type_and_formula()` dicts.
|
|
142
|
+
- `mace` is patched at the top of `test_standard_active_learning.py` via `sys.modules.setdefault(...)` before the module is imported (avoids GPU dependency at collection time).
|
|
143
|
+
- `wfl` is not installed — never patch or import it in tests.
|