assystant 0.1.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.
- assystant-0.1.0/.github/workflows/clean-install.yml +143 -0
- assystant-0.1.0/.github/workflows/release.yml +104 -0
- assystant-0.1.0/.gitignore +9 -0
- assystant-0.1.0/CHANGELOG.md +23 -0
- assystant-0.1.0/LICENSE +29 -0
- assystant-0.1.0/PKG-INFO +171 -0
- assystant-0.1.0/README.md +141 -0
- assystant-0.1.0/assystant/__init__.py +38 -0
- assystant-0.1.0/assystant/cli.py +194 -0
- assystant-0.1.0/assystant/collect.py +182 -0
- assystant-0.1.0/assystant/config.py +608 -0
- assystant-0.1.0/assystant/data/EleBulk_MatProj_VorVol.csv +88 -0
- assystant-0.1.0/assystant/generate.py +378 -0
- assystant-0.1.0/assystant/incar.py +76 -0
- assystant-0.1.0/assystant/runner.py +105 -0
- assystant-0.1.0/assystant/status.py +125 -0
- assystant-0.1.0/assystant/submit.py +301 -0
- assystant-0.1.0/docs/2026_07_31_assystant_design.md +377 -0
- assystant-0.1.0/docs/2026_07_31_pyiron_workflow_vasp_port_log.md +197 -0
- assystant-0.1.0/docs/2026_08_01_pyiron_workflow_assyst_port_log.md +208 -0
- assystant-0.1.0/docs/behaviour-changes.md +187 -0
- assystant-0.1.0/docs/commands.md +268 -0
- assystant-0.1.0/docs/configuration.md +422 -0
- assystant-0.1.0/docs/implementation-report.txt +136 -0
- assystant-0.1.0/docs/installation.md +209 -0
- assystant-0.1.0/docs/operations.md +325 -0
- assystant-0.1.0/docs/overview.md +108 -0
- assystant-0.1.0/docs/plans/2026-07-31-pyiron-workflow-vasp-0.19-port.md +1557 -0
- assystant-0.1.0/docs/plans/2026-08-01-pyiron-workflow-assyst-0.19-port.md +1299 -0
- assystant-0.1.0/docs/troubleshooting.md +304 -0
- assystant-0.1.0/docs/worked-example.md +265 -0
- assystant-0.1.0/examples/Al_tutorial.yaml +53 -0
- assystant-0.1.0/examples/Fe_smoke.yaml +54 -0
- assystant-0.1.0/examples/Fe_unary.yaml +89 -0
- assystant-0.1.0/pyproject.toml +51 -0
- assystant-0.1.0/tests/conftest.py +72 -0
- assystant-0.1.0/tests/test_collect.py +171 -0
- assystant-0.1.0/tests/test_config.py +227 -0
- assystant-0.1.0/tests/test_generate.py +234 -0
- assystant-0.1.0/tests/test_incar.py +70 -0
- assystant-0.1.0/tests/test_runner.py +82 -0
- assystant-0.1.0/tests/test_status.py +104 -0
- assystant-0.1.0/tests/test_submit.py +402 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Proves a fresh environment can `pip install assystant` with no
|
|
2
|
+
# --no-build-isolation and no pre-installed scientific stack: build sdist and
|
|
3
|
+
# wheel in isolation, install each into an empty venv from outside the
|
|
4
|
+
# checkout, then run the CLI and the test suite against the installed copy.
|
|
5
|
+
#
|
|
6
|
+
# Upstream requirements that are not released yet are built as release
|
|
7
|
+
# candidates by the `upstream` job (see there).
|
|
8
|
+
|
|
9
|
+
name: clean-install
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
branches: [main]
|
|
14
|
+
pull_request:
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
# Release candidates for upstream packages that are not on PyPI yet.
|
|
18
|
+
# For each requirement below, if PyPI already satisfies it nothing is built
|
|
19
|
+
# and the install jobs test against the published release. Otherwise a wheel
|
|
20
|
+
# is built from the upstream pull request that prepares that release (the
|
|
21
|
+
# same way its release workflow will build it) and handed to the install
|
|
22
|
+
# jobs via --find-links. Once the releases are out this job is a no-op and
|
|
23
|
+
# the CANDIDATES entries can be deleted.
|
|
24
|
+
upstream:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
env:
|
|
27
|
+
# requirement | GitHub repo | git ref | versioneer tag to stamp (or "-")
|
|
28
|
+
CANDIDATES: |
|
|
29
|
+
pyiron_workflow_vasp>=0.2.0,<0.3 | pyiron/pyiron_workflow_vasp | pull/7/head | pyiron_workflow_vasp-0.2.0
|
|
30
|
+
pyiron_workflow_assyst>=0.2.0,<0.3 | ligerzero-ai/pyiron_workflow_assyst | pull/2/head | -
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/setup-python@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.12"
|
|
35
|
+
- name: Build wheels for unreleased upstream requirements
|
|
36
|
+
run: |
|
|
37
|
+
set -euo pipefail
|
|
38
|
+
python -m pip install --upgrade pip build pyyaml==6.0.1 toml==0.10.2
|
|
39
|
+
WH="$RUNNER_TEMP/wheelhouse"; mkdir -p "$WH"
|
|
40
|
+
while IFS='|' read -r req repo ref tag; do
|
|
41
|
+
req=$(echo "$req" | xargs); repo=$(echo "$repo" | xargs)
|
|
42
|
+
ref=$(echo "$ref" | xargs); tag=$(echo "$tag" | xargs)
|
|
43
|
+
[ -z "$req" ] && continue
|
|
44
|
+
if python -m pip download -q --no-deps -d "$RUNNER_TEMP/probe" "$req" 2>/dev/null; then
|
|
45
|
+
echo "::notice::$req is on PyPI; testing against the release"
|
|
46
|
+
continue
|
|
47
|
+
fi
|
|
48
|
+
echo "::warning::$req is not on PyPI yet; building a candidate from $repo@$ref"
|
|
49
|
+
src="$RUNNER_TEMP/src-${repo##*/}"
|
|
50
|
+
git clone -q "https://github.com/$repo" "$src"
|
|
51
|
+
git -C "$src" fetch -q origin "$ref"
|
|
52
|
+
git -C "$src" checkout -q FETCH_HEAD
|
|
53
|
+
if [ "$tag" != "-" ]; then
|
|
54
|
+
# -f: the upstream tag may already exist on another commit; this
|
|
55
|
+
# clone is throwaway, so stamping the candidate is local only.
|
|
56
|
+
git -C "$src" -c user.name=ci -c user.email=ci@localhost tag -f "$tag"
|
|
57
|
+
fi
|
|
58
|
+
# pyiron release workflows rewrite == pins into ranges before
|
|
59
|
+
# building; do the same so the candidate carries release metadata.
|
|
60
|
+
script="$src/.github/actions/.support/update_pyproject_dependencies.py"
|
|
61
|
+
if [ -f "$script" ]; then
|
|
62
|
+
(cd "$src" && python "$script" --input_toml=pyproject.toml \
|
|
63
|
+
--lower_bound_yaml=.ci_support/lower-bound.yml \
|
|
64
|
+
--upper_bound_yaml=.ci_support/upper-bound.yml \
|
|
65
|
+
--semantic_upper_bound=minor --always_pin_unstable=yes \
|
|
66
|
+
--output_toml=none \
|
|
67
|
+
--pypi_to_conda_name_map_file=.github/actions/.support/pypi_vs_conda_names.json)
|
|
68
|
+
fi
|
|
69
|
+
python -m build --wheel --outdir "$WH" "$src"
|
|
70
|
+
done <<< "$CANDIDATES"
|
|
71
|
+
# The artifact must exist even when nothing was built, and upload-artifact
|
|
72
|
+
# skips hidden files, so leave a visible manifest.
|
|
73
|
+
(cd "$WH" && ls *.whl 2>/dev/null || true) > "$WH/MANIFEST.txt"
|
|
74
|
+
ls -la "$WH"
|
|
75
|
+
- uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: wheelhouse
|
|
78
|
+
path: ${{ runner.temp }}/wheelhouse/
|
|
79
|
+
if-no-files-found: ignore
|
|
80
|
+
|
|
81
|
+
build:
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/checkout@v4
|
|
85
|
+
- uses: actions/setup-python@v5
|
|
86
|
+
with:
|
|
87
|
+
python-version: "3.12"
|
|
88
|
+
- name: Build sdist and wheel (isolated)
|
|
89
|
+
run: |
|
|
90
|
+
python -m pip install --upgrade pip build twine
|
|
91
|
+
python -m build
|
|
92
|
+
python -m twine check --strict dist/*
|
|
93
|
+
- uses: actions/upload-artifact@v4
|
|
94
|
+
with:
|
|
95
|
+
name: dist
|
|
96
|
+
path: dist/
|
|
97
|
+
|
|
98
|
+
install:
|
|
99
|
+
needs: [build, upstream]
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
strategy:
|
|
102
|
+
fail-fast: false
|
|
103
|
+
matrix:
|
|
104
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
105
|
+
source: [wheel, sdist]
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@v4
|
|
108
|
+
- uses: actions/setup-python@v5
|
|
109
|
+
with:
|
|
110
|
+
python-version: ${{ matrix.python-version }}
|
|
111
|
+
- uses: actions/download-artifact@v4
|
|
112
|
+
with:
|
|
113
|
+
name: dist
|
|
114
|
+
path: dist/
|
|
115
|
+
- uses: actions/download-artifact@v4
|
|
116
|
+
with:
|
|
117
|
+
name: wheelhouse
|
|
118
|
+
path: ${{ runner.temp }}/wheelhouse
|
|
119
|
+
- name: Install into a fresh venv from outside the checkout
|
|
120
|
+
run: |
|
|
121
|
+
WORK="$RUNNER_TEMP/clean"
|
|
122
|
+
mkdir -p "$WORK"
|
|
123
|
+
python -m venv "$WORK/venv"
|
|
124
|
+
"$WORK/venv/bin/python" -m pip install --upgrade pip
|
|
125
|
+
if [ "${{ matrix.source }}" = wheel ]; then
|
|
126
|
+
PKG=$(ls "$GITHUB_WORKSPACE"/dist/*.whl)
|
|
127
|
+
else
|
|
128
|
+
PKG=$(ls "$GITHUB_WORKSPACE"/dist/*.tar.gz)
|
|
129
|
+
fi
|
|
130
|
+
cd "$WORK"
|
|
131
|
+
"$WORK/venv/bin/python" -m pip install --find-links "$RUNNER_TEMP/wheelhouse" "${PKG}[test]"
|
|
132
|
+
"$WORK/venv/bin/python" -m pip check
|
|
133
|
+
- name: Import and CLI smoke test
|
|
134
|
+
working-directory: ${{ runner.temp }}/clean
|
|
135
|
+
run: |
|
|
136
|
+
venv/bin/python -c "import assystant, sys; print(assystant.__version__, assystant.__file__); assert 'site-packages' in assystant.__file__"
|
|
137
|
+
venv/bin/assystant --help
|
|
138
|
+
venv/bin/assystant gen "$GITHUB_WORKSPACE/examples/Al_tutorial.yaml" --root "$RUNNER_TEMP/clean/camp" --no-progress
|
|
139
|
+
- name: Test suite against the installed package
|
|
140
|
+
working-directory: ${{ runner.temp }}/clean
|
|
141
|
+
run: |
|
|
142
|
+
cp -r "$GITHUB_WORKSPACE/tests" tests
|
|
143
|
+
venv/bin/python -m pytest tests -q -p no:cacheprovider
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Build and publish assystant to PyPI.
|
|
2
|
+
#
|
|
3
|
+
# - pull_request: build + `twine check` only (dry run, nothing is published).
|
|
4
|
+
# - release published: build, verify the release tag matches the package
|
|
5
|
+
# version, then publish via PyPI trusted publishing (OIDC, no API token).
|
|
6
|
+
#
|
|
7
|
+
# Trusted publisher on PyPI: owner ligerzero-ai, repository assystant,
|
|
8
|
+
# workflow release.yml, environment pypi.
|
|
9
|
+
|
|
10
|
+
name: release
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
release:
|
|
14
|
+
types: [published]
|
|
15
|
+
pull_request:
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
name: Build distributions
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
with:
|
|
27
|
+
persist-credentials: false
|
|
28
|
+
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
|
|
33
|
+
- name: Install build tooling
|
|
34
|
+
run: python -m pip install build twine
|
|
35
|
+
|
|
36
|
+
- name: Build sdist and wheel
|
|
37
|
+
run: python -m build
|
|
38
|
+
|
|
39
|
+
- name: Check distributions
|
|
40
|
+
run: twine check --strict dist/*
|
|
41
|
+
|
|
42
|
+
- name: Verify release tag matches package version
|
|
43
|
+
if: github.event_name == 'release'
|
|
44
|
+
env:
|
|
45
|
+
TAG: ${{ github.event.release.tag_name }}
|
|
46
|
+
DIST: assystant
|
|
47
|
+
run: |
|
|
48
|
+
set -euo pipefail
|
|
49
|
+
wheels=(dist/*.whl)
|
|
50
|
+
if [ "${#wheels[@]}" -ne 1 ]; then
|
|
51
|
+
echo "::error::expected exactly one wheel in dist/, found ${#wheels[@]}"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
wheel_version=$(basename "${wheels[0]}" | cut -d- -f2)
|
|
55
|
+
meta_version=$(python - "${wheels[0]}" <<'PY'
|
|
56
|
+
import sys, zipfile
|
|
57
|
+
from email.parser import Parser
|
|
58
|
+
with zipfile.ZipFile(sys.argv[1]) as zf:
|
|
59
|
+
name = next(n for n in zf.namelist() if n.endswith(".dist-info/METADATA"))
|
|
60
|
+
print(Parser().parsestr(zf.read(name).decode())["Version"])
|
|
61
|
+
PY
|
|
62
|
+
)
|
|
63
|
+
if [ "$wheel_version" != "$meta_version" ]; then
|
|
64
|
+
echo "::error::wheel filename version $wheel_version != METADATA version $meta_version"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
# Accept "v<ver>", "<dist>-<ver>" (underscore or hyphen spelling), or "<ver>".
|
|
68
|
+
ver="$TAG"
|
|
69
|
+
for prefix in "v" "${DIST}-" "${DIST//_/-}-"; do
|
|
70
|
+
if [ "${ver#"$prefix"}" != "$ver" ]; then
|
|
71
|
+
ver="${ver#"$prefix"}"
|
|
72
|
+
break
|
|
73
|
+
fi
|
|
74
|
+
done
|
|
75
|
+
if [ "$ver" != "$meta_version" ]; then
|
|
76
|
+
echo "::error::release tag '$TAG' (version '$ver') does not match package version '$meta_version'"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
echo "Tag '$TAG' matches package version $meta_version"
|
|
80
|
+
|
|
81
|
+
- uses: actions/upload-artifact@v4
|
|
82
|
+
with:
|
|
83
|
+
name: dist
|
|
84
|
+
path: dist/
|
|
85
|
+
if-no-files-found: error
|
|
86
|
+
|
|
87
|
+
publish:
|
|
88
|
+
name: Publish to PyPI
|
|
89
|
+
needs: build
|
|
90
|
+
if: github.event_name == 'release'
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
environment:
|
|
93
|
+
name: pypi
|
|
94
|
+
url: https://pypi.org/p/assystant
|
|
95
|
+
permissions:
|
|
96
|
+
id-token: write
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/download-artifact@v4
|
|
99
|
+
with:
|
|
100
|
+
name: dist
|
|
101
|
+
path: dist/
|
|
102
|
+
|
|
103
|
+
- name: Publish to PyPI
|
|
104
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - Unreleased
|
|
8
|
+
|
|
9
|
+
Initial release.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `assystant` command-line driver for ASSYST campaigns:
|
|
14
|
+
- `gen`: PyXtal structure generation and INCAR assembly from a campaign config.
|
|
15
|
+
- `run`: per-structure DFT submission to SLURM via `sbatch`, with an in-job
|
|
16
|
+
runner and a `--dry-run` mode.
|
|
17
|
+
- `status`: campaign progress across structures.
|
|
18
|
+
- `collect`: collation of finished calculations into a training dataset.
|
|
19
|
+
- `vasp.launcher` config option for the MPI launcher.
|
|
20
|
+
- User documentation set under `docs/` and a runnable example campaign
|
|
21
|
+
(`examples/Fe_smoke.yaml`).
|
|
22
|
+
- CI: `clean-install` workflow and a `release` workflow that publishes to PyPI
|
|
23
|
+
via trusted publishing.
|
assystant-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Han Lin Mai, Max-Planck-Institut für Nachhaltige Materialien GmbH (MPI-SusMat)
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
assystant-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: assystant
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Command-line driver for ASSYST campaigns: PyXtal generation -> per-structure DFT on SLURM -> collated training dataset
|
|
5
|
+
Author: Han Lin Mai
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: assyst,dft,machine-learning-potentials,pyxtal,slurm,vasp
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
15
|
+
Requires-Python: <3.14,>=3.11
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: pandas>=1.3.0
|
|
18
|
+
Requires-Dist: pyiron-workflow-assyst<0.3,>=0.2.0
|
|
19
|
+
Requires-Dist: pyiron-workflow<0.21,>=0.19
|
|
20
|
+
Requires-Dist: pymatgen>=2023.0.0
|
|
21
|
+
Requires-Dist: pyxtal>=1.0
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: tqdm>=4.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-cov>=2.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=6.0; extra == 'dev'
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: pytest>=6.0; extra == 'test'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# assystant
|
|
32
|
+
|
|
33
|
+
Run an ASSYST campaign end to end from a single YAML file: PyXtal structure
|
|
34
|
+
generation → one SLURM job per structure running the ISIF7 → ISIF5 → ISIF2
|
|
35
|
+
relaxation and permutation-statics chain → a single collated training dataset.
|
|
36
|
+
|
|
37
|
+
ASSYST generates DFT training data for interatomic potentials: random crystals
|
|
38
|
+
across all 230 spacegroups, relaxed through a three-stage ISIF chain, then
|
|
39
|
+
high-accuracy statics on the relaxed images *and* on rattled, triaxially strained
|
|
40
|
+
and sheared permutations of them — the off-equilibrium configurations a potential
|
|
41
|
+
needs and that ordinary MD never visits.
|
|
42
|
+
|
|
43
|
+
`assystant` drives that per campaign, replacing hand-copied
|
|
44
|
+
`2025_03_16_Unaries.py` scripts, element lists hardcoded in notebook cells, and a
|
|
45
|
+
`submit_temp.sh` maintained by hand with one line per element.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
assystant gen camp.yaml # PyXtal scan -> POSCARs + manifest
|
|
49
|
+
assystant run camp.yaml --dry-run --limit 1 # render one job; read it
|
|
50
|
+
assystant run camp.yaml # submit; also the resume command
|
|
51
|
+
assystant status camp.yaml --watch 300 # progress
|
|
52
|
+
assystant collect camp.yaml -o train.pkl # one dataframe
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Documentation
|
|
56
|
+
|
|
57
|
+
| | |
|
|
58
|
+
|---|---|
|
|
59
|
+
| [**Overview**](docs/overview.md) | what ASSYST does, what `assystant` adds, how the three repos fit together |
|
|
60
|
+
| [**Installation**](docs/installation.md) | dependencies, install order, the POTCAR config |
|
|
61
|
+
| [**Worked example**](docs/worked-example.md) | a complete campaign, start to finish, that you can copy |
|
|
62
|
+
| [**Configuration**](docs/configuration.md) | every YAML key: meaning, default, when to change it |
|
|
63
|
+
| [**Commands**](docs/commands.md) | `gen` / `run` / `status` / `collect` reference |
|
|
64
|
+
| [**Operations**](docs/operations.md) | content-hashed directories, the staleness guard, `--from-scratch`, smoke testing — the part that saves days |
|
|
65
|
+
| [**Behaviour changes**](docs/behaviour-changes.md) | read before comparing new data against pre-2026 campaigns |
|
|
66
|
+
| [**Troubleshooting**](docs/troubleshooting.md) | real failure modes and their actual error text |
|
|
67
|
+
|
|
68
|
+
Background: [design spec](docs/2026_07_31_assystant_design.md) ·
|
|
69
|
+
[implementation report](docs/implementation-report.txt)
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python -m pip install assystant
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Python 3.11–3.13. No `--no-build-isolation`, no pre-installed scientific stack:
|
|
78
|
+
`pip`, `uv pip install assystant`, a `venv`, a conda/mamba env or a pixi project
|
|
79
|
+
all work the same way. See [installation](docs/installation.md) for each variant,
|
|
80
|
+
the POTCAR configuration and what a real run additionally needs (SLURM, a VASP
|
|
81
|
+
binary).
|
|
82
|
+
|
|
83
|
+
> **Pre-release:** until `pyiron_workflow_vasp` 0.2.0 and
|
|
84
|
+
> `pyiron_workflow_assyst` 0.2.0 are on PyPI, install those two from their wheels
|
|
85
|
+
> first — see [installation](docs/installation.md#before-the-dependencies-are-on-pypi).
|
|
86
|
+
|
|
87
|
+
## Quick start
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
assystant --help
|
|
91
|
+
assystant gen examples/Al_tutorial.yaml --root ./al_campaign --no-progress
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`gen` needs neither SLURM nor VASP, so this works on any machine: it writes 43
|
|
95
|
+
POSCARs and a `manifest.csv` under `./al_campaign/Al/`. `--root` overrides the
|
|
96
|
+
cluster path in the YAML. To go further — `run`, `status`, `collect` — edit the
|
|
97
|
+
`slurm:` and `vasp:` sections to point at your partition, environment and VASP
|
|
98
|
+
binary, and set up the [POTCAR config](docs/installation.md#the-potcar-library).
|
|
99
|
+
|
|
100
|
+
[`examples/Al_tutorial.yaml`](examples/Al_tutorial.yaml) is a small, complete,
|
|
101
|
+
minutes-long campaign walked through in
|
|
102
|
+
[`docs/worked-example.md`](docs/worked-example.md).
|
|
103
|
+
[`examples/Fe_unary.yaml`](examples/Fe_unary.yaml) shows production settings.
|
|
104
|
+
[`examples/Fe_smoke.yaml`](examples/Fe_smoke.yaml) proves an install actually runs
|
|
105
|
+
DFT, on a login node, in under a minute.
|
|
106
|
+
|
|
107
|
+
## Layout on disk
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
<root>/
|
|
111
|
+
assystant_config.yaml the resolved config `run` was launched with
|
|
112
|
+
Fe/
|
|
113
|
+
manifest.csv index, hash, spacegroup, composition, n_atoms, V/atom
|
|
114
|
+
structures/00000_structure.vasp ...
|
|
115
|
+
struct_00000_3f9c1a20/ one per structure, content-hashed
|
|
116
|
+
assystant_job.sh
|
|
117
|
+
assystant.out / assystant.err
|
|
118
|
+
ISIF7.tar.gz ISIF5.tar.gz ISIF2.tar.gz ...
|
|
119
|
+
df_ASSYST_jobs.pkl present == finished
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The `_3f9c1a20` is the first eight hex digits of the SHA-256 of the POSCAR. That
|
|
123
|
+
is a correctness mechanism, not a naming convention — the caching layer
|
|
124
|
+
underneath keys on directory path and never hashes its inputs, so a
|
|
125
|
+
content-derived name is what stops one structure's results from being served for
|
|
126
|
+
another. See
|
|
127
|
+
[content-hashed run directories](docs/operations.md#content-hashed-run-directories).
|
|
128
|
+
|
|
129
|
+
## Three things to know before your first campaign
|
|
130
|
+
|
|
131
|
+
1. **Start with `--limit 1`.** A dry run proves the script renders; it cannot
|
|
132
|
+
prove the VASP binary, the modules, the POTCAR library or your INCAR work on a
|
|
133
|
+
compute node. Submit one, check it, then submit the rest.
|
|
134
|
+
2. **`IBRION` is required** whenever `assyst.ionic_steps > 0`. A missing one makes
|
|
135
|
+
VASP default to molecular dynamics and refuse the job. Use `IBRION: 2`.
|
|
136
|
+
3. **`ionic_steps` now applies to all three ISIF stages**, where pre-2026
|
|
137
|
+
campaigns applied it to ISIF7 only. New ISIF5/ISIF2 data is not directly
|
|
138
|
+
comparable with old datasets — see
|
|
139
|
+
[behaviour changes](docs/behaviour-changes.md).
|
|
140
|
+
|
|
141
|
+
## Tests
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
python -m pip install "assystant[test]"
|
|
145
|
+
pytest tests/ -q
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
108 tests. None call SLURM or VASP; `submit_campaign` takes `queued_dirs` and
|
|
149
|
+
`submit_fn` injection seams for exactly that reason. For an end-to-end check that
|
|
150
|
+
runs real DFT, use [`examples/Fe_smoke.yaml`](examples/Fe_smoke.yaml).
|
|
151
|
+
|
|
152
|
+
## Releasing
|
|
153
|
+
|
|
154
|
+
Releases go to PyPI from GitHub Actions via
|
|
155
|
+
[trusted publishing](https://docs.pypi.org/trusted-publishers/), so no API token
|
|
156
|
+
is stored anywhere.
|
|
157
|
+
|
|
158
|
+
1. Bump `__version__` in `assystant/__init__.py` (the only place the version lives) and add
|
|
159
|
+
an entry to [`CHANGELOG.md`](CHANGELOG.md).
|
|
160
|
+
2. Open a PR and merge it to `main`. The
|
|
161
|
+
[`release`](.github/workflows/release.yml) workflow builds the sdist and
|
|
162
|
+
wheel and runs `twine check --strict` on every PR as a dry run.
|
|
163
|
+
3. Create a GitHub release with tag `v<version>` (e.g. `v0.1.0`). Publishing
|
|
164
|
+
the release builds again, checks that the tag matches the package version
|
|
165
|
+
(a `v` or `assystant-` prefix is stripped), and uploads to
|
|
166
|
+
[PyPI](https://pypi.org/p/assystant).
|
|
167
|
+
|
|
168
|
+
One-time setup: on PyPI, add a trusted publisher for `assystant` with owner
|
|
169
|
+
`ligerzero-ai`, repository `assystant`, workflow `release.yml` and environment
|
|
170
|
+
`pypi`, and create a `pypi` environment in the GitHub repository settings
|
|
171
|
+
(optionally with required reviewers).
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# assystant
|
|
2
|
+
|
|
3
|
+
Run an ASSYST campaign end to end from a single YAML file: PyXtal structure
|
|
4
|
+
generation → one SLURM job per structure running the ISIF7 → ISIF5 → ISIF2
|
|
5
|
+
relaxation and permutation-statics chain → a single collated training dataset.
|
|
6
|
+
|
|
7
|
+
ASSYST generates DFT training data for interatomic potentials: random crystals
|
|
8
|
+
across all 230 spacegroups, relaxed through a three-stage ISIF chain, then
|
|
9
|
+
high-accuracy statics on the relaxed images *and* on rattled, triaxially strained
|
|
10
|
+
and sheared permutations of them — the off-equilibrium configurations a potential
|
|
11
|
+
needs and that ordinary MD never visits.
|
|
12
|
+
|
|
13
|
+
`assystant` drives that per campaign, replacing hand-copied
|
|
14
|
+
`2025_03_16_Unaries.py` scripts, element lists hardcoded in notebook cells, and a
|
|
15
|
+
`submit_temp.sh` maintained by hand with one line per element.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
assystant gen camp.yaml # PyXtal scan -> POSCARs + manifest
|
|
19
|
+
assystant run camp.yaml --dry-run --limit 1 # render one job; read it
|
|
20
|
+
assystant run camp.yaml # submit; also the resume command
|
|
21
|
+
assystant status camp.yaml --watch 300 # progress
|
|
22
|
+
assystant collect camp.yaml -o train.pkl # one dataframe
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Documentation
|
|
26
|
+
|
|
27
|
+
| | |
|
|
28
|
+
|---|---|
|
|
29
|
+
| [**Overview**](docs/overview.md) | what ASSYST does, what `assystant` adds, how the three repos fit together |
|
|
30
|
+
| [**Installation**](docs/installation.md) | dependencies, install order, the POTCAR config |
|
|
31
|
+
| [**Worked example**](docs/worked-example.md) | a complete campaign, start to finish, that you can copy |
|
|
32
|
+
| [**Configuration**](docs/configuration.md) | every YAML key: meaning, default, when to change it |
|
|
33
|
+
| [**Commands**](docs/commands.md) | `gen` / `run` / `status` / `collect` reference |
|
|
34
|
+
| [**Operations**](docs/operations.md) | content-hashed directories, the staleness guard, `--from-scratch`, smoke testing — the part that saves days |
|
|
35
|
+
| [**Behaviour changes**](docs/behaviour-changes.md) | read before comparing new data against pre-2026 campaigns |
|
|
36
|
+
| [**Troubleshooting**](docs/troubleshooting.md) | real failure modes and their actual error text |
|
|
37
|
+
|
|
38
|
+
Background: [design spec](docs/2026_07_31_assystant_design.md) ·
|
|
39
|
+
[implementation report](docs/implementation-report.txt)
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python -m pip install assystant
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Python 3.11–3.13. No `--no-build-isolation`, no pre-installed scientific stack:
|
|
48
|
+
`pip`, `uv pip install assystant`, a `venv`, a conda/mamba env or a pixi project
|
|
49
|
+
all work the same way. See [installation](docs/installation.md) for each variant,
|
|
50
|
+
the POTCAR configuration and what a real run additionally needs (SLURM, a VASP
|
|
51
|
+
binary).
|
|
52
|
+
|
|
53
|
+
> **Pre-release:** until `pyiron_workflow_vasp` 0.2.0 and
|
|
54
|
+
> `pyiron_workflow_assyst` 0.2.0 are on PyPI, install those two from their wheels
|
|
55
|
+
> first — see [installation](docs/installation.md#before-the-dependencies-are-on-pypi).
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
assystant --help
|
|
61
|
+
assystant gen examples/Al_tutorial.yaml --root ./al_campaign --no-progress
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`gen` needs neither SLURM nor VASP, so this works on any machine: it writes 43
|
|
65
|
+
POSCARs and a `manifest.csv` under `./al_campaign/Al/`. `--root` overrides the
|
|
66
|
+
cluster path in the YAML. To go further — `run`, `status`, `collect` — edit the
|
|
67
|
+
`slurm:` and `vasp:` sections to point at your partition, environment and VASP
|
|
68
|
+
binary, and set up the [POTCAR config](docs/installation.md#the-potcar-library).
|
|
69
|
+
|
|
70
|
+
[`examples/Al_tutorial.yaml`](examples/Al_tutorial.yaml) is a small, complete,
|
|
71
|
+
minutes-long campaign walked through in
|
|
72
|
+
[`docs/worked-example.md`](docs/worked-example.md).
|
|
73
|
+
[`examples/Fe_unary.yaml`](examples/Fe_unary.yaml) shows production settings.
|
|
74
|
+
[`examples/Fe_smoke.yaml`](examples/Fe_smoke.yaml) proves an install actually runs
|
|
75
|
+
DFT, on a login node, in under a minute.
|
|
76
|
+
|
|
77
|
+
## Layout on disk
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
<root>/
|
|
81
|
+
assystant_config.yaml the resolved config `run` was launched with
|
|
82
|
+
Fe/
|
|
83
|
+
manifest.csv index, hash, spacegroup, composition, n_atoms, V/atom
|
|
84
|
+
structures/00000_structure.vasp ...
|
|
85
|
+
struct_00000_3f9c1a20/ one per structure, content-hashed
|
|
86
|
+
assystant_job.sh
|
|
87
|
+
assystant.out / assystant.err
|
|
88
|
+
ISIF7.tar.gz ISIF5.tar.gz ISIF2.tar.gz ...
|
|
89
|
+
df_ASSYST_jobs.pkl present == finished
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The `_3f9c1a20` is the first eight hex digits of the SHA-256 of the POSCAR. That
|
|
93
|
+
is a correctness mechanism, not a naming convention — the caching layer
|
|
94
|
+
underneath keys on directory path and never hashes its inputs, so a
|
|
95
|
+
content-derived name is what stops one structure's results from being served for
|
|
96
|
+
another. See
|
|
97
|
+
[content-hashed run directories](docs/operations.md#content-hashed-run-directories).
|
|
98
|
+
|
|
99
|
+
## Three things to know before your first campaign
|
|
100
|
+
|
|
101
|
+
1. **Start with `--limit 1`.** A dry run proves the script renders; it cannot
|
|
102
|
+
prove the VASP binary, the modules, the POTCAR library or your INCAR work on a
|
|
103
|
+
compute node. Submit one, check it, then submit the rest.
|
|
104
|
+
2. **`IBRION` is required** whenever `assyst.ionic_steps > 0`. A missing one makes
|
|
105
|
+
VASP default to molecular dynamics and refuse the job. Use `IBRION: 2`.
|
|
106
|
+
3. **`ionic_steps` now applies to all three ISIF stages**, where pre-2026
|
|
107
|
+
campaigns applied it to ISIF7 only. New ISIF5/ISIF2 data is not directly
|
|
108
|
+
comparable with old datasets — see
|
|
109
|
+
[behaviour changes](docs/behaviour-changes.md).
|
|
110
|
+
|
|
111
|
+
## Tests
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
python -m pip install "assystant[test]"
|
|
115
|
+
pytest tests/ -q
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
108 tests. None call SLURM or VASP; `submit_campaign` takes `queued_dirs` and
|
|
119
|
+
`submit_fn` injection seams for exactly that reason. For an end-to-end check that
|
|
120
|
+
runs real DFT, use [`examples/Fe_smoke.yaml`](examples/Fe_smoke.yaml).
|
|
121
|
+
|
|
122
|
+
## Releasing
|
|
123
|
+
|
|
124
|
+
Releases go to PyPI from GitHub Actions via
|
|
125
|
+
[trusted publishing](https://docs.pypi.org/trusted-publishers/), so no API token
|
|
126
|
+
is stored anywhere.
|
|
127
|
+
|
|
128
|
+
1. Bump `__version__` in `assystant/__init__.py` (the only place the version lives) and add
|
|
129
|
+
an entry to [`CHANGELOG.md`](CHANGELOG.md).
|
|
130
|
+
2. Open a PR and merge it to `main`. The
|
|
131
|
+
[`release`](.github/workflows/release.yml) workflow builds the sdist and
|
|
132
|
+
wheel and runs `twine check --strict` on every PR as a dry run.
|
|
133
|
+
3. Create a GitHub release with tag `v<version>` (e.g. `v0.1.0`). Publishing
|
|
134
|
+
the release builds again, checks that the tag matches the package version
|
|
135
|
+
(a `v` or `assystant-` prefix is stripped), and uploads to
|
|
136
|
+
[PyPI](https://pypi.org/p/assystant).
|
|
137
|
+
|
|
138
|
+
One-time setup: on PyPI, add a trusted publisher for `assystant` with owner
|
|
139
|
+
`ligerzero-ai`, repository `assystant`, workflow `release.yml` and environment
|
|
140
|
+
`pypi`, and create a `pypi` environment in the GitHub repository settings
|
|
141
|
+
(optionally with required reviewers).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""assystant -- run an ASSYST campaign end to end from one YAML file.
|
|
2
|
+
|
|
3
|
+
``gen`` generates PyXtal structures, ``run`` submits one SLURM job per
|
|
4
|
+
structure, ``status`` reports progress, ``collect`` concatenates the results
|
|
5
|
+
into a single training dataset.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .config import (
|
|
9
|
+
CampaignConfig,
|
|
10
|
+
ConfigError,
|
|
11
|
+
StaleConfigError,
|
|
12
|
+
system_name,
|
|
13
|
+
)
|
|
14
|
+
from .generate import (
|
|
15
|
+
enumerate_compositions,
|
|
16
|
+
parse_struct_dir,
|
|
17
|
+
poscar_hash,
|
|
18
|
+
struct_dir_name,
|
|
19
|
+
)
|
|
20
|
+
from .incar import get_incar_magmom_string, stack_element_string
|
|
21
|
+
from .submit import RESULT_FILENAME
|
|
22
|
+
|
|
23
|
+
__version__ = "0.1.0"
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"CampaignConfig",
|
|
27
|
+
"ConfigError",
|
|
28
|
+
"StaleConfigError",
|
|
29
|
+
"system_name",
|
|
30
|
+
"enumerate_compositions",
|
|
31
|
+
"parse_struct_dir",
|
|
32
|
+
"poscar_hash",
|
|
33
|
+
"struct_dir_name",
|
|
34
|
+
"get_incar_magmom_string",
|
|
35
|
+
"stack_element_string",
|
|
36
|
+
"RESULT_FILENAME",
|
|
37
|
+
"__version__",
|
|
38
|
+
]
|