Skip to content

Commit ccfef5f

Browse files
🐛 Protocols: Fix behaviour for pseudos overrides
There are currently two bugs in the `PwBaseWorkChain.get_builder_from_protocol()` method when the users overrides the `pw.pseudos` input: 1. For magnetic calculations, the `starting_magnetization` is still constructed based on the number of valence electrons (`z_valence`) from the `pseudo_family`. 2. If the user overrides the `pseudos` input but doesn't override the cutoffs (`SYSTEM.ecutrho`, `SYSTEM.ecutwfc`), these are still (silently) taken from the `pseudo_family`. Here we fix these two issues by 1. Passing the `z_valences` of the provides `pseudos` override to the `get_magnetization` function. 2. Strictly checking the overrides: they should both (i) provide pseudo potentials for _all_ the kinds in the input `structure` and (ii) provide both energy cutoffs (`SYSTEM.ecutrho`, `SYSTEM.ecutwfc`) in case the `pseudos` are overridden. Moreover, if the user completely specifies the pseudos and cutoffs through the overrides, it should be fine in case `pseudo_family` is None or False. This is important in the context of adding custom pseudo potential functionality in the QEapp, see: aiidalab/aiidalab-qe#1348 Co-authored-by: Edan Bainglass <edan.bainglass@gmail.com>
1 parent 51dec17 commit ccfef5f

2 files changed

Lines changed: 131 additions & 37 deletions

File tree

