Skip to content

Commit 782e638

Browse files
mbercxedan-bainglass
authored andcommitted
🐛 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 722a542 commit 782e638

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
@@ -203,19 +203,100 @@ def test_parallelization_overrides(fixture_code, generate_structure):
203203

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

218-
assert pseudos['Si'] == silicon_pseudo
296+
with pytest.raises(error_type, match=error_msg):
297+
PwBaseWorkChain.get_builder_from_protocol(
298+
code=fixture_code('quantumespresso.pw'), structure=structure, overrides=overrides
299+
)
219300

220301

221302
def test_pseudos_family_structure_fail(fixture_code, generate_structure):

0 commit comments

Comments
 (0)