diff --git a/aiida_kkr/workflows/_decimation.py b/aiida_kkr/workflows/_decimation.py index 9fcf2d35..07328124 100644 --- a/aiida_kkr/workflows/_decimation.py +++ b/aiida_kkr/workflows/_decimation.py @@ -19,10 +19,11 @@ __copyright__ = (u'Copyright (c), 2020, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.3.0' +__version__ = '0.4.0' __contributors__ = u'Philipp Rüßmann' _eV2Ry = 1.0 / get_Ry2eV() +_options_single = {'tot_num_mpiprocs': 1, 'num_machines': 1, 'num_cores_per_mpiproc': 1} class kkr_decimation_wc(WorkChain): @@ -75,14 +76,15 @@ class kkr_decimation_wc(WorkChain): 'nkz': 30, # number of k-points in z-direction for substrate 'nprinc': 4, # number of layer in principle layer 'nplayer': 4, # number of principle layers (naez deci: nprinc*nplayer) + 'use_left': True, # do decimation for left or right side of the thin film (default is left/top surface) 'dosmode': False, # run DOS calculation 'dos_params': { 'emin_EF': -5.0, # EMIN-EF in eV 'emax_EF': 3.0, # EMAX-EF in eV 'nepts': 96, # number of points in contour 'tempr': 100, # smearing temperature - 'kmesh': [50, 50, 50], - }, # k-mesh used in dos calculation + 'kmesh': [50, 50, 50] # k-mesh used in dos calculation + }, } _options_default = { 'resources': { @@ -93,20 +95,18 @@ class kkr_decimation_wc(WorkChain): } _keys2d = [ + # standard names 'INTERFACE', + 'ZPERIODL', '', '', '', '', - '', '', - 'ZPERIODL', - 'ZPERIODR', # standard names - 'NLBASIS', - 'RBLEFT', - 'NRBASIS', - 'RBRIGHT' # version of keywords without brackets + '', ] + # add version of keywords without <> brackets + _keys2d += [i[1:-1] for i in _keys2d if '<' in i] # intended to guide user interactively in setting up a valid wf_params node @classmethod @@ -137,7 +137,6 @@ def define(cls, spec): 'options', valid_type=Dict, required=False, - default=lambda: Dict(dict=cls._wf_default), help= 'Computer options used in the deicmation step (voronoi and deci-out steps run serially but use the walltime given here).' ) @@ -291,6 +290,7 @@ def start(self): self.ctx.nprinc = wf_dict.get('nprinc', self._wf_default['nprinc']) self.ctx.nplayer = wf_dict.get('nplayer', self._wf_default['nplayer']) self.ctx.nkz = wf_dict.get('nkz', self._wf_default['nkz']) + self.ctx.use_left = wf_dict.get('use_left', self._wf_default['use_left']) self.ctx.dosmode = wf_dict.get('dosmode', self._wf_default.get('dosmode')) self.ctx.dos_params = wf_dict.get('dos_params', self._wf_default.get('dos_params')) @@ -400,8 +400,14 @@ def prepare_deci_from_slab(self): alat_slab = self.ctx.slab_calc.outputs.output_parameters['alat_internal'] out = make_decimation_param_nodes( - self.ctx.slab_calc.inputs.parameters, Float(alat_slab), self.ctx.struc_decimation, self.ctx.struc_substrate, - Int(self.ctx.nkz), self.ctx.params_overwrite, self.ctx.params_overwrite_decimate + self.ctx.slab_calc.inputs.parameters, + Float(alat_slab), + self.ctx.struc_decimation, + self.ctx.struc_substrate, + Int(self.ctx.nkz), + self.ctx.params_overwrite, + params_overwrite_decimate=self.ctx.params_overwrite_decimate, + use_left=self.ctx.use_left ) self.ctx.dsubstrate = out['dsubstrate'] @@ -412,7 +418,7 @@ def prepare_deci_from_slab(self): init_noco_slab = self.ctx.slab_calc.inputs.initial_noco_angles struc_decimation = self.ctx.struc_decimation struc_substrate = self.ctx.struc_substrate - noco_angles = get_noco_angles_deci(init_noco_slab, struc_decimation, struc_substrate) + noco_angles = get_noco_angles_deci(init_noco_slab, struc_decimation, struc_substrate, self.ctx.use_left) self.ctx.noco_angles_substrate = noco_angles['initial_noco_angles_substrate'] self.ctx.noco_angles_decimation = noco_angles['initial_noco_angles_decimation'] @@ -425,6 +431,13 @@ def run_voroaux(self): self.report('Skip voroaux steps due to previous decimation input') return + def _add_shapefun_overwrite(builder, shapefun_overwrite): + """add overwrite shapefun, lives only inside the scope of this function""" + builder.shapefun_overwrite = shapefun_overwrite + app_text = builder.metadata.options.get('append_text', '') + app_text += '\nmv shapefun shapefun_voro; mv shapefun_overwrite shapefun' + builder.metadata.options['append_text'] = app_text + # set up voronoi calculation for substrate builder = VoronoiCalculation.get_builder() builder.code = self.inputs.voronoi @@ -432,15 +445,10 @@ def run_voroaux(self): builder.structure = self.ctx.struc_substrate builder.metadata.label = 'auxiliary_voronoi_substrate' # pylint: disable=no-member builder.metadata.options = self.ctx.options # pylint: disable=no-member - builder.metadata.options['resources'] = {'tot_num_mpiprocs': 1, 'num_machines': 1} # pylint: disable=no-member - + builder.metadata.options['resources'] = _options_single # pylint: disable=no-member builder.potential_overwrite = self.ctx.startpot_substrate if 'shapefun_substrate_overwrite' in self.inputs: - builder.shapefun_overwrite = self.inputs.shapefun_substrate_overwrite - # make sure the shapefun is really used - append_text = builder.metadata.options.get('append_text', '') # pylint: disable=no-member - append_text += '\nmv shapefun shapefun_voro; mv shapefun_overwrite shapefun' - builder.metadata.options['append_text'] = append_text # pylint: disable=no-member + _add_shapefun_overwrite(builder, self.inputs.shapefun_substrate_overwrite) # submit voroaux for substrate calculation future_substrate = self.submit(builder) @@ -453,14 +461,10 @@ def run_voroaux(self): builder.structure = self.ctx.struc_decimation builder.metadata.label = 'auxiliary_voronoi_decimation' # pylint: disable=no-member builder.metadata.options = self.ctx.options # pylint: disable=no-member - builder.metadata.options['resources'] = {'tot_num_mpiprocs': 1, 'num_machines': 1} # pylint: disable=no-member + builder.metadata.options['resources'] = _options_single # pylint: disable=no-member builder.potential_overwrite = self.ctx.startpot_decimation if 'shapefun_deci_overwrite' in self.inputs: - builder.shapefun_overwrite = self.inputs.shapefun_deci_overwrite - # make sure the shapefun is really used - append_text = builder.metadata.options.get('append_text', '') # pylint: disable=no-member - append_text += '\nmv shapefun shapefun_voro; mv shapefun_overwrite shapefun' - builder.metadata.options['append_text'] = append_text # pylint: disable=no-member + _add_shapefun_overwrite(builder, self.inputs.shapefun_deci_overwrite) # submit voroaux for substrate calculation future_decimation = self.submit(builder) @@ -558,10 +562,10 @@ def _create_structures(self): create substrate and slab structures for deci-out and decimation steps """ struc_deci = get_deci_structure( - Int(self.ctx.nprinc), Int(self.ctx.nplayer), self.ctx.slab_calc.outputs.remote_folder + Int(self.ctx.nprinc), Int(self.ctx.nplayer), self.ctx.slab_calc.outputs.remote_folder, self.ctx.use_left ) struc_substrate = get_substrate_structure( - Int(self.ctx.nprinc), Int(self.ctx.nplayer), self.ctx.slab_calc.outputs.remote_folder + Int(self.ctx.nprinc), Int(self.ctx.nplayer), self.ctx.slab_calc.outputs.remote_folder, self.ctx.use_left ) self.ctx.struc_decimation = struc_deci @@ -575,7 +579,12 @@ def _create_startpots(self): # create decimation region startpot Nlayer_deci = len(self.ctx.struc_decimation.sites) - new_pot_indices = list(range(Nlayer_deci)) + if self.ctx.use_left: + new_pot_indices = list(range(Nlayer_deci)) + else: + struc = VoronoiCalculation.find_parent_structure(self.ctx.slab_calc)[0] + nlayer_slab = len(struc.sites) + new_pot_indices = list(range(nlayer_slab - Nlayer_deci, nlayer_slab)) settings = Dict({ 'pot1': 'out_potential', @@ -586,9 +595,13 @@ def _create_startpots(self): startpot_deci = neworder_potential_wf(settings_node=settings, parent_calc_folder=scf_slab_remote) # create substrate startpot - nrbasis = self.ctx.struc_decimation.extras['kkr_settings']['NRBASIS'] Nlayer_deci = len(self.ctx.struc_decimation.sites) - new_pot_indices = list(range(Nlayer_deci, Nlayer_deci + nrbasis)) + if self.ctx.use_left: + nrbasis = self.ctx.struc_decimation.extras['kkr_settings']['NRBASIS'] + new_pot_indices = list(range(Nlayer_deci, Nlayer_deci + nrbasis)) + else: + nlbasis = self.ctx.struc_decimation.extras['kkr_settings']['NLBASIS'] + new_pot_indices = list(range(nlayer_slab - Nlayer_deci - nlbasis, nlayer_slab - Nlayer_deci)) settings = Dict({ 'pot1': 'out_potential', @@ -617,17 +630,28 @@ def _get_adens_substrate(self): # then starting with atom index 1 (remember that Fortran starts counting at 1 and not 0) retrieved = self.ctx.slab_calc.outputs.retrieved params = self.ctx.slab_calc.inputs.parameters.get_dict() - nrbasis = params.get('', params.get('NRBASIS')) nplayer = self.ctx.nplayer nprinc = self.ctx.nprinc - rename_files = Dict( - dict( - # next nrbasis atoms after nplayer*nprinc are the substrate atoms - # format: (index in slab, index in substrate) - # Note: AiiDA needs the key in the dict to be a string instead of an integer - [(str(nplayer * nprinc + i + 1), i + 1) for i in range(nrbasis)] + if self.ctx.use_left: + nrbasis = params.get('', params.get('NRBASIS')) + rename_files = Dict( + dict( + # next nrbasis atoms after nplayer*nprinc are the substrate atoms + # format: (index in slab, index in substrate) + # Note: AiiDA needs the key in the dict to be a string instead of an integer + [(str(nplayer * nprinc + i + 1), i + 1) for i in range(nrbasis)] + ) + ) + else: + nlbasis = params.get('', params.get('NLBASIS')) + rename_files = Dict( + dict( + # first nlbasis atoms after are the substrate atoms + # format: (index in slab, index in substrate) + # Note: AiiDA needs the key in the dict to be a string instead of an integer + [(str(i + 1), i + 1) for i in range(nlbasis)] + ) ) - ) # copy and relabel the anomalous density files adens = get_anomalous_density_data(retrieved, rename_files) @@ -648,12 +672,25 @@ def _get_adens_decimate(self): retrieved = self.ctx.slab_calc.outputs.retrieved nplayer = self.ctx.nplayer nprinc = self.ctx.nprinc - rename_files = Dict( - dict( - # the first nplayer*nprinc are the decimation region - [(str(i + 1), i + 1) for i in range(nplayer * nprinc)] + + if self.ctx.use_left: + rename_files = Dict( + dict( + # the last nplayer*nprinc layers are the decimation region + [(str(i + 1), i + 1) for i in range(nplayer * nprinc)] + ) ) - ) + else: + struc = VoronoiCalculation.find_parent_structure(self.ctx.slab_calc)[0] + nlbasis = dd.get('', dd.get('NLBASIS')) + nrest = len(struc.sites) - nlbasis - (nplayer * nprinc) + rename_files = Dict( + dict( + # the last nplayer*nprinc layers are the decimation region + [(str(nrest + i + 1), i + 1) for i in range(nplayer * nprinc)] + ) + ) + # copy only the files in the renaming list, others are ignored and not copied adens = get_anomalous_density_data(retrieved, rename_files) @@ -664,15 +701,17 @@ def _get_adens_decimate(self): @calcfunction -def get_deci_structure(nprinc, nplayer, slab_calc): +def get_deci_structure(nprinc, nplayer, slab_calc, use_left=True): """ calcfunction that creates the decimation structure """ nprinc, nplayer = nprinc.value, nplayer.value struc, voro_calc = VoronoiCalculation.find_parent_structure(slab_calc) voro_params = voro_calc.inputs.parameters.get_dict() - nrbasis = voro_params.get('', voro_params.get('NRBASIS')) - zperiodr = voro_params.get('ZPERIODR') + if use_left: + nrbasis = voro_params.get('', voro_params.get('NRBASIS')) + else: + nlbasis = voro_params.get('', voro_params.get('NLBASIS')) # create decimation structure cell = struc.cell @@ -680,8 +719,12 @@ def get_deci_structure(nprinc, nplayer, slab_calc): cell[2] = list(np.array(cell[2]) * (nplayer * nprinc) / len(struc.sites)) struc_deci = StructureData(cell=cell) # add layers that are included in decimation region - for i in range(nplayer * nprinc): - struc_deci.append_atom(position=struc.sites[i].position, symbols=struc.sites[i].kind_name) + if use_left: + for i in range(nplayer * nprinc): + struc_deci.append_atom(position=struc.sites[i].position, symbols=struc.sites[i].kind_name) + else: + for i in range(nplayer * nprinc)[::-1]: + struc_deci.append_atom(position=struc.sites[-i - 1].position, symbols=struc.sites[-i - 1].kind_name) # 2D periodic boundary conditions struc_deci.pbc = (True, True, False) @@ -690,10 +733,18 @@ def get_deci_structure(nprinc, nplayer, slab_calc): for k in kkr_decimation_wc._keys2d: if k in voro_params: kkr_settings[k] = voro_params[k] - kkr_settings['NRBASIS'] = nrbasis - kkr_settings[''] = list([list(struc.sites[nplayer * nprinc + i].position) for i in range(nrbasis)]) - if len(kkr_settings['']) == 1: - kkr_settings[''] = kkr_settings[''][0] + if use_left: + kkr_settings['NRBASIS'] = nrbasis + kkr_settings[''] = list([list(struc.sites[nplayer * nprinc + i].position) for i in range(nrbasis)]) + if len(kkr_settings['']) == 1: + kkr_settings[''] = kkr_settings[''][0] + else: + kkr_settings['NLBASIS'] = nlbasis + kkr_settings[''] = list([ + list(struc.sites[-(nplayer * nprinc) - i - 1].position) for i in range(nlbasis)[::-1] + ]) + if len(kkr_settings['']) == 1: + kkr_settings[''] = kkr_settings[''][0] kkr_settings['NPRINCD'] = nprinc # save as extras to decimation structure struc_deci.extras['kkr_settings'] = kkr_settings @@ -702,24 +753,41 @@ def get_deci_structure(nprinc, nplayer, slab_calc): @calcfunction -def get_substrate_structure(nprinc, nplayer, slab_calc): +def get_substrate_structure(nprinc, nplayer, slab_calc, use_left=True): """ calcfunction that creates the decimation structure """ nprinc, nplayer = nprinc.value, nplayer.value struc, voro_calc = VoronoiCalculation.find_parent_structure(slab_calc) voro_params = voro_calc.inputs.parameters.get_dict() - nrbasis = voro_params.get('', voro_params.get('NRBASIS')) - zperiodr = voro_params.get('ZPERIODR') + if use_left: + nrbasis = voro_params.get('', voro_params.get('NRBASIS')) + zperiodr = voro_params.get('ZPERIODR') + else: + nlbasis = voro_params.get('', voro_params.get('NLBASIS')) + zperiodl = voro_params.get('ZPERIODL') # make continuation structure (i.e. the substrate) cell = struc.cell - cell[2] = zperiodr + if use_left: + cell[2] = zperiodr + else: + cell[2] = zperiodl + # zperiodl is usually in -z direction which needs to be corrected to +z + cell[2][2] *= -1 struc_substrate = StructureData(cell=cell) - for i in range(nrbasis): - struc_substrate.append_atom( - position=struc.sites[nplayer * nprinc + i].position, symbols=struc.sites[nplayer * nprinc + i].kind_name - ) + if use_left: + for i in range(nrbasis): + struc_substrate.append_atom( + position=struc.sites[nplayer * nprinc + i].position, + symbols=struc.sites[nplayer * nprinc + i].kind_name + ) + else: + for i in range(nlbasis)[::-1]: + struc_substrate.append_atom( + position=struc.sites[-(nplayer * nprinc + i + 1)].position, + symbols=struc.sites[-(nplayer * nprinc + i + 1)].kind_name + ) struc_substrate.pbc = (True, True, True) return struc_substrate @@ -765,14 +833,17 @@ def _make_d_substrate(d, params_overwrite, slab_alat, nkz): return dsubstrate -def _make_d_deci(d, struc_deci, params_overwrite, slab_alat): +def _make_d_deci(d, struc_deci, params_overwrite, slab_alat, use_left=True): """ Create the input parameters for the substrate calculation """ # set kkr params for substrate writeout ddeci = kkrparams(**d) ddeci.set_value('NSTEPS', 1) - ddeci.set_value('DECIFILES', ['vacuum', 'decifile']) + if use_left: + ddeci.set_value('DECIFILES', ['vacuum', 'decifile']) + else: + ddeci.set_value('DECIFILES', ['decifile', 'vacuum']) ddeci.set_multiple_values(**struc_deci.extras['kkr_settings']) # add decimate runopt @@ -821,7 +892,8 @@ def make_decimation_param_nodes( struc_substrate, nkz, params_overwrite=None, - params_overwrite_decimate=None + params_overwrite_decimate=None, + use_left=True ): """ Create parameter nodes for deci-out and decimation steps @@ -832,17 +904,30 @@ def make_decimation_param_nodes( # make kkr params for substrate calculation (i.e. deci-out mode, needs to run in serial!) dsubstrate = _make_d_substrate(d, params_overwrite, slab_alat, nkz) - ddeci = _make_d_deci(d, struc_deci, params_overwrite, slab_alat) + if params_overwrite_decimate is not None: - # overwrite parameters for the decimation step only - for k, v in params_overwrite_decimate.items(): - ddeci[k] = v + if params_overwrite is not None: + # overwrite additional parameters if needed for the decimation step only + p = params_overwrite.get_dict() + for k, v in params_overwrite_decimate.get_dict().items(): + p[k] = v + params_overwrite = Dict(p) + else: + params_overwrite = params_overwrite_decimate + + ddeci = _make_d_deci(d, struc_deci, params_overwrite, slab_alat, use_left) # modify array inputs to the right size Ndeci = len(struc_deci.sites) - _adapt_array_sizes(ddeci, pick_layers=[i for i in range(Ndeci)]) + if use_left: + _adapt_array_sizes(ddeci, pick_layers=[i for i in range(Ndeci)]) + else: + _adapt_array_sizes(ddeci, pick_layers=[-(i + 1) for i in range(Ndeci)[::-1]]) Nsubstrate = len(struc_substrate.sites) - _adapt_array_sizes(dsubstrate, pick_layers=[Ndeci + i for i in range(Nsubstrate)]) + if use_left: + _adapt_array_sizes(dsubstrate, pick_layers=[Ndeci + i for i in range(Nsubstrate)]) + else: + _adapt_array_sizes(dsubstrate, pick_layers=[-(Ndeci + i + 1) for i in range(Nsubstrate)[::-1]]) # now create Dict nodes with params dsubstrate = Dict(dsubstrate) @@ -852,7 +937,7 @@ def make_decimation_param_nodes( @calcfunction -def get_noco_angles_deci(init_noco_slab, struc_decimation, struc_substrate): +def get_noco_angles_deci(init_noco_slab, struc_decimation, struc_substrate, use_left=True): """ create noco angles for substrate and decimation regions from initial angles of slab calc """ @@ -862,20 +947,30 @@ def get_noco_angles_deci(init_noco_slab, struc_decimation, struc_substrate): fix_dir_slab = init_noco_slab['fix_dir'] # get number of layers in decimation and substrate regions Nlayer_deci = len(struc_decimation.sites) - nrbasis = len(struc_substrate.sites) + nlrbasis = len(struc_substrate.sites) # set correct noco angles for substrate or decimation region # deci-out - theta = theta_slab[Nlayer_deci:Nlayer_deci + nrbasis] - phi = phi_slab[Nlayer_deci:Nlayer_deci + nrbasis] - fix_dir = fix_dir_slab[Nlayer_deci:Nlayer_deci + nrbasis] + if use_left: + theta = theta_slab[Nlayer_deci:Nlayer_deci + nlrbasis] + phi = phi_slab[Nlayer_deci:Nlayer_deci + nlrbasis] + fix_dir = fix_dir_slab[Nlayer_deci:Nlayer_deci + nlrbasis] + else: + theta = theta_slab[-Nlayer_deci - nlrbasis:-Nlayer_deci] + phi = phi_slab[-Nlayer_deci - nlrbasis:-Nlayer_deci] + fix_dir = fix_dir_slab[-Nlayer_deci - nlrbasis:-Nlayer_deci] initial_noco_angles_substrate = Dict({'theta': theta, 'phi': phi, 'fix_dir': fix_dir}) # decimation - theta = theta_slab[:Nlayer_deci] - phi = phi_slab[:Nlayer_deci] - fix_dir = fix_dir_slab[:Nlayer_deci] + if use_left: + theta = theta_slab[:Nlayer_deci] + phi = phi_slab[:Nlayer_deci] + fix_dir = fix_dir_slab[:Nlayer_deci] + else: + theta = theta_slab[-Nlayer_deci:] + phi = phi_slab[-Nlayer_deci:] + fix_dir = fix_dir_slab[-Nlayer_deci:] initial_noco_angles_decimation = Dict({'theta': theta, 'phi': phi, 'fix_dir': fix_dir}) return { diff --git a/tests/data_dir/decimate.aiida b/tests/data_dir/decimate.aiida index 28703fb6..b4a216f3 100644 Binary files a/tests/data_dir/decimate.aiida and b/tests/data_dir/decimate.aiida differ diff --git a/tests/data_dir/decimate_bandstruc.aiida b/tests/data_dir/decimate_bandstruc.aiida index d530a818..892a8587 100644 Binary files a/tests/data_dir/decimate_bandstruc.aiida and b/tests/data_dir/decimate_bandstruc.aiida differ diff --git a/tests/data_dir/decimate_dos.aiida b/tests/data_dir/decimate_dos.aiida index 8b984deb..f8cbc97d 100644 Binary files a/tests/data_dir/decimate_dos.aiida and b/tests/data_dir/decimate_dos.aiida differ