Skip to content

Commit 5965e32

Browse files
authored
Cache macos/ubuntu optional dependency install in CI pipeline, enable mcsqs/pyzeo tests (#4455)
* separate optional dep install script * disable wget cert check for now * try dependency cache * optional dep needed before pyzeo install * Revert "optional dep needed before pyzeo install" This reverts commit 10543b9. * install zeopp in optional dep * skip pyzeo on windows * tyep annotation * install mcsqs * fix mcsqs runtime error * fix mcsqs caller test * declare global opt bin dir * skip failing test for now * reuse zeo_found * more readable cache key * pytext skip -> xfail * fix test class naming * remove unnecessary test file dir check
1 parent 8ba4b6e commit 5965e32

24 files changed

+90
-72
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
set -x
4+
5+
# Use a custom bin directory for easier GitHub Actions caching
6+
BIN_DIR="${1:-$PWD/opt/bin}" && mkdir -p "$BIN_DIR"
7+
8+
# Install BoltzTraP
9+
wget --no-verbose -O BoltzTraP.tar.bz2 https://owncloud.tuwien.ac.at/index.php/s/s2d55LYlZnioa3s/download
10+
tar -jxf BoltzTraP.tar.bz2
11+
cp boltztrap-1.2.5/src/{BoltzTraP,x_trans} "$BIN_DIR"
12+
13+
# Install Vampire 5.0
14+
# TODO: Accepts self-signed cert (https://github.com/richard-evans/vampire/issues/122)
15+
wget --no-verbose --no-check-certificate https://vampire.york.ac.uk/resources/release-5/vampire-5.0-linux.tar.gz
16+
tar -zxf vampire-5.0-linux.tar.gz && mv linux vampire-5.0
17+
cp vampire-5.0/vampire-serial "$BIN_DIR"
18+
19+
# Install Voro++ and ZEO++
20+
wget --no-verbose http://www.zeoplusplus.org/zeo++-0.3.tar.gz
21+
tar -xzf zeo++-0.3.tar.gz
22+
make -C zeo++-0.3/voro++/src -s CFLAGS="-w"
23+
make -C zeo++-0.3 -s CFLAGS="-w"
24+
cp zeo++-0.3/voro++/src/voro++ "$BIN_DIR"
25+
cp zeo++-0.3/network "$BIN_DIR"
26+
27+
# Install mcsqs (from ATAT)
28+
# TODO: cannot build on MacOS
29+
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
30+
wget --no-verbose https://axelvandewalle.github.io/www-avdw/atat/atat3_50.tar.gz
31+
tar -zxf atat3_50.tar.gz
32+
33+
# Replace `BINDIR` in makefile
34+
sed -i "s|^BINDIR=.*|BINDIR=$BIN_DIR|" atat/makefile
35+
36+
sudo apt install csh -y -q
37+
make -C atat && make -C atat install
38+
fi
39+
40+
# Add to path
41+
echo "$BIN_DIR" >> "$GITHUB_PATH"

.github/workflows/test.yml

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
MPLBACKEND: Agg # non-interactive backend for matplotlib
5757
PMG_MAPI_KEY: ${{ secrets.PMG_MAPI_KEY }}
5858
PYTHONWARNDEFAULTENCODING: "true" # PEP 597: Enable optional EncodingWarning
59+
OPT_BIN_DIR: ${{ github.workspace }}/opt/bin # for optional Unix dependencies
5960
steps:
6061
- name: Check out repo
6162
uses: actions/checkout@v4
@@ -94,23 +95,17 @@ jobs:
9495
pip install --upgrade matgl>=1.2.6
9596
pip install torch==2.2.0 torchdata==0.7.1
9697
97-
- name: Install optional Ubuntu and macOS dependencies
98+
- name: Restore cache for optional Ubuntu/MacOS dependencies
9899
if: matrix.config.os == 'ubuntu-latest' || matrix.config.os == 'macos-latest'
99-
run: |
100-
# Install BoltzTraP
101-
wget -q -O BoltzTraP.tar.bz2 https://owncloud.tuwien.ac.at/index.php/s/s2d55LYlZnioa3s/download
102-
tar -jxf BoltzTraP.tar.bz2 && realpath boltztrap-1.2.5/src/ >> $GITHUB_PATH
103-
104-
# Install Vampire 5.0
105-
wget -q https://vampire.york.ac.uk/resources/release-5/vampire-5.0-linux.tar.gz
106-
tar -zxf vampire-5.0-linux.tar.gz && mv linux vampire-5.0
107-
realpath vampire-5.0/ >> $GITHUB_PATH
108-
109-
# Install Voro++ and ZEO++
110-
wget -q http://www.zeoplusplus.org/zeo++-0.3.tar.gz && tar -xzf zeo++-0.3.tar.gz
100+
id: optbin-cache
101+
uses: actions/cache@v4
102+
with:
103+
path: ${{ env.OPT_BIN_DIR }}
104+
key: opt-bin-${{ runner.os }}-${{ hashFiles('.github/workflows/_install_opt_unit_deps.sh') }}
111105

112-
cd zeo++-0.3/voro++/src && make -s CFLAGS="-w" && realpath . >> $GITHUB_PATH && cd ../..
113-
make -s CFLAGS="-w" && realpath . >> $GITHUB_PATH
106+
- name: Build optional Ubuntu/MacOS dependencies (when cache misses)
107+
if: (matrix.config.os == 'ubuntu-latest' || matrix.config.os == 'macos-latest') && steps.optbin-cache.outputs.cache-hit != 'true'
108+
run: bash .github/workflows/_install_opt_unit_deps.sh "$OPT_BIN_DIR"
114109

115110
- name: pytest split ${{ matrix.split }}
116111
env:

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ mlp = ["matgl>=1.2.7 ; python_version<'3.13'"]
105105
numba = ["numba>=0.55"]
106106
numpy-v1 = ["numpy>=1.25.0,<2"] # Test NP1 on Windows (quite buggy ATM)
107107
optional = [
108-
"pymatgen[abinit,ase,mlp,tblite,matcalc]",
108+
"pymatgen[abinit,ase,matcalc,mlp,tblite,zeopp]",
109109
"beautifulsoup4",
110110
# PR4347: BoltzTraP2 needs to bump cmake version for spglib build
111111
# "BoltzTraP2>=24.9.4",
@@ -125,7 +125,7 @@ symmetry = ["moyopy[interface]>=0.3", "spglib>=2.5"]
125125
# https://github.com/tblite/tblite/issues/175
126126
tblite = ["tblite[ase]>=0.3.0; platform_system=='Linux' and python_version<'3.12'"]
127127
vis = ["vtk>=6.0.0"]
128-
zeopp = ["pyzeo"] # Note: requires Voro++ and Zeo++ to be installed
128+
zeopp = ["pyzeo; platform_system != 'Windows'"] # Note: requires Voro++ and Zeo++ to be installed
129129

130130
[project.scripts]
131131
pmg = "pymatgen.cli.pmg:main"

src/pymatgen/command_line/mcsqs_caller.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def run_mcsqs(
146146

147147
raise RuntimeError("mcsqs exited before timeout reached")
148148

149-
except TimeoutExpired:
149+
except TimeoutExpired as exc:
150150
for process in mcsqs_find_sqs_processes:
151151
process.kill()
152152
process.communicate()
@@ -157,7 +157,7 @@ def run_mcsqs(
157157
raise RuntimeError(
158158
"mcsqs did not generate output files, "
159159
"is search_time sufficient or are number of instances too high?"
160-
)
160+
) from exc
161161

162162
process = Popen(["mcsqs", "-best"])
163163
process.communicate()
@@ -166,7 +166,7 @@ def run_mcsqs(
166166
return _parse_sqs_path(".")
167167

168168
os.chdir(original_directory)
169-
raise TimeoutError("Cluster expansion took too long.")
169+
raise TimeoutError("Cluster expansion took too long.") from exc
170170

171171

172172
def _parse_sqs_path(path) -> Sqs:

src/pymatgen/io/zeopp.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
from zeo.cluster import prune_voronoi_network_close_node
4242
from zeo.netstorage import AtomNetwork
4343

44-
zeo_found = True
45-
zeo_source: str | None = "zeo"
44+
zeo_found: bool = True
45+
zeo_source: Literal["zeo", "pyzeo"] | None = "zeo"
4646
except ImportError:
4747
try:
4848
from pyzeo import AtomNetwork
@@ -58,6 +58,7 @@
5858

5959
if TYPE_CHECKING:
6060
from pathlib import Path
61+
from typing import Literal
6162

6263
from typing_extensions import Self
6364

tests/analysis/chemenv/coordination_environments/test_coordination_geometry_finder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ def _strategy_test(self, strategy):
166166
# Check that the environment found is the expected one
167167
assert coord_env == expected_geoms[ienv]
168168

169-
@pytest.mark.skip("TODO: need someone to fix this")
169+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
170170
def test_simplest_chemenv_strategy(self):
171171
strategy = SimplestChemenvStrategy()
172172
self._strategy_test(strategy)
173173

174-
@pytest.mark.skip("TODO: need someone to fix this")
174+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
175175
def test_simple_abundance_chemenv_strategy(self):
176176
strategy = SimpleAbundanceChemenvStrategy()
177177
self._strategy_test(strategy)

tests/analysis/test_graphs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def test_auto_image_detection(self):
239239

240240
assert len(list(struct_graph.graph.edges(data=True))) == 3
241241

242-
@pytest.mark.skip(reason="Need someone to fix this, see issue 4206")
242+
@pytest.mark.xfail(reason="Need someone to fix this, see issue 4206")
243243
def test_str(self):
244244
square_sg_str_ref = """Structure Graph
245245
Structure:

tests/analysis/test_molecule_structure_comparator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def test_to_and_from_dict(self):
139139
d2 = MoleculeStructureComparator.from_dict(d1).as_dict()
140140
assert d1 == d2
141141

142-
@pytest.mark.skip("TODO: need someone to fix this")
142+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
143143
def test_structural_change_in_geom_opt(self):
144144
qcout_path = f"{TEST_DIR}/mol_1_3_bond.qcout"
145145
qcout = QCOutput(qcout_path)

tests/analysis/test_piezo_sensitivity.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def test_get_stable_fcm(self):
208208
assert_allclose(asum1, np.zeros([3, 3]), atol=1e-5)
209209
assert_allclose(asum2, np.zeros([3, 3]), atol=1e-5)
210210

211-
@pytest.mark.skipif(
211+
@pytest.mark.xfail(
212212
platform.system() == "Windows" and int(np.__version__[0]) >= 2,
213213
reason="See https://github.com/conda-forge/phonopy-feedstock/pull/158#issuecomment-2227506701",
214214
)
@@ -262,7 +262,7 @@ def test_get_piezo(self):
262262
piezo = get_piezo(self.BEC, self.IST, self.FCM)
263263
assert_allclose(piezo, self.piezo, atol=1e-5)
264264

265-
@pytest.mark.skipif(
265+
@pytest.mark.xfail(
266266
platform.system() == "Windows" and int(np.__version__[0]) >= 2,
267267
reason="See https://github.com/conda-forge/phonopy-feedstock/pull/158#issuecomment-2227506701",
268268
)

tests/analysis/test_surface_analysis.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,37 +253,37 @@ def test_entry_dict_from_list(self):
253253
surf_ene_plotter = SurfaceEnergyPlotter(all_Pt_slab_entries, self.Pt_analyzer.ucell_entry)
254254
assert surf_ene_plotter.list_of_chempots == self.Pt_analyzer.list_of_chempots
255255

256-
@pytest.mark.skip("TODO: need someone to fix this")
256+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
257257
def test_monolayer_vs_BE(self):
258258
for el in self.Oads_analyzer_dict:
259259
# Test WulffShape for adsorbed surfaces
260260
analyzer = self.Oads_analyzer_dict[el]
261261
analyzer.monolayer_vs_BE()
262262

263-
@pytest.mark.skip("TODO: need someone to fix this")
263+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
264264
def test_area_frac_vs_chempot_plot(self):
265265
for el in self.Oads_analyzer_dict:
266266
# Test WulffShape for adsorbed surfaces
267267
analyzer = self.Oads_analyzer_dict[el]
268268
analyzer.area_frac_vs_chempot_plot(x_is_u_ads=True)
269269

270-
@pytest.mark.skip("TODO: need someone to fix this")
270+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
271271
def test_chempot_vs_gamma_clean(self):
272272
self.Cu_analyzer.chempot_vs_gamma_clean()
273273
for el in self.Oads_analyzer_dict:
274274
# Test WulffShape for adsorbed surfaces
275275
analyzer = self.Oads_analyzer_dict[el]
276276
analyzer.chempot_vs_gamma_clean(x_is_u_ads=True)
277277

278-
@pytest.mark.skip("TODO: need someone to fix this")
278+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
279279
def test_chempot_vs_gamma_facet(self):
280280
for el, val in self.metals_O_entry_dict.items():
281281
for hkl in val:
282282
# Test WulffShape for adsorbed surfaces
283283
analyzer = self.Oads_analyzer_dict[el]
284284
analyzer.chempot_vs_gamma_facet(hkl)
285285

286-
@pytest.mark.skip("TODO: need someone to fix this")
286+
@pytest.mark.xfail(reason="TODO: need someone to fix this")
287287
def test_surface_chempot_range_map(self):
288288
for el, val in self.metals_O_entry_dict.items():
289289
for hkl in val:

0 commit comments

Comments
 (0)