src/aiida_quantumespresso/workflows/pw/base.py

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -156,31 +156,47 @@ def get_builder_from_protocol(
156156

157157
natoms = len(structure.sites)
158158

159-
try:
160-
pseudo_set = (PseudoDojoFamily, SsspFamily, CutoffsPseudoPotentialFamily)
161-
pseudo_family = orm.QueryBuilder().append(pseudo_set, filters={'label': pseudo_family}).one()[0]
162-
except exceptions.NotExistent as exception:
163-
raise ValueError(
164-
f'required pseudo family `{pseudo_family}` is not installed. Please use `aiida-pseudo install` to'
165-
'install it.'
166-
) from exception
167-
168-
try:
169-
cutoff_wfc, cutoff_rho = pseudo_family.get_recommended_cutoffs(structure=structure, unit='Ry')
170-
pseudos = pseudo_family.get_pseudos(structure=structure)
171-
except ValueError as exception:
172-
raise ValueError(
173-
f'failed to obtain recommended cutoffs for pseudo family `{pseudo_family}`: {exception}'
174-
) from exception
175-
176159
# Update the parameters based on the protocol inputs
177160
parameters = inputs['pw']['parameters']
161+
162+
if overrides and 'pseudos' in overrides['pw']:
163+
164+
pseudos = overrides['pw']['pseudos']
165+
166+
if sorted(pseudos.keys()) != sorted(structure.get_kind_names()):
167+
raise ValueError(f'`pseudos` override needs one value for each of the {len(structure.kinds)} kinds.')
168+
169+
system_overrides = overrides['pw'].get('parameters', {}).get('SYSTEM', {})
170+
171+
if not all(key in system_overrides for key in ('ecutwfc', 'ecutrho')):
172+
raise ValueError(
173+
'When overriding the pseudo potentials, both `ecutwfc` and `ecutrho` cutoffs should be '
174+
f'provided in the `overrides`: {overrides}'
175+
)
176+
177+
else:
178+
try:
179+
pseudo_set = (PseudoDojoFamily, SsspFamily, CutoffsPseudoPotentialFamily)
180+
pseudo_family = orm.QueryBuilder().append(pseudo_set, filters={'label': pseudo_family}).one()[0]
181+
except exceptions.NotExistent as exception:
182+
raise ValueError(
183+
f'required pseudo family `{pseudo_family}` is not installed. Please use `aiida-pseudo install` to'
184+
'install it.'
185+
) from exception
186+
187+
try:
188+
parameters['SYSTEM']['ecutwfc'], parameters['SYSTEM'][
189+
'ecutrho'] = pseudo_family.get_recommended_cutoffs(structure=structure, unit='Ry')
190+
pseudos = pseudo_family.get_pseudos(structure=structure)
191+
except ValueError as exception:
192+
raise ValueError(
193+
f'failed to obtain recommended cutoffs for pseudo family `{pseudo_family}`: {exception}'
194+
) from exception
195+
178196
parameters['CONTROL']['etot_conv_thr'] = natoms * meta_parameters['etot_conv_thr_per_atom']
179197
parameters['ELECTRONS']['conv_thr'] = natoms * meta_parameters['conv_thr_per_atom']
180-
parameters['SYSTEM']['ecutwfc'] = cutoff_wfc
181-
parameters['SYSTEM']['ecutrho'] = cutoff_rho
182198

183-
#If the structure is 2D periodic in the x-y plane, we set assume_isolate to `2D`
199+
# If the structure is 2D periodic in the x-y plane, we set assume_isolate to `2D`
184200
if structure.pbc == (True, True, False):
185201
parameters['SYSTEM']['assume_isolated'] = '2D'
186202

@@ -191,9 +207,9 @@ def get_builder_from_protocol(
191207

192208
magnetization = get_magnetization(
193209
structure=structure,
194-
z_valences={kind.name: pseudo_family.get_pseudo(element=kind.symbol).z_valence for kind in structure.kinds},
210+
z_valences={kind.name: pseudos[kind.name].z_valence for kind in structure.kinds},
195211
initial_magnetic_moments=initial_magnetic_moments,
196-
spin_type=spin_type
212+
spin_type=spin_type,
197213
)
198214
if spin_type is SpinType.COLLINEAR:
199215
parameters['SYSTEM']['starting_magnetization'] = magnetization['starting_magnetization']
@@ -217,9 +233,6 @@ def get_builder_from_protocol(
217233
if parameters.get('SYSTEM', {}).get('tot_magnetization') is not None:
218234
parameters.setdefault('SYSTEM', {}).pop('starting_magnetization', None)
219235

220-
pseudos_overrides = overrides.get('pw', {}).get('pseudos', {})
221-
pseudos = recursive_merge(pseudos, pseudos_overrides)
222-
223236
metadata = inputs['pw']['metadata']
224237

225238
if options:

tests/workflows/protocols/pw/test_base.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -218,19 +218,100 @@ def test_parallelization_overrides(fixture_code, generate_structure):
218218

219219
def test_pseudos_overrides(fixture_code, generate_structure, generate_upf_data):
220220
"""Test specifying ``pw.pseudos`` ``overrides`` for the ``get_builder_from_protocol()`` method."""
221-
code = fixture_code('quantumespresso.pw')
222-
structure = generate_structure('silicon')
223-
silicon_pseudo = generate_upf_data('Si')
224-
225-
overrides = {'pw': {'pseudos': {'Si': silicon_pseudo}}}
226-
builder = PwBaseWorkChain.get_builder_from_protocol(
227-
code,
228-
structure,
229-
overrides=overrides,
230-
)
231-
pseudos = builder.pw.pseudos # pylint: disable=no-member
221+
structure = generate_structure('cobalt-prim')
222+
upf_data = generate_upf_data('Co', z_valence=5.0)
223+
224+
for overrides in (
225+
{
226+
'pw': {
227+
'pseudos': {
228+
'Co': upf_data,
229+
},
230+
'parameters': {
231+
'SYSTEM': {
232+
'ecutrho': 123,
233+
'ecutwfc': 456,
234+
}
235+
}
236+
}
237+
},
238+
{
239+
'pseudo_family': None,
240+
'pw': {
241+
'pseudos': {
242+
'Co': upf_data,
243+
},
244+
'parameters': {
245+
'SYSTEM': {
246+
'ecutrho': 123,
247+
'ecutwfc': 456,
248+
}
249+
}
250+
}
251+
},
252+
):
253+
builder = PwBaseWorkChain.get_builder_from_protocol(
254+
code=fixture_code('quantumespresso.pw'),
255+
structure=structure,
256+
overrides=overrides,
257+
spin_type=SpinType.COLLINEAR
258+
)
259+
pseudos = builder.pw.pseudos # pylint: disable=no-member
260+
parameters = builder.pw.parameters.get_dict()
261+
262+
assert pseudos['Co'] == upf_data
263+
assert parameters['SYSTEM']['ecutrho'] == 123
264+
assert parameters['SYSTEM']['ecutwfc'] == 456
265+
assert parameters['SYSTEM']['starting_magnetization']['Co'] == 1.0
266+
267+
268+
def test_pseudos_overrides_raises(fixture_code, generate_structure, generate_upf_data):
269+
"""Test specifying ``pw.pseudos`` ``overrides`` for the ``get_builder_from_protocol()`` method.
270+
271+
This test checks that the `get_builder_from_protocol` raises when the user overrides the `pseudos` but does not
272+
override the cutoffs.
273+
"""
274+
structure = generate_structure('cobalt-prim')
275+
upf_data = generate_upf_data('Co')
276+
277+
for params in (
278+
({
279+
'pw': {
280+
'pseudos': {
281+
'Co': upf_data
282+
}
283+
}
284+
}, ValueError, 'both `ecutwfc` and `ecutrho` cutoffs should be'),
285+
({
286+
'pw': {
287+
'pseudos': {
288+
'Si': upf_data
289+
}
290+
}
291+
}, ValueError, '`pseudos` override needs one value'),
292+
({
293+
'pseudo_family': None,
294+
'pw': {
295+
'pseudos': {
296+
'Co': upf_data
297+
}
298+
}
299+
}, ValueError, 'both `ecutwfc` and `ecutrho` cutoffs should be'),
300+
({
301+
'pseudo_family': None,
302+
'pw': {
303+
'pseudos': {
304+
'Si': upf_data
305+
}
306+
}
307+
}, ValueError, '`pseudos` override needs one value'),
308+
):
309+
overrides, error_type, error_msg = params
232310

233-
assert pseudos['Si'] == silicon_pseudo
311+
with pytest.raises(error_type, match=error_msg):
312+
PwBaseWorkChain.get_builder_from_protocol(
313+
code=fixture_code('quantumespresso.pw'), structure=structure, overrides=overrides
314+
)
234315

235316

236317
def test_pseudos_family_structure_fail(fixture_code, generate_structure):

0 commit comments

Comments
 (0)