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.
Files changed (102) hide show
  1. alomancy-0.2.0/.coverage +0 -0
  2. alomancy-0.2.0/.github/workflows/ci.yml +214 -0
  3. alomancy-0.2.0/.gitignore +17 -0
  4. alomancy-0.2.0/.pre-commit-config.yaml +42 -0
  5. alomancy-0.2.0/.readthedocs.yaml +22 -0
  6. alomancy-0.2.0/CHANGELOG.md +103 -0
  7. alomancy-0.2.0/CLAUDE.md +143 -0
  8. alomancy-0.2.0/PKG-INFO +353 -0
  9. alomancy-0.2.0/README.md +300 -0
  10. alomancy-0.2.0/TESTING.md +340 -0
  11. alomancy-0.2.0/__init__.py +0 -0
  12. alomancy-0.2.0/docs/api/configs.rst +10 -0
  13. alomancy-0.2.0/docs/api/core.rst +25 -0
  14. alomancy-0.2.0/docs/api/database.rst +6 -0
  15. alomancy-0.2.0/docs/api/high_accuracy_evaluation.rst +23 -0
  16. alomancy-0.2.0/docs/api/index.rst +15 -0
  17. alomancy-0.2.0/docs/api/mlip.rst +14 -0
  18. alomancy-0.2.0/docs/api/structure_generation.rst +18 -0
  19. alomancy-0.2.0/docs/api/utils.rst +22 -0
  20. alomancy-0.2.0/docs/conf.py +62 -0
  21. alomancy-0.2.0/docs/contributing.md +37 -0
  22. alomancy-0.2.0/docs/examples.md +225 -0
  23. alomancy-0.2.0/docs/index.md +32 -0
  24. alomancy-0.2.0/docs/installation.md +39 -0
  25. alomancy-0.2.0/docs/quickstart.md +85 -0
  26. alomancy-0.2.0/docs/requirements.txt +4 -0
  27. alomancy-0.2.0/docs/usage.rst +6 -0
  28. alomancy-0.2.0/examples/basic_use/basic_use.py +36 -0
  29. alomancy-0.2.0/examples/basic_use/input_files/hpc_config.yaml +61 -0
  30. alomancy-0.2.0/examples/basic_use/input_files/standard_config.yaml +78 -0
  31. alomancy-0.2.0/pyproject.toml +237 -0
  32. alomancy-0.2.0/pytest.ini +23 -0
  33. alomancy-0.2.0/run_tests.py +82 -0
  34. alomancy-0.2.0/setup.cfg +4 -0
  35. alomancy-0.2.0/src/alomancy/__init__.py +0 -0
  36. alomancy-0.2.0/src/alomancy/analysis/__init__.py +0 -0
  37. alomancy-0.2.0/src/alomancy/analysis/example_committee_al_loop_mae_plot.png +0 -0
  38. alomancy-0.2.0/src/alomancy/analysis/plotting.py +120 -0
  39. alomancy-0.2.0/src/alomancy/configs/__init__.py +0 -0
  40. alomancy-0.2.0/src/alomancy/configs/config_dictionaries.py +56 -0
  41. alomancy-0.2.0/src/alomancy/configs/remote_info.py +138 -0
  42. alomancy-0.2.0/src/alomancy/core/__init__.py +0 -0
  43. alomancy-0.2.0/src/alomancy/core/base_active_learning.py +319 -0
  44. alomancy-0.2.0/src/alomancy/core/standard_active_learning.py +682 -0
  45. alomancy-0.2.0/src/alomancy/database/__init__.py +3 -0
  46. alomancy-0.2.0/src/alomancy/database/global_database.py +229 -0
  47. alomancy-0.2.0/src/alomancy/high_accuracy_evaluation/dft/__init__.py +51 -0
  48. alomancy-0.2.0/src/alomancy/high_accuracy_evaluation/dft/run_qe.py +166 -0
  49. alomancy-0.2.0/src/alomancy/high_accuracy_evaluation/dft/run_vasp.py +90 -0
  50. alomancy-0.2.0/src/alomancy/initialize/__init__.pyu +0 -0
  51. alomancy-0.2.0/src/alomancy/initialize/amorphize.py +116 -0
  52. alomancy-0.2.0/src/alomancy/initialize/initialization_structure_list.py +277 -0
  53. alomancy-0.2.0/src/alomancy/initialize/mp_interface.py +77 -0
  54. alomancy-0.2.0/src/alomancy/initialize/singles_dimers_trimers.py +93 -0
  55. alomancy-0.2.0/src/alomancy/initialize/stretch_and_compress.py +65 -0
  56. alomancy-0.2.0/src/alomancy/mlip/__init__.py +0 -0
  57. alomancy-0.2.0/src/alomancy/mlip/get_mace_eval_info.py +38 -0
  58. alomancy-0.2.0/src/alomancy/mlip/mace_wfl.py +282 -0
  59. alomancy-0.2.0/src/alomancy/remote_submission/__init__.py +15 -0
  60. alomancy-0.2.0/src/alomancy/remote_submission/executor.py +158 -0
  61. alomancy-0.2.0/src/alomancy/remote_submission/submitters.py +147 -0
  62. alomancy-0.2.0/src/alomancy/structure_generation/__init__.py +0 -0
  63. alomancy-0.2.0/src/alomancy/structure_generation/find_high_sd_structures.py +250 -0
  64. alomancy-0.2.0/src/alomancy/structure_generation/md/__init__.py +0 -0
  65. alomancy-0.2.0/src/alomancy/structure_generation/md/generate_structures.py +155 -0
  66. alomancy-0.2.0/src/alomancy/structure_generation/md/md_wfl.py +220 -0
  67. alomancy-0.2.0/src/alomancy/structure_generation/select_initial_structures.py +194 -0
  68. alomancy-0.2.0/src/alomancy/utils/__init__.py +0 -0
  69. alomancy-0.2.0/src/alomancy/utils/clean_structures.py +61 -0
  70. alomancy-0.2.0/src/alomancy/utils/dft_utils.py +67 -0
  71. alomancy-0.2.0/src/alomancy/utils/file_saving_and_parsing.py +23 -0
  72. alomancy-0.2.0/src/alomancy/utils/logging_config.py +55 -0
  73. alomancy-0.2.0/src/alomancy/utils/test_train_manager.py +136 -0
  74. alomancy-0.2.0/src/alomancy/version.py +24 -0
  75. alomancy-0.2.0/src/alomancy.egg-info/PKG-INFO +353 -0
  76. alomancy-0.2.0/src/alomancy.egg-info/SOURCES.txt +100 -0
  77. alomancy-0.2.0/src/alomancy.egg-info/dependency_links.txt +1 -0
  78. alomancy-0.2.0/src/alomancy.egg-info/requires.txt +35 -0
  79. alomancy-0.2.0/src/alomancy.egg-info/scm_file_list.json +96 -0
  80. alomancy-0.2.0/src/alomancy.egg-info/scm_version.json +8 -0
  81. alomancy-0.2.0/src/alomancy.egg-info/top_level.txt +1 -0
  82. alomancy-0.2.0/tests/__init__.py +0 -0
  83. alomancy-0.2.0/tests/analysis_tests/__init__.py +0 -0
  84. alomancy-0.2.0/tests/analysis_tests/test_plotting.py +247 -0
  85. alomancy-0.2.0/tests/conftest.py +192 -0
  86. alomancy-0.2.0/tests/core_tests/test_base_active_learning.py +801 -0
  87. alomancy-0.2.0/tests/core_tests/test_standard_active_learning.py +1449 -0
  88. alomancy-0.2.0/tests/database_tests/__init__.py +0 -0
  89. alomancy-0.2.0/tests/database_tests/test_global_database.py +368 -0
  90. alomancy-0.2.0/tests/high_acc_tests/test_dft_registry.py +64 -0
  91. alomancy-0.2.0/tests/high_acc_tests/test_dft_utils.py +156 -0
  92. alomancy-0.2.0/tests/high_acc_tests/test_quantum_espresso.py +197 -0
  93. alomancy-0.2.0/tests/high_acc_tests/test_run_vasp.py +132 -0
  94. alomancy-0.2.0/tests/initialize_tests/__init__.py +0 -0
  95. alomancy-0.2.0/tests/initialize_tests/test_amorphize.py +95 -0
  96. alomancy-0.2.0/tests/initialize_tests/test_create_initialization.py +434 -0
  97. alomancy-0.2.0/tests/initialize_tests/test_initialization.py +235 -0
  98. alomancy-0.2.0/tests/initialize_tests/test_singles_dimers_trimers.py +183 -0
  99. alomancy-0.2.0/tests/initialize_tests/test_stretch_and_compress.py +135 -0
  100. alomancy-0.2.0/tests/mlip_train_tests/test_mace_training.py +319 -0
  101. alomancy-0.2.0/tests/struc_gen_tests/test_structure_generation.py +1299 -0
  102. alomancy-0.2.0/tests/utils_tests/test_utilities.py +636 -0
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,17 @@
1
+ *traj
2
+ *xyz
3
+ *log
4
+
5
+ mace_general
6
+ results
7
+ working_demo
8
+ MACE_model
9
+ archive
10
+ dist
11
+ build
12
+ version.py
13
+ *.egg-info
14
+ *.egg
15
+ __pycache__
16
+ src/alomancy.egg-info/SOURCES.txt
17
+ src/alomancy.egg-info/dependency_links.txt
@@ -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
@@ -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.