Skip to content

Commit 68b3dee

Browse files
Merge branch 'master' into remove-unused-q-start-current-q-arrays
2 parents 0bc1875 + aa725e6 commit 68b3dee

12 files changed

Lines changed: 185 additions & 90 deletions

File tree

.github/workflows/build.yml

Lines changed: 0 additions & 37 deletions
This file was deleted.

.github/workflows/python-testsuite.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020

2121
services:
2222
rabbitmq:
23-
image: rabbitmq:latest
23+
image: rabbitmq:3.12-management
2424
ports:
2525
- 5672:5672
2626

Modules/Cluster.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,30 @@ def __setattr__(self, name, value):
333333
super(Cluster, self).__setattr__(name, value)
334334

335335

336+
def __getstate__(self):
337+
"""
338+
Return the picklable state of the cluster.
339+
340+
The thread lock created by compute_ensemble_batch cannot be pickled,
341+
so it is dropped here. This allows sscha.Utilities.save_binary to
342+
store objects holding a cluster after a calculation has run.
343+
"""
344+
state = self.__dict__.copy()
345+
state["lock"] = None
346+
return state
347+
348+
349+
def __setstate__(self, state):
350+
"""
351+
Restore the cluster from a pickled state.
352+
353+
The thread lock is transient runtime state and is reset to None,
354+
as after __init__; compute_ensemble_batch recreates it when needed.
355+
"""
356+
state["lock"] = None
357+
self.__dict__.update(state)
358+
359+
336360

