From ef21642ba8c715b384e726628f866f7a3df63890 Mon Sep 17 00:00:00 2001 From: AndresOrtegaGuerrero <34098967+AndresOrtegaGuerrero@users.noreply.github.com> Date: Mon, 17 Apr 2023 14:51:18 +0200 Subject: [PATCH] Protocols: consider `pbc` of `StructureData` (#907) Currently none of the `get_builder_from_protocol()` methods consider the periodic boundary conditions (`pbc`) of the input `StructureData`. Here we add support for several cases to the `PwBaseWorkChain` and `PwRelaxWorkChain`: * 2D structures in the x-y plane with periodic boundary conditions for the first two lattice vectors (`pbc == [True, True, False]`). For this case, the `PwBaseWorkChain` protocol will set `SYSTEM.assume_isolated` to `2D`, and the `PwRelaxWorkChain` protocol will use `CELL.do_free = '2Dxy'` in case the `RelaxType` is `CELL` or `POSITIONS_CELL`. * 1D structures defined on an arbitrary lattice vector will use the corresponding `CELL.do_free` setting (`'x'`, `'y'`, `'z'`) if `RelaxType` is `CELL` or `POSITIONS_CELL`. So far, no validation is done to see if the structure has the correct `cell` specified. This could potentially be added to the `PwCalculation` validation. Co-authored-by: Marnik Bercx --- .../workflows/pw/base.py | 4 ++++ .../workflows/pw/relax.py | 13 ++++++++++++- tests/conftest.py | 17 +++++++++++++++++ tests/workflows/protocols/pw/test_base.py | 19 +++++++++++++++++++ tests/workflows/protocols/pw/test_relax.py | 18 ++++++++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/aiida_quantumespresso/workflows/pw/base.py b/src/aiida_quantumespresso/workflows/pw/base.py index 1bebfb7d6..80f5f83b1 100644 --- a/src/aiida_quantumespresso/workflows/pw/base.py +++ b/src/aiida_quantumespresso/workflows/pw/base.py @@ -180,6 +180,10 @@ def get_builder_from_protocol( parameters['SYSTEM']['ecutwfc'] = cutoff_wfc parameters['SYSTEM']['ecutrho'] = cutoff_rho + #If the structure is 2D periodic in the x-y plane, we set assume_isolate to `2D` + if structure.pbc == (True, True, False): + parameters['SYSTEM']['assume_isolated'] = '2D' + if electronic_type is ElectronicType.INSULATOR: parameters['SYSTEM']['occupations'] = 'fixed' parameters['SYSTEM'].pop('degauss') diff --git a/src/aiida_quantumespresso/workflows/pw/relax.py b/src/aiida_quantumespresso/workflows/pw/relax.py index ba97daedd..d7fdd7a5a 100644 --- a/src/aiida_quantumespresso/workflows/pw/relax.py +++ b/src/aiida_quantumespresso/workflows/pw/relax.py @@ -143,7 +143,18 @@ def get_builder_from_protocol( base.pw.parameters['CELL']['cell_dofree'] = 'shape' if relax_type in (RelaxType.CELL, RelaxType.POSITIONS_CELL): - base.pw.parameters['CELL']['cell_dofree'] = 'all' + + pbc_cell_dofree_map = { + (True, True, True): 'all', + (True, False, False): 'x', + (False, True, False): 'y', + (False, False, True): 'z', + (True, True, False): '2Dxy', + } + if structure.pbc in pbc_cell_dofree_map: + base.pw.parameters['CELL']['cell_dofree'] = pbc_cell_dofree_map[structure.pbc] + else: + raise ValueError(f'Structures with periodic boundary conditions `{structure.pbc}` are not supported.') builder = cls.get_builder() builder.base = base diff --git a/tests/conftest.py b/tests/conftest.py index d93215538..e0b08b57e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -386,6 +386,23 @@ def _generate_structure(structure_id='silicon'): structure = StructureData(cell=cell) structure.append_atom(position=(0., 0., 0.), symbols='U', name='U') structure.append_atom(position=(param / 4., param / 4., param / 4.), symbols='U', name='U') + elif structure_id == '2D-xy-arsenic': + cell = [[3.61, 0, 0], [-1.80, 3.13, 0], [0, 0, 21.3]] + structure = StructureData(cell=cell, pbc=(True, True, False)) + structure.append_atom(position=(1.804, 1.042, 11.352), symbols='As', name='As') + structure.append_atom(position=(0, 2.083, 9.960), symbols='As', name='As') + elif structure_id == '1D-x-carbon': + cell = [[4.2, 0, 0], [0, 20, 0], [0, 0, 20]] + structure = StructureData(cell=cell, pbc=(True, False, False)) + structure.append_atom(position=(0, 0, 0), symbols='C', name='C') + elif structure_id == '1D-y-carbon': + cell = [[20, 0, 0], [0, 4.2, 0], [0, 0, 20]] + structure = StructureData(cell=cell, pbc=(False, True, False)) + structure.append_atom(position=(0, 0, 0), symbols='C', name='C') + elif structure_id == '1D-z-carbon': + cell = [[20, 0, 0], [0, 20, 0], [0, 0, 4.2]] + structure = StructureData(cell=cell, pbc=(False, False, True)) + structure.append_atom(position=(0, 0, 0), symbols='C', name='C') else: raise KeyError(f'Unknown structure_id="{structure_id}"') return structure diff --git a/tests/workflows/protocols/pw/test_base.py b/tests/workflows/protocols/pw/test_base.py index f70e686dc..d4b398c61 100644 --- a/tests/workflows/protocols/pw/test_base.py +++ b/tests/workflows/protocols/pw/test_base.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# pylint: disable=no-member,redefined-outer-name """Tests for the ``PwBaseWorkChain.get_builder_from_protocol`` method.""" from aiida.engine import ProcessBuilder import pytest @@ -67,6 +68,24 @@ def test_spin_type(fixture_code, generate_structure): assert parameters['SYSTEM']['starting_magnetization'] == {'Si': 0.1} +@pytest.mark.parametrize( + 'struc_name,assume_isolated', ( + ('silicon', None), + ('2D-xy-arsenic', '2D'), + ('1D-x-carbon', None), + ('1D-y-carbon', None), + ('1D-z-carbon', None), + ) +) +def test_pbc_assume_isolated(fixture_code, generate_structure, struc_name, assume_isolated): + """Test structures with various ``pbc`` set the correct ``assume_isolated``.""" + code = fixture_code('quantumespresso.pw') + structure = generate_structure(struc_name) + + builder = PwBaseWorkChain.get_builder_from_protocol(code, structure) + assert builder.pw.parameters['SYSTEM'].get('assume_isolated', None) == assume_isolated + + @pytest.mark.parametrize('initial_magnetic_moments', ({}, {'Si1': 1.0, 'Si2': 2.0})) def test_initial_magnetic_moments_invalid(fixture_code, generate_structure, initial_magnetic_moments): """Test ``PwBaseWorkChain.get_builder_from_protocol`` with invalid ``initial_magnetic_moments`` keyword.""" diff --git a/tests/workflows/protocols/pw/test_relax.py b/tests/workflows/protocols/pw/test_relax.py index e104faadd..7242c40ad 100644 --- a/tests/workflows/protocols/pw/test_relax.py +++ b/tests/workflows/protocols/pw/test_relax.py @@ -126,3 +126,21 @@ def test_options(fixture_code, generate_structure): builder.base_final_scf.pw.metadata, ): assert subspace['options']['queue_name'] == queue_name + + +@pytest.mark.parametrize( + 'struc_name,cell_dofree', ( + ('silicon', 'all'), + ('2D-xy-arsenic', '2Dxy'), + ('1D-x-carbon', 'x'), + ('1D-y-carbon', 'y'), + ('1D-z-carbon', 'z'), + ) +) +def test_pbc_cell(fixture_code, generate_structure, struc_name, cell_dofree): + """Test structures with various ``pbc`` set the correct ``CELL`` parameters.""" + code = fixture_code('quantumespresso.pw') + structure = generate_structure(struc_name) + + builder = PwRelaxWorkChain.get_builder_from_protocol(code, structure) + assert builder.base.pw.parameters['CELL'].get('cell_dofree', None) == cell_dofree