Spaces:
Running
Running
Yuan (Cyrus) Chiang
commited on
Allow fine grained intermediate cache for optimization in EOS task (#35)
Browse files* add eos cache
* remove unused import
* add uv into test workflow
* avoid bash inside uv
* disable concurrent eos test due to gh action instability
- .github/workflows/test.yaml +17 -17
- mlip_arena/tasks/eos.py +15 -5
- tests/test_eos.py +24 -5
.github/workflows/test.yaml
CHANGED
@@ -6,8 +6,8 @@ on:
|
|
6 |
pull_request:
|
7 |
branches: [main]
|
8 |
|
9 |
-
|
10 |
-
|
11 |
|
12 |
jobs:
|
13 |
test:
|
@@ -23,30 +23,30 @@ jobs:
|
|
23 |
- name: Checkout repository
|
24 |
uses: actions/checkout@v4
|
25 |
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
# - name: Set up Python ${{ matrix.python-version }}
|
33 |
-
# run: uv python install ${{ matrix.python-version }}
|
34 |
|
35 |
- name: Set up Python ${{ matrix.python-version }}
|
36 |
uses: actions/setup-python@v5
|
37 |
with:
|
38 |
python-version: ${{ matrix.python-version }}
|
|
|
39 |
cache: 'pip'
|
40 |
|
41 |
- name: Install dependencies
|
42 |
run: |
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
pip install -
|
48 |
-
pip install -
|
49 |
-
pip install -e .[
|
|
|
|
|
50 |
|
51 |
- name: List dependencies
|
52 |
run: pip list
|
|
|
6 |
pull_request:
|
7 |
branches: [main]
|
8 |
|
9 |
+
env:
|
10 |
+
UV_SYSTEM_PYTHON: 1
|
11 |
|
12 |
jobs:
|
13 |
test:
|
|
|
23 |
- name: Checkout repository
|
24 |
uses: actions/checkout@v4
|
25 |
|
26 |
+
- name: Install uv
|
27 |
+
uses: astral-sh/setup-uv@v4
|
28 |
+
with:
|
29 |
+
enable-cahce: true
|
30 |
+
cache-dependency-glob: "pyproject.toml"
|
|
|
|
|
|
|
31 |
|
32 |
- name: Set up Python ${{ matrix.python-version }}
|
33 |
uses: actions/setup-python@v5
|
34 |
with:
|
35 |
python-version: ${{ matrix.python-version }}
|
36 |
+
python-version-file: "pyproject.toml"
|
37 |
cache: 'pip'
|
38 |
|
39 |
- name: Install dependencies
|
40 |
run: |
|
41 |
+
TORCH=2.2
|
42 |
+
CUDA=cu121
|
43 |
+
uv pip install torch==${TORCH}.0
|
44 |
+
uv pip install torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
45 |
+
uv pip install torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.0+${CUDA}.html
|
46 |
+
uv pip install dgl -f https://data.dgl.ai/wheels/torch-${TORCH}/${CUDA}/repo.html
|
47 |
+
uv pip install -e .[test]
|
48 |
+
uv pip install -e .[mace]
|
49 |
+
uv pip install -e .[deepmd]
|
50 |
|
51 |
- name: List dependencies
|
52 |
run: pip list
|
mlip_arena/tasks/eos.py
CHANGED
@@ -56,6 +56,7 @@ def run(
|
|
56 |
max_abs_strain: float = 0.1,
|
57 |
npoints: int = 11,
|
58 |
concurrent: bool = True,
|
|
|
59 |
) -> dict[str, Any] | State:
|
60 |
"""
|
61 |
Compute the equation of state (EOS) for the given atoms and calculator.
|
@@ -73,11 +74,17 @@ def run(
|
|
73 |
max_abs_strain: The maximum absolute strain to use.
|
74 |
npoints: The number of points to sample.
|
75 |
concurrent: Whether to relax multiple structures concurrently.
|
|
|
76 |
|
77 |
Returns:
|
78 |
A dictionary containing the EOS data, bulk modulus, equilibrium volume, and equilibrium energy if successful. Otherwise, a prefect state object.
|
79 |
"""
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
81 |
atoms=atoms,
|
82 |
calculator_name=calculator_name,
|
83 |
calculator_kwargs=calculator_kwargs,
|
@@ -92,8 +99,11 @@ def run(
|
|
92 |
|
93 |
if state.is_failed():
|
94 |
return state
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
97 |
assert isinstance(first_relax, dict)
|
98 |
relaxed = first_relax["atoms"]
|
99 |
|
@@ -108,7 +118,7 @@ def run(
|
|
108 |
atoms = relaxed.copy()
|
109 |
atoms.set_cell(c0 * f, scale_atoms=True)
|
110 |
|
111 |
-
future =
|
112 |
atoms=atoms,
|
113 |
calculator_name=calculator_name,
|
114 |
calculator_kwargs=calculator_kwargs,
|
@@ -134,7 +144,7 @@ def run(
|
|
134 |
atoms = relaxed.copy()
|
135 |
atoms.set_cell(c0 * f, scale_atoms=True)
|
136 |
|
137 |
-
state =
|
138 |
atoms=atoms,
|
139 |
calculator_name=calculator_name,
|
140 |
calculator_kwargs=calculator_kwargs,
|
|
|
56 |
max_abs_strain: float = 0.1,
|
57 |
npoints: int = 11,
|
58 |
concurrent: bool = True,
|
59 |
+
cache_opt: bool = True,
|
60 |
) -> dict[str, Any] | State:
|
61 |
"""
|
62 |
Compute the equation of state (EOS) for the given atoms and calculator.
|
|
|
74 |
max_abs_strain: The maximum absolute strain to use.
|
75 |
npoints: The number of points to sample.
|
76 |
concurrent: Whether to relax multiple structures concurrently.
|
77 |
+
cache_opt: Whether to cache the intermediate optimization results.
|
78 |
|
79 |
Returns:
|
80 |
A dictionary containing the EOS data, bulk modulus, equilibrium volume, and equilibrium energy if successful. Otherwise, a prefect state object.
|
81 |
"""
|
82 |
+
|
83 |
+
OPT_ = OPT.with_options(
|
84 |
+
refresh_cache=not cache_opt
|
85 |
+
)
|
86 |
+
|
87 |
+
state = OPT_(
|
88 |
atoms=atoms,
|
89 |
calculator_name=calculator_name,
|
90 |
calculator_kwargs=calculator_kwargs,
|
|
|
99 |
|
100 |
if state.is_failed():
|
101 |
return state
|
102 |
+
elif state.is_completed() and state.name in ["Completed", "Cached"]:
|
103 |
+
first_relax = state.result(raise_on_failure=False)
|
104 |
+
elif state.is_completed() and state.name in ["Rollback"]:
|
105 |
+
first_relax = state.result(raise_on_failure=False)
|
106 |
+
|
107 |
assert isinstance(first_relax, dict)
|
108 |
relaxed = first_relax["atoms"]
|
109 |
|
|
|
118 |
atoms = relaxed.copy()
|
119 |
atoms.set_cell(c0 * f, scale_atoms=True)
|
120 |
|
121 |
+
future = OPT_.submit(
|
122 |
atoms=atoms,
|
123 |
calculator_name=calculator_name,
|
124 |
calculator_kwargs=calculator_kwargs,
|
|
|
144 |
atoms = relaxed.copy()
|
145 |
atoms.set_cell(c0 * f, scale_atoms=True)
|
146 |
|
147 |
+
state = OPT_(
|
148 |
atoms=atoms,
|
149 |
calculator_name=calculator_name,
|
150 |
calculator_kwargs=calculator_kwargs,
|
tests/test_eos.py
CHANGED
@@ -10,10 +10,14 @@ from mlip_arena.tasks.eos import run as EOS
|
|
10 |
|
11 |
|
12 |
@flow
|
13 |
-
def single_eos_flow(calculator_name):
|
14 |
atoms = bulk("Cu", "fcc", a=3.6)
|
15 |
|
16 |
-
|
|
|
|
|
|
|
|
|
17 |
atoms=atoms,
|
18 |
calculator_name=calculator_name,
|
19 |
calculator_kwargs={},
|
@@ -27,7 +31,8 @@ def single_eos_flow(calculator_name):
|
|
27 |
),
|
28 |
max_abs_strain=0.1,
|
29 |
npoints=6,
|
30 |
-
concurrent=
|
|
|
31 |
)
|
32 |
|
33 |
|
@@ -35,8 +40,9 @@ def single_eos_flow(calculator_name):
|
|
35 |
sys.version_info[:2] != (3, 11),
|
36 |
reason="avoid prefect race condition on concurrent tasks",
|
37 |
)
|
|
|
38 |
@pytest.mark.parametrize("model", [MLIPEnum["MACE-MP(M)"]])
|
39 |
-
def test_eos(model: MLIPEnum):
|
40 |
"""
|
41 |
Test EOS prefect workflow with a simple cubic lattice.
|
42 |
"""
|
@@ -44,6 +50,19 @@ def test_eos(model: MLIPEnum):
|
|
44 |
with prefect_test_harness():
|
45 |
result = single_eos_flow(
|
46 |
calculator_name=model.name,
|
|
|
|
|
47 |
)
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
|
12 |
@flow
|
13 |
+
def single_eos_flow(calculator_name, concurrent=True, cache=False):
|
14 |
atoms = bulk("Cu", "fcc", a=3.6)
|
15 |
|
16 |
+
EOS_ = EOS.with_options(
|
17 |
+
refresh_cache=not cache,
|
18 |
+
)
|
19 |
+
|
20 |
+
return EOS_(
|
21 |
atoms=atoms,
|
22 |
calculator_name=calculator_name,
|
23 |
calculator_kwargs={},
|
|
|
31 |
),
|
32 |
max_abs_strain=0.1,
|
33 |
npoints=6,
|
34 |
+
concurrent=concurrent,
|
35 |
+
cache_opt=cache
|
36 |
)
|
37 |
|
38 |
|
|
|
40 |
sys.version_info[:2] != (3, 11),
|
41 |
reason="avoid prefect race condition on concurrent tasks",
|
42 |
)
|
43 |
+
@pytest.mark.parametrize("concurrent", [False])
|
44 |
@pytest.mark.parametrize("model", [MLIPEnum["MACE-MP(M)"]])
|
45 |
+
def test_eos(model: MLIPEnum, concurrent: bool):
|
46 |
"""
|
47 |
Test EOS prefect workflow with a simple cubic lattice.
|
48 |
"""
|
|
|
50 |
with prefect_test_harness():
|
51 |
result = single_eos_flow(
|
52 |
calculator_name=model.name,
|
53 |
+
concurrent=concurrent,
|
54 |
+
cache=False,
|
55 |
)
|
56 |
+
assert isinstance(b0_scratch := result["b0"], float)
|
57 |
+
|
58 |
+
# @pytest.mark.dependency(depends=["test_eos"])
|
59 |
+
# @pytest.mark.parametrize("model", [MLIPEnum["MACE-MP(M)"]])
|
60 |
+
# def test_eos_cache(model: MLIPEnum):
|
61 |
|
62 |
+
result = single_eos_flow(
|
63 |
+
calculator_name=model.name,
|
64 |
+
concurrent=concurrent,
|
65 |
+
cache=True,
|
66 |
+
)
|
67 |
+
assert isinstance(b0_cache := result["b0"], float)
|
68 |
+
assert b0_scratch == pytest.approx(b0_cache, rel=1e-6)
|