337361
def copy_file(self, source, destination, server_source = False, server_dest = True, raise_error=False, **kwargs):
338362
"""

Modules/SchaMinimizer.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ def minimization_step(self, custom_function_gradient = None, timer=None):
634634

635635
# Update the structure
636636
if self.minim_struct:
637-
self.dyn.structure.coords[:,:] = new_struct
637+
self.dyn.structure.coords[:,:] = np.real(new_struct)
638638

639639
# Check if we must enforce the symmetries and the sum rule:
640640
if self.enforce_sum_rule and (not self.neglect_symmetries):
@@ -643,6 +643,11 @@ def minimization_step(self, custom_function_gradient = None, timer=None):
643643
else:
644644
self.dyn.Symmetrize(use_spglib = self.use_spglib)
645645

646+
# Enforce the dynamical matrix to be real at q = -q + G (time-reversal symmetry)
647+
bg = self.dyn.structure.get_reciprocal_vectors() / (2 * np.pi)
648+
for iq, q in enumerate(self.dyn.q_tot):
649+
if CC.Methods.get_min_dist_into_cell(bg, q, -q) < 1e-6:
650+
self.dyn.dynmats[iq] = np.real(self.dyn.dynmats[iq])
646651

647652
# If we have imaginary frequencies, force the kl ratio to zero
648653

Tutorials/H3S/Automatic_Calculations.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,21 +1197,21 @@
11971197
],
11981198
"metadata": {
11991199
"kernelspec": {
1200-
"display_name": "Python 3",
1200+
"display_name": "Python 3 (ipykernel)",
12011201
"language": "python",
12021202
"name": "python3"
12031203
},
12041204
"language_info": {
12051205
"codemirror_mode": {
12061206
"name": "ipython",
1207-
"version": 2
1207+
"version": 3
12081208
},
12091209
"file_extension": ".py",
12101210
"mimetype": "text/x-python",
12111211
"name": "python",
12121212
"nbconvert_exporter": "python",
12131213
"pygments_lexer": "ipython3",
1214-
"version": "3.7.6"
1214+
"version": "3.13.7"
12151215
}
12161216
},
12171217
"nbformat": 4,

meson.build

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
project('python-sscha',
22
['c','fortran'],
3-
version : '1.6.0',
3+
version : '1.6.1',
44
license: 'GPL',
55
meson_version: '>= 1.1.0', # <- set min version of meson.
66
default_options : [
@@ -34,8 +34,18 @@ project('python-sscha',
3434
blas_dep = mkl_dep
3535
lapack_dep = mkl_dep
3636
else
37-
lapack_dep = dependency('lapack', required: true)
38-
blas_dep = dependency('blas', required: true)
37+
lapack_dep = dependency('lapack', required: false)
38+
blas_dep = dependency('blas', required: false)
39+
40+
if not lapack_dep.found()
41+
lapack_lib = fc.find_library('lapack', required: true)
42+
lapack_dep = declare_dependency(dependencies: lapack_lib)
43+
endif
44+
45+
if not blas_dep.found()
46+
blas_lib = fc.find_library('blas', required: true)
47+
blas_dep = declare_dependency(dependencies: blas_lib)
48+
endif
3949
endif
4050

4151
# Look for the OpenMP library if it is needed for parallelization.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "mesonpy"
44

55
[project]
66
name = "python-sscha"
7-
version = "1.6.0"
7+
version = "1.6.1"
88
description = "Python implementation of the sscha code"
99
authors = [{name = "Lorenzo Monacelli"}] # Put here email
1010
readme = "README.md"

pytest.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[pytest]
2+
markers =
3+
julia: tests requiring the Julia Fourier backend
4+
release: long-running tests excluded from normal CI

tests/aiida_ensemble/test_aiida_ensemble.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22
import pytest
33
import numpy as np
44

5+
try:
6+
import aiida
7+
HAVE_AIIDA = True
8+
except ImportError:
9+
HAVE_AIIDA = False
10+
511
from sscha.aiida_ensemble import AiiDAEnsemble
612

13+
aiida_required = pytest.mark.skipif(not HAVE_AIIDA, reason='aiida not installed')
14+
715

816
def get_ensemble() -> AiiDAEnsemble:
917
"""Return an AiiDAEnsemble instance."""
@@ -34,6 +42,7 @@ def test_clean_runs():
3442
assert np.all(np.isclose(ensemble.forces, np.ones((num_configs-1, num_atoms, 3))))
3543

3644

45+
@aiida_required
3746
@pytest.mark.usefixtures('aiida_profile')
3847
def test_get_running_workchains(generate_workchain_pw_node):
3948
"""Test the :func:`sscha.aiida_ensemble.get_running_workchains` method."""
@@ -67,6 +76,7 @@ def test_get_running_workchains(generate_workchain_pw_node):
6776
assert success == [False, True, False]
6877

6978

79+
@aiida_required
7080
@pytest.mark.usefixtures('aiida_profile')
7181
def test_submit_and_get_workchains(fixture_code):
7282
"""Test the :func:`sscha.aiida_ensemble.submit_and_get_workchains` method."""

tests/conftest.py

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,17 @@
1010

1111
import pytest
1212

13-
pytest_plugins = ['aiida.manage.tests.pytest_fixtures'] # pylint: disable=invalid-name
13+
# Conditionally include aiida fixtures only if aiida is available
14+
try:
15+
import aiida
16+
HAVE_AIIDA = True
17+
except ImportError:
18+
HAVE_AIIDA = False
19+
20+
if HAVE_AIIDA:
21+
pytest_plugins = ['aiida.manage.tests.pytest_fixtures'] # pylint: disable=invalid-name
22+
else:
23+
pytest_plugins = []
1424

1525

1626
@pytest.fixture(scope='session')
@@ -25,25 +35,27 @@ def filepath_tests():
2535

2636

2737
@pytest.fixture
28-
def filepath_fixtures(filepath_tests):
29-
"""Return the absolute filepath to the directory containing the file `fixtures`."""
30-
return os.path.join(filepath_tests, 'fixtures')
31-
32-
33-
@pytest.fixture(scope='function')
3438
def fixture_sandbox():
3539
"""Return a `SandboxFolder`."""
40+
if not HAVE_AIIDA:
41+
pytest.skip("aiida not installed")
3642
from aiida.common.folders import SandboxFolder
3743
with SandboxFolder() as folder:
3844
yield folder
3945

4046

41-
@pytest.fixture
42-
def fixture_localhost(aiida_localhost):
43-
"""Return a localhost `Computer`."""
44-
localhost = aiida_localhost
45-
localhost.set_default_mpiprocs_per_machine(1)
46-
return localhost
47+
if HAVE_AIIDA:
48+
@pytest.fixture
49+
def fixture_localhost(aiida_localhost):
50+
"""Return a localhost `Computer`."""
51+
localhost = aiida_localhost
52+
localhost.set_default_mpiprocs_per_machine(1)
53+
return localhost
54+
else:
55+
@pytest.fixture
56+
def fixture_localhost():
57+
"""Dummy fixture when aiida is not installed."""
58+
pytest.skip("aiida not installed")
4759

4860

4961
@pytest.fixture
@@ -131,49 +143,51 @@ def _serialize_builder(builder):
131143
return _serialize_builder
132144

133145

134-
@pytest.fixture(scope='session', autouse=True)
135-
def sssp(aiida_profile, generate_upf_data):
136-
"""Create an SSSP pseudo potential family from scratch."""
137-
from aiida.common.constants import elements
138-
from aiida.plugins import GroupFactory
146+
if HAVE_AIIDA:
139147

140-
aiida_profile.clear_profile()
148+
@pytest.fixture(scope='session', autouse=True)
149+
def sssp(aiida_profile, generate_upf_data):
150+
"""Create an SSSP pseudo potential family from scratch."""
151+
from aiida.common.constants import elements
152+
from aiida.plugins import GroupFactory
141153

142-
SsspFamily = GroupFactory('pseudo.family.sssp')
154+
aiida_profile.clear_profile()
143155

144-
cutoffs = {}
145-
stringency = 'standard'
156+
SsspFamily = GroupFactory('pseudo.family.sssp')
146157

147-
with tempfile.TemporaryDirectory() as dirpath:
148-
for values in elements.values():
158+
cutoffs = {}
159+
stringency = 'standard'
149160

150-
element = values['symbol']
161+
with tempfile.TemporaryDirectory() as dirpath:
162+
for values in elements.values():
151163

152-
actinides = ('Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr')
164+
element = values['symbol']
153165

154-
if element in actinides:
155-
continue
166+
actinides = ('Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr')
156167

157-
upf = generate_upf_data(element)
158-
dirpath = pathlib.Path(dirpath)
159-
filename = dirpath / f'{element}.upf'
168+
if element in actinides:
169+
continue
160170

161-
with open(filename, 'w+b') as handle:
162-
with upf.open(mode='rb') as source:
163-
handle.write(source.read())
164-
handle.flush()
171+
upf = generate_upf_data(element)
172+
dirpath = pathlib.Path(dirpath)
173+
filename = dirpath / f'{element}.upf'
165174

166-
cutoffs[element] = {
167-
'cutoff_wfc': 30.0,
168-
'cutoff_rho': 240.0,
169-
}
175+
with open(filename, 'w+b') as handle:
176+
with upf.open(mode='rb') as source:
177+
handle.write(source.read())
178+
handle.flush()
179+
180+
cutoffs[element] = {
181+
'cutoff_wfc': 30.0,
182+
'cutoff_rho': 240.0,
183+
}
170184

171-
label = 'SSSP/1.3/PBEsol/efficiency'
172-
family = SsspFamily.create_from_folder(dirpath, label)
185+
label = 'SSSP/1.3/PBEsol/efficiency'
186+
family = SsspFamily.create_from_folder(dirpath, label)
173187

174-
family.set_cutoffs(cutoffs, stringency, unit='Ry')
188+
family.set_cutoffs(cutoffs, stringency, unit='Ry')
175189

176-
return family
190+
return family
177191

178192

179193
@pytest.fixture

0 commit comments

Comments
 (0)