diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 350160f2..bf5a47bc 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.2.1 +current_version = 2.3.0 commit = True tag = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? diff --git a/aiida_kkr/__init__.py b/aiida_kkr/__init__.py index fe5a0b49..c009b28d 100644 --- a/aiida_kkr/__init__.py +++ b/aiida_kkr/__init__.py @@ -2,4 +2,4 @@ AiiDA KKR """ -__version__ = '2.2.1' +__version__ = '2.3.0' diff --git a/aiida_kkr/calculations/kkr.py b/aiida_kkr/calculations/kkr.py index 4de3e44d..a8bf36df 100644 --- a/aiida_kkr/calculations/kkr.py +++ b/aiida_kkr/calculations/kkr.py @@ -25,7 +25,7 @@ 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.13.1' +__version__ = '0.13.2' __contributors__ = ('Jens Bröder', 'Philipp Rüßmann') @@ -1063,8 +1063,8 @@ def _use_initial_noco_angles(self, parameters, structure, tempfolder): f'Error: theta value out of range (0..180): iatom={iatom}, theta={theta}' ) # convert fix_dir to boolean if given as integer - if not isinstance(fix_dir, bool): - fix_dir = (fix_dir == 1) + if not isinstance(fix_dir[iatom], bool): + fix_dir[iatom] = (fix_dir[iatom] == 1) # write line noco_angle_file.write(f' {theta} {phi} {fix_dir[iatom]}\n') diff --git a/aiida_kkr/calculations/kkrimp.py b/aiida_kkr/calculations/kkrimp.py index 83e0a414..bdbdccf7 100644 --- a/aiida_kkr/calculations/kkrimp.py +++ b/aiida_kkr/calculations/kkrimp.py @@ -77,7 +77,9 @@ class KkrimpCalculation(CalcJob): _OUT_LMDOS_INTERPOL = u'out_lmdos.interpol.atom=*' _OUT_MAGNETICMOMENTS = u'out_magneticmoments' _OUT_ORBITALMOMENTS = u'out_orbitalmoments' - _LDAUPOT = 'ldaupot' + _LDAUPOT = u'ldaupot' + + _TEST_RHO2NS = u'test_rho2ns' # template.product entry point defined in setup.json _default_parser = u'kkr.kkrimpparser' @@ -787,6 +789,10 @@ def _initialize_kkrimp_params(self, params_host, parameters, GFhost_folder, temp # take care of LLYsimple (i.e. Lloyd in host system) if 'LLOYD' in runopts: runflag = self._use_lloyd(runflag, GFhost_folder, tempfolder) + # alternative to Lloyd mode: renormalization factor of energy integration weights + if params_host.get_value('WFAC_RENORM') is not None: + if params_host.get_value('WFAC_RENORM'): + runflag = self._use_lloyd(runflag, GFhost_folder, tempfolder, True) # now set runflags params_kkrimp.set_value('RUNFLAG', runflag) @@ -824,19 +830,33 @@ def _set_nosoc(self, params_host, GFhost_folder, tempfolder): for socscl in socscale: kkrflex_socfac.write(f' {socscl}\n') - def _use_lloyd(self, runflag, GFhost_folder, tempfolder): + def _use_lloyd(self, runflag, GFhost_folder, tempfolder, wfac_renorm=False): """Use the LLYsimple version of KKRimp code with the average renormalization factor from the host calculation""" + # add runflag for imp code runflag.append('LLYsimple') - # also extract renormalization factor and create kkrflex_llyfac file (contains one value only) - with GFhost_folder.open('output.000.txt') as f: - txt = f.readlines() + + # find renormalization factor + if wfac_renorm: + # option 1: found wfac_renorm in Kkrhost parent's input parameters + with GFhost_folder.open('inputcard') as f: + txt = f.readlines() + iline = search_string('WFAC_RENORM', txt) + else: + # option 2: host parent is a Lloyd calculation + # extract renormalization factor and create kkrflex_llyfac file (contains one value only) + with GFhost_folder.open('output.000.txt') as f: + txt = f.readlines() iline = search_string('RENORM_LLY: Renormalization factor of total charge', txt) - if iline >= 0: - llyfac = txt[iline].split()[-1] - # now write kkrflex_llyfac to tempfolder where later on config file is also written - with tempfolder.open(self._KKRFLEX_LLYFAC, 'w') as f2: - f2.writelines([llyfac]) + + if iline >= 0: + llyfac = txt[iline].split()[-1] + else: + raise ValueError('Failed to extract llyfac in KkrimpCalculation') + + # now write kkrflex_llyfac to tempfolder where later on config file is also written + with tempfolder.open(self._KKRFLEX_LLYFAC, 'w') as f2: + f2.writelines([llyfac]) return runflag def _activate_jij_calc(self, runflag, params_kkrimp, GFhost_folder, tempfolder): diff --git a/aiida_kkr/calculations/voro.py b/aiida_kkr/calculations/voro.py index ac6cad5c..9bc66d6a 100644 --- a/aiida_kkr/calculations/voro.py +++ b/aiida_kkr/calculations/voro.py @@ -14,7 +14,7 @@ __copyright__ = (u'Copyright (c), 2017, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.5.3' +__version__ = '0.5.4' __contributors__ = ('Jens Broeder', 'Philipp Rüßmann') @@ -182,7 +182,8 @@ def prepare_for_submission(self, tempfolder): # Decide what files to copy local_copy_list = [] if overwrite_potential: - # copy the right files #TODO check first if file, exists and throw + # copy the right files + #TODO check first if file, exists and throw # warning, now this will throw an error if found_parent and self._is_KkrCalc(parent_calc): outfolder = parent_calc.outputs.retrieved # copy from remote folder @@ -199,11 +200,11 @@ def prepare_for_submission(self, tempfolder): filename = self._POTENTIAL_IN_OVERWRITE local_copy_list.append((outfolder.uuid, file1, filename)) # pylint: disable=possibly-used-before-assignment - # add shapefun to overwrite - if 'shapefun_overwrite' in self.inputs: - shapefun_overwrite = self.inputs.shapefun_overwrite - filename = shapefun_overwrite.filename - local_copy_list.append((shapefun_overwrite.uuid, filename, 'shapefun_overwrite')) + # add shapefun to overwrite + if 'shapefun_overwrite' in self.inputs: + shapefun_overwrite = self.inputs.shapefun_overwrite + filename = shapefun_overwrite.filename + local_copy_list.append((shapefun_overwrite.uuid, filename, 'shapefun_overwrite')) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() diff --git a/aiida_kkr/parsers/kkrimp.py b/aiida_kkr/parsers/kkrimp.py index 79783da2..9e78d855 100644 --- a/aiida_kkr/parsers/kkrimp.py +++ b/aiida_kkr/parsers/kkrimp.py @@ -8,8 +8,8 @@ import tarfile import os from aiida import __version__ as aiida_core_version -from aiida.orm import Dict -from aiida.common.exceptions import InputValidationError, NotExistent +from aiida.orm import Dict, CalcJobNode +from aiida.common.exceptions import InputValidationError, NotExistent, NotExistentAttributeError from aiida.parsers.parser import Parser from aiida_kkr.calculations.kkrimp import KkrimpCalculation from aiida_kkr.tools.context import open_files_in_context @@ -21,8 +21,8 @@ __copyright__ = (u'Copyright (c), 2018, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.6.0' -__contributors__ = ('Philipp Rüßmann') +__version__ = '0.6.2' +__contributors__ = (u'Philipp Rüßmann', u'Raffaele Aliberti') class KkrimpParser(Parser): @@ -42,7 +42,7 @@ def __init__(self, calc): # pylint: disable=protected-access # pylint: disable=unexpected-keyword-arg - def parse(self, debug=False, ignore_nan=True, **kwargs): + def parse(self, debug=False, ignore_nan=True, doscalc=None, **kwargs): """ Parse output data folder, store results in database. @@ -64,6 +64,19 @@ def parse(self, debug=False, ignore_nan=True, **kwargs): file_errors = [] files = {} + # Get the node of the parent calculation (useful for the imp calculations) + calc = out_folder.get_incoming(node_class=CalcJobNode).first().node + + # figure out if this is a DOS calculation (if not specified in the input already) + if doscalc is None: + parent_host_calc = calc.inputs.host_Greenfunction_folder.get_incoming(node_class=CalcJobNode).first().node + # This parameter discriminates if it is a DOS calc or not + npol = parent_host_calc.inputs.parameters.get_dict().get('NPOL', 1) + doscalc = (npol == 0) + + if debug: + print('doscalc = ', doscalc) + # Parse output files of KKRimp calculation # first get path to files and catch errors if files are not present @@ -113,7 +126,7 @@ def parse(self, debug=False, ignore_nan=True, **kwargs): # now we can parse the output files success, msg_list, out_dict = KkrimpParserFunctions().parse_kkrimp_outputfile( - out_dict, named_file_handles, debug=debug, ignore_nan=ignore_nan + out_dict, named_file_handles, debug=debug, ignore_nan=ignore_nan, doscalc=doscalc ) out_dict['parser_errors'] = msg_list diff --git a/aiida_kkr/tools/__init__.py b/aiida_kkr/tools/__init__.py index 4732a28d..4c0793d8 100644 --- a/aiida_kkr/tools/__init__.py +++ b/aiida_kkr/tools/__init__.py @@ -6,7 +6,7 @@ from .common_workfunctions import ( update_params_wf, prepare_VCA_structure_wf, prepare_2Dcalc_wf, test_and_get_codenode, get_inputs_kkr, get_inputs_kkrimporter, get_inputs_voronoi, get_inputs_kkrimp, get_parent_paranode, - generate_inputcard_from_structure, check_2Dinput_consistency, structure_from_params, vca_check + generate_inputcard_from_structure, check_2Dinput_consistency, structure_from_params, vca_check, truncate_string ) from .find_cluster_radius import find_cluster_radius from .plot_kkr import plot_kkr diff --git a/aiida_kkr/tools/combine_imps.py b/aiida_kkr/tools/combine_imps.py index 58cdf5bb..b56ed828 100644 --- a/aiida_kkr/tools/combine_imps.py +++ b/aiida_kkr/tools/combine_imps.py @@ -32,10 +32,6 @@ def get_host_structure(impurity_workflow_or_calc): extract host structure from impurity """ #TODO extract host parent no from input but take into account calculation of host GF from inside kkrimp full workflow - print( - f'This is line in the combine impurity tool files at:: /opt/aiida-kkr/aiida_kkr/tools for deburging the line', - end=' ' - ) print(f'impurity_workflow_or_calc: {impurity_workflow_or_calc}') if impurity_workflow_or_calc.process_class == KkrimpCalculation: host_parent = impurity_workflow_or_calc.inputs.host_Greenfunction_folder diff --git a/aiida_kkr/tools/common_workfunctions.py b/aiida_kkr/tools/common_workfunctions.py index 6ba2218e..66319345 100644 --- a/aiida_kkr/tools/common_workfunctions.py +++ b/aiida_kkr/tools/common_workfunctions.py @@ -336,6 +336,16 @@ def get_inputs_kkrimp( return builder +def truncate_string(string, max_length): + """ + Truncate the length of a string to max_length-3 entries. + The last three characters '...' to indicate the truncation. + """ + if len(string) > max_length: + string = string[:max_length - 3] + '...' + return string + + def get_inputs_common( calculation, code, @@ -381,7 +391,8 @@ def get_inputs_common( inputs.metadata.description = '' if label: - inputs.metadata.label = label + # Attention: max label length is 255 characters + inputs.metadata.label = truncate_string(label, 255) else: inputs.metadata.label = '' diff --git a/aiida_kkr/tools/imp_cluster_tools.py b/aiida_kkr/tools/imp_cluster_tools.py index 775a56e9..21726c12 100644 --- a/aiida_kkr/tools/imp_cluster_tools.py +++ b/aiida_kkr/tools/imp_cluster_tools.py @@ -45,15 +45,22 @@ def get_scoef_single_imp(host_structure, impinfo_node): :return: scoef array (positions [x,y,z], layer index, distance to first position in imp cluster) :type: numpy.array """ - impinfo = impinfo_node.get_dict() - Rcut = impinfo.get('Rcut', None) - hcut = impinfo.get('hcut', -1.) - cylinder_orient = impinfo.get('cylinder_orient', [0., 0., 1.]) - ilayer_center = impinfo.get('ilayer_center', 0) - - clust = create_scoef_array(host_structure, Rcut, hcut, cylinder_orient, ilayer_center) - # sort after distance - clust = clust[(clust[:, -1]).argsort()] + # check if we can read it from a node extra + if 'imp_cls' not in impinfo_node.extras: + impinfo = impinfo_node.get_dict() + Rcut = impinfo.get('Rcut', None) + hcut = impinfo.get('hcut', -1.) + cylinder_orient = impinfo.get('cylinder_orient', [0., 0., 1.]) + ilayer_center = impinfo.get('ilayer_center', 0) + + clust = create_scoef_array(host_structure, Rcut, hcut, cylinder_orient, ilayer_center) + # sort after distance + clust = clust[(clust[:, -1]).argsort()] + + # save as extra for second run + impinfo_node.set_extra('imp_cls', clust) + else: + clust = np.array(impinfo_node.extras['imp_cls']) return clust diff --git a/aiida_kkr/tools/tools_STM_scan.py b/aiida_kkr/tools/tools_STM_scan.py index 123bf306..ccd0f551 100644 --- a/aiida_kkr/tools/tools_STM_scan.py +++ b/aiida_kkr/tools/tools_STM_scan.py @@ -13,7 +13,7 @@ __copyright__ = (u'Copyright (c), 2023, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.1.2' +__version__ = '0.1.4' __contributors__ = (u'Philipp Rüßmann', u'Raffaele Aliberti') ############################################################################## @@ -25,7 +25,7 @@ def convert_to_imp_cls(host_structure, imp_info): convert imp info to rcls form """ if 'imp_cls' in imp_info.get_dict(): - clust1 = imp_info['imp_cls'] + clust1 = np.array(imp_info['imp_cls']) imp_info_cls = imp_info else: # convert Zimp, Rcut info to imp_cls info @@ -56,7 +56,7 @@ def get_imp_cls_add(host_structure, add_position): ilayer = add_position['ilayer'] imp_info2 = orm.Dict({'ilayer_center': ilayer, 'Zimp': [Zadd], 'Rcut': 1e-5}) # old version is too slow: - # clust2 = get_scoef_single_imp(host_structure, imp_info2) + #clust2 = get_scoef_single_imp(host_structure, imp_info2) # new version creates the array without calling the get_scoef_single_imp function: clust2 = np.array([[0., 0., 0., ilayer + 1, 0., 0.]]) return imp_info2, clust2 @@ -80,9 +80,9 @@ def get_r_offset(clust1, clust2, host_structure, add_position): alat = get_alat_from_bravais(np.array(host_structure.cell), host_structure.pbc[2]) r_out_of_plane /= alat - # remove spurious offsets that might be there due to the choice of the unit cell positions - r_out_of_plane = np.round(r_out_of_plane, 7) - r_out_of_plane[:2] %= 1 # modulo 1 for x and y coordinate + # # remove spurious offsets that might be there due to the choice of the unit cell positions + # r_out_of_plane = np.round(r_out_of_plane, 7) + # r_out_of_plane[:2] %= 1 # modulo 1 for x and y coordinate # calculate in-plane vector from da, db inputs da = add_position.get_dict().get('da', 0) @@ -132,10 +132,16 @@ def get_imp_info_add_position(add_position, host_structure, imp_info): # combine cluster information pos_exists_in_imp1, _ = pos_exists_already(clust1, clust2) if pos_exists_in_imp1: - raise ValueError('Additional position exists already in impurity cluster.') + # If the position exists already we simply skip the addition of the new scanning position + return None + #raise ValueError('Additional position exists already in impurity cluster.') cluster_combined, rimp_rel_combined, _, _ = combine_clusters(clust1, clust2_offset, False, debug=False) # combine the zimp arrays - zimp_combined = imp_info['Zimp'] + imp_info2['Zimp'] + zimp1 = imp_info['Zimp'] + if not isinstance(zimp1, list): + # convert to list if necessary + zimp1 = [zimp1] + zimp_combined = zimp1 + imp_info2['Zimp'] # now combine the imp info node imp_info_combined = orm.Dict({'imp_cls': cluster_combined, 'Zimp': zimp_combined, 'Rimp_rel': rimp_rel_combined}) @@ -247,78 +253,76 @@ def create_combined_potential_node_cf(add_position, host_calc, imp_potential_nod def STM_pathfinder(host_remote): - """This function is used to help visualize the scanned positions - and the symmetries that are present in the system + """ + Calcfunction that gives back the structural information of the film, and the symmetries of the system + + inputs :: - inputs:: - host_remote : RemoteData : The Remote data contains all the information needed to create the path to scan + host_remote : Remote_data : node containing the remote data of the host material - outputs:: - struc_info : Dict : Dictionary containing the structural information of the film - matrices : Array : Array containing the matrices that generate the symmetries of the system + return :: + + plane_vectors : list : list containing the 2 in plane vectors that span the surface. + unique_matrices : list : list of matrices, contains the 2x2 matrices that constitue the symmetry operations of the system. """ - def info_creation(structure): - from ase.spacegroup import get_spacegroup - # List of the Bravais vectors - vec_list = structure.cell.tolist() + from pymatgen.symmetry.analyzer import SpacegroupAnalyzer, SymmOp - # Find the Bravais vectors that are in plane vectors - plane_vectors = {'plane_vectors': [], 'space_group': ''} - for vec in vec_list: - # Is this sufficient to find all the in-plane vectors? - if vec[2] == 0: - plane_vectors['plane_vectors'].append(vec[:2]) + struc = find_parent_structure(host_remote) + # clone the structure since it has already been saved in AiiDA and cannot be modified + supp_struc = struc.clone() - space_symmetry = get_spacegroup(structure) - plane_vectors['space_group'] = space_symmetry.no + # If the structure is not periodic in every direction we force it to be. + supp_struc.pbc = (True, True, True) - return plane_vectors + # Pymatgen struc + py_struc = supp_struc.get_pymatgen() - def symmetry_finder(struc_info): - from ase.spacegroup import Spacegroup - # Here we get the symmetry operations that are possible - symmetry_matrices = Spacegroup(struc_info['space_group']) + struc_dict = py_struc.as_dict() + # Find the Bravais vectors that are in-plane vectors (assumes 2D structure) + plane_vectors = {'plane_vectors': [], 'space_group': ''} + for vec in struc_dict['lattice']['matrix']: + # Is this sufficient to find all the in-plane vectors? + if vec[2] == 0 or (struc.pbc[2] and (vec[0] + vec[1]) > 0): + plane_vectors['plane_vectors'].append(vec[:2]) + # finally check if setting of plane_vectors worked + if 'plane_vectors' not in plane_vectors: + raise ValueError('Could not set "plane_vectors" in STM_pathfinder') - # Reduce the dimensionality, we only want the 2D matrices - matrices = [] - for element in symmetry_matrices.get_rotations(): - matrices.append(element[:2, :2]) + # Here we get the symmetry operations that are possible + symmetry_matrices = SpacegroupAnalyzer(py_struc).get_point_group_operations(cartesian=True) - # Uniqueness of the elements must be preserved - unique_matrices = [] - for matrix in matrices: - if not any(np.array_equal(matrix, m) for m in unique_matrices): - unique_matrices.append(matrix) + plane_vectors['space_group'] = SpacegroupAnalyzer(py_struc).get_symmetry_dataset()['number'] - return unique_matrices + # Here we get the symmetry rotations - struc = find_parent_structure(host_remote) - # clone the structure since it has already been saved in AiiDA and cannot be modified - supp_struc = struc.clone() + supp_mat = [] - # If the structure is not periodic in every direction we force it to be. - if not supp_struc.pbc[2]: - # find film thickness - zs = np.array([i.position[2] for i in supp_struc.sites]) - z = zs.max() - zs.min() + 5 # add 5 to have a unit cell larger than the considered film thickness - # set third bravais vector along z direction - cell = supp_struc.cell - cell[2] = [0, 0, z] - supp_struc.set_cell(cell) - # change periodic boundary conditions to periodic - supp_struc.pbc = (True, True, True) - - # ASE struc - ase_struc = supp_struc.get_ase() - - # Structural informations are stored here - struc_info = info_creation(ase_struc) - - # The structural informations are then used to find the symmetries of the system - symm_matrices = symmetry_finder(struc_info) + # Get the affine representation of the matrices + for symmops in range(len(symmetry_matrices)): + supp_mat.append(np.array(SymmOp.as_dict(symmetry_matrices[symmops])['matrix'][:3])) - return struc_info, symm_matrices + # Get only the rotation matrices and correct the numerical error + rot_mat = [] + + # Take only the matrices for the in-plane rotations + for elements in range(len(supp_mat)): + rot_mat.append(supp_mat[elements][0:2, 0:2]) + + # Sometimes it will happen that some rotation matrices projected to the 2D space will have the same representation. + # here we only take the unique ones + unique_matrices = [] + for matrix in rot_mat: + if not any(np.array_equal(matrix, m) for m in unique_matrices): + unique_matrices.append(matrix) + + # Round off the numerical error + for elements in range(len(unique_matrices)): + for rows in range(len(unique_matrices[elements])): + for cols in range(len(unique_matrices[elements][rows])): + unique_matrices[elements][rows][cols] = round(unique_matrices[elements][rows][cols]) + + return plane_vectors, unique_matrices @engine.calcfunction @@ -336,53 +340,65 @@ def STM_pathfinder_cf(host_structure): # lattice generation (function of lattice plot) -def lattice_generation(x_len, y_len, rot, vec): - import math - """ - x_len : int : value to create points between - x and x. - y_len : int : value to create points between - y and y. - rot : list : list of the rotation matrices given by the symmetry of the system. - vec : list : list containing the two Bravais vectors. +def lattice_generation(rot, vec, x_start, y_start, xmax, ymax): """ - # Here we create a grid in made of points whic are the linear combination of the lattice vectors - lattice_points = [] + inputs :: - for i in range(-x_len, x_len + 1): - lattice_points_col = [] - for j in range(-y_len, y_len + 1): - p = [i * x + j * y for x, y in zip(vec[0], vec[1])] - lattice_points_col.append(p) - lattice_points.append(lattice_points_col) + x_len : int : value to create points between - x and x. + y_len : int : value to create points between - y and y. + rot : list : list of the rotation matrices given by the symmetry of the system. + vec : list : list containing the two Bravais vectors. + start_x: int : starting value for the lattice generation in the x direction + start_y: int : starting value for the lattice generation in the y direction - # Eliminiatio of the symmetrical sites - points_to_eliminate = [] + return :: + + points_to_eliminate : list : list of list containing the (x,y) positions to NOT to + be scanned + points_to_scan : list : list of list containing the (x,y) positions to BE be + scanned + """ + + # Here we create a grid made of points which are the linear combination of the lattice vectors + x_len = xmax * 10 + y_len = ymax * 10 # maybe there is a way to make this more efficient... - for i in range(-x_len, x_len + 1): - for j in range(-y_len, y_len + 1): - if ((lattice_points[i][j][0] > 0 or math.isclose(lattice_points[i][j][0], 0, abs_tol=1e-3)) and - (lattice_points[i][j][1] > 0 or math.isclose(lattice_points[i][j][1], 0, abs_tol=1e-3))): - for element in rot[1:]: - point = np.dot(element, lattice_points[i][j]) - if point[0] >= 0 and point[1] >= 0: - continue - else: - points_to_eliminate.append(point) + x_interval = [i for i in range(-x_len, x_len)] + + y_interval = [i for i in range(-y_len, y_len)] points_to_scan = [] - for i in range(-x_len, x_len + 1): - for j in range(-y_len, y_len + 1): - eliminate = False - for elem in points_to_eliminate: - # Since there can be some numerical error in the dot product we use the isclose function - if ( - math.isclose(elem[0], lattice_points[i][j][0], abs_tol=1e-4) and - math.isclose(elem[1], lattice_points[i][j][1], abs_tol=1e-4) - ): - eliminate = True - if not eliminate: - points_to_scan.append(lattice_points[i][j]) + # Now we only generate points in the first quadrant and the we use the symmetry analysis + # To visualize the other unscanned sites + + for i in x_interval: + points_to_scan_col = [] + for j in y_interval: + p = [i * x + j * y for x, y in zip(vec[0], vec[1])] + if ((p[0] < 0 or p[0] > xmax) or (p[1] < 0 or p[1] > ymax)) or (p[0] < x_start or p[1] < y_start): + continue + else: + points_to_scan_col.append(p) + if len(points_to_scan_col) != 0: + points_to_scan.append(points_to_scan_col) + + #print(lattice_points) + points_to_eliminate = [] + + for i in range(len(points_to_scan)): + for j in range(len(points_to_scan[i])): + #print(lattice_points[i][j]) + #if lattice_points[i][j][0] >= 0 and lattice_points[i][j][1] >= 0: + for element in rot[1:]: + point = np.dot(element, points_to_scan[i][j]) + #if point[0] >= 0 and point[1] >=0: + # continue + #else: + points_to_eliminate.append(point) + + #print(point_to_eliminate) return points_to_eliminate, points_to_scan @@ -392,13 +408,31 @@ def lattice_generation(x_len, y_len, rot, vec): def lattice_plot(plane_vectors, symm_vec, symm_matrices, grid_length_x, grid_length_y): + """ + Helper tool to plot the position that will be scanned in the submission of the + kkr_STM_wc workchain + + inputs :: + + plane_vectors : list : list containing the Bravais vector of the 2D lattice + symm_vec : bool : Toggle to show or not the Bravais vectors in the plot + symm_matrices : list : list of the point-group symmetries matrices of the system + grid_length_x : int : scanning distance in the x direction + grid_length_y : int : scanning distance in the y direction + + return :: + + None + + """ + #from aiida_kkr.tools.tools_STM_scan import lattice_generation import matplotlib.pyplot as plt from matplotlib.lines import Line2D origin = np.array([[0, 0], [0, 0]]) # Generation of the points to plot - unused, used = lattice_generation(grid_length_x, grid_length_y, symm_matrices, plane_vectors) + unused, used = lattice_generation(symm_matrices, plane_vectors, 0, 0, grid_length_x, grid_length_y) # Plotting of the points for element in unused: @@ -446,32 +480,34 @@ def lattice_plot(plane_vectors, symm_vec, symm_matrices, grid_length_x, grid_len def find_linear_combination_coefficients(plane_vectors, vectors): from operator import itemgetter """This helper function takes the planar vectors and a list of vectors - and return the coefficients in the base of the planar vectors""" + and return the coefficients in the base of the planar vectors - # Formulate the system of equations Ax = b - A = np.vstack((plane_vectors[0], plane_vectors[1])).T + inputs :: - # Solve the system of equations using least squares method - data = [] - for element in vectors: - b = element - # We use the least square mean error procedure to evaulate the units of da and db - # lstsq returns: coeff, residue, rank, and singular value - # We only need the coefficient. - data.append(np.linalg.lstsq(A, b, rcond=None)) + plane vectors: list : list of list of the form [[a_x, a_y], [b_x, b_y]] + returns :: + + indices : list : list of list of the form [[int_1, int_1]...[int_n, int_n]] + the integers refers to how many times that specific vectors is + present in the linear combination r = int_x * a + int_2 * b + + """ + + # Formulate the system of equations Ax = b + A = np.vstack((plane_vectors[0], plane_vectors[1])).T + # inverse of A matrix + Ainv = np.matrix(A)**(-1) indices = [] - for element in data: - supp = [] - for elem in element[0]: - # Here we round to an integer, this is because of the numerical error - # which is present inside the calculation. - supp.append(round(elem)) - indices.append(supp) - - # Before returning the indices, we reorder them first from the lowest to the highest valued - # on the x axis and then from the lowest to the highest on the y axis. - - indices = sorted(indices, key=itemgetter(0, 1)) + # loop over flattened list + for vec in np.array(vectors).reshape(-1, 2): + # get indices from x = A^{-1}.b + index = np.array((Ainv * np.matrix(vec).transpose()).transpose()).reshape(2) + indices.append(index) + # make sure to have integer values + indices = np.round(np.array(indices), 0) + + isort = indices[:, 0] + indices[:, 1] / (10 * max(indices[:, 0])) + indices = indices[isort.argsort()] return indices diff --git a/aiida_kkr/workflows/_combine_imps.py b/aiida_kkr/workflows/_combine_imps.py index 899b3ec6..f6afafb4 100644 --- a/aiida_kkr/workflows/_combine_imps.py +++ b/aiida_kkr/workflows/_combine_imps.py @@ -19,7 +19,7 @@ __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.5' +__version__ = '0.3.6' __contributors__ = (u'Philipp Rüßmann , Rubel Mozumder, David Antognini Silva') # activate debug writeout @@ -289,7 +289,14 @@ def combine_single_single(self): imp_2 = self.ctx.imp2 # check for the impurity1 whether from single kkr_imp_wc or not if imp_1.process_class == KkrimpCalculation: - Zimp_num_1 = imp_1.inputs.impurity_info.get_dict().get('Zimp') + try: + impurity_info = imp_1.inputs.impurity_info + except: + host_GF = imp_1.inputs.host_Greenfunction_folder + host_GF_calc = host_GF.base.links.get_incoming(node_class=CalcJobNode).first().node + impurity_info = host_GF_calc.inputs.impurity_info + Zimp_num_1 = impurity_info.get_dict().get('Zimp') + if isinstance(Zimp_num_1, list): if len(Zimp_num_1) > 1: single_imp_1 = False @@ -304,7 +311,14 @@ def combine_single_single(self): # check for the impurity2 whether from single kkr_imp_wc or not if imp_2.process_class == KkrimpCalculation: - Zimp_num_2 = imp_2.inputs.impurity_info.get_dict().get('Zimp') + try: + impurity_info = imp_2.inputs.impurity_info + except: + host_GF = imp_2.inputs.host_Greenfunction_folder + host_GF_calc = host_GF.base.links.get_incoming(node_class=CalcJobNode).first().node + impurity_info = host_GF_calc.inputs.impurity_info + Zimp_num_2 = impurity_info.get_dict().get('Zimp') + if isinstance(Zimp_num_2, list): if len(Zimp_num_2) > 1: single_imp_2 = False @@ -393,7 +407,7 @@ def extract_imps_info_exact_cluster(self): imps_info_in_exact_cluster['Zimps'].append(Zimp_2) imps_info_in_exact_cluster['ilayers'].append(imp2_impurity_info.get_dict()['ilayer_center']) - # TODO: Delete the below print line as it is for deburging + # TODO: Delete the below print line as it is for debugging self.report(f'DEBUG: The is the imps_info_in_exact_cluster dict: {imps_info_in_exact_cluster}\n') return 0, imps_info_in_exact_cluster # return also exit code 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/aiida_kkr/workflows/gf_writeout.py b/aiida_kkr/workflows/gf_writeout.py index 831e470f..a780acfb 100644 --- a/aiida_kkr/workflows/gf_writeout.py +++ b/aiida_kkr/workflows/gf_writeout.py @@ -153,6 +153,7 @@ def start(self): ####### init ####### # internal para / control para self.ctx.abort = False + self.ctx.exit_code = None # input both wf and options parameters options_dict = self.inputs.options.get_dict() @@ -224,13 +225,13 @@ def validate_input(self): if not 'impurity_info' in inputs: input_ok = False - return self.exit_codes.ERROR_INVALID_INPUT_IMP_INFO # pylint: disable=no-member + self.ctx.exit_code = self.exit_codes.ERROR_INVALID_INPUT_IMP_INFO # pylint: disable=no-member if 'remote_data' in inputs: input_ok = True else: input_ok = False - return self.exit_codes.ERROR_INVALID_REMOTE_DATA # pylint: disable=no-member + self.ctx.exit_code = self.exit_codes.ERROR_INVALID_REMOTE_DATA # pylint: disable=no-member # extract correct remote folder of last calculation if input remote_folder node # is not from KKRCalculation but kkr_scf_wc workflow @@ -264,7 +265,7 @@ def validate_input(self): 'use the plugin kkr.kkr') self.ctx.errors.append(error) input_ok = False - return self.exit_codes.ERROR_INVALID_INPUT_KKR # pylint: disable=no-member + self.ctx.exit_code = self.exit_codes.ERROR_INVALID_INPUT_KKR # pylint: disable=no-member # set self.ctx.input_params_KKR self.ctx.input_params_KKR = get_parent_paranode(self.inputs.remote_data) @@ -334,19 +335,20 @@ def set_params_flex(self): self.report(f'INFO: RUNOPT set to: {runopt}') if 'wf_parameters' in self.inputs: - # extract Fermi energy in Ry - remote_data_parent = self.inputs.remote_data - parent_calc = remote_data_parent.get_incoming(link_label_filter='remote_folder').first().node - ef = parent_calc.outputs.output_parameters.get_dict().get('fermi_energy') - # check if ef needs to be taken from a voronoi parent - if ef is None: - objects = parent_calc.outputs.retrieved.list_object_names() - fname = VoronoiCalculation._OUT_POTENTIAL_voronoi - if fname in objects: - with parent_calc.outputs.retrieved.open(fname) as _f: - ef = get_ef_from_potfile(_f) - if ef is None: - return self.exit_codes.ERROR_NO_EF_FOUND # pylint: disable=no-member + if self.ctx.dos_run or self.ctx.ef_shift != 0: + # extract Fermi energy in Ry + remote_data_parent = self.inputs.remote_data + parent_calc = remote_data_parent.get_incoming(link_label_filter='remote_folder').first().node + ef = parent_calc.outputs.output_parameters.get_dict().get('fermi_energy') + # check if ef needs to be taken from a voronoi parent + if ef is None: + objects = parent_calc.outputs.retrieved.list_object_names() + fname = VoronoiCalculation._OUT_POTENTIAL_voronoi + if fname in objects: + with parent_calc.outputs.retrieved.open(fname) as _f: + ef = get_ef_from_potfile(_f) + if ef is None: + return self.exit_codes.ERROR_NO_EF_FOUND # pylint: disable=no-member if self.ctx.dos_run: # possibly remove keys which are overwritten from DOS params @@ -483,6 +485,9 @@ def return_results(self): therefore it only uses results from context. """ + if self.ctx.exit_code is not None: + return self.ctx.exit_code + # capture error of unsuccessful flexrun if not self.ctx.flexrun.is_finished_ok: self.ctx.successful = False diff --git a/aiida_kkr/workflows/imp_BdG.py b/aiida_kkr/workflows/imp_BdG.py index 8b9af222..060c551c 100644 --- a/aiida_kkr/workflows/imp_BdG.py +++ b/aiida_kkr/workflows/imp_BdG.py @@ -9,7 +9,7 @@ __copyright__ = (u'Copyright (c), 2022, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.1.2' +__version__ = '0.1.3' __contributors__ = (u'David Antognini Silva, Philipp Rüßmann') # TODO: add _wf_default parameters and activate get_wf_defaults method @@ -474,9 +474,9 @@ def results(self): return the results nodes of the workchain """ if self.inputs.calc_DOS: - if 'dos_data' in self.ctx.DOS_node.outputs.dos_data: + if 'dos_data' in self.ctx.DOS_node.outputs: self.out('dos_data', self.ctx.DOS_node.outputs.dos_data) - if 'dos_data_interpol' in self.ctx.DOS_node.outputs.dos_data_interpol: + if 'dos_data_interpol' in self.ctx.DOS_node.outputs: self.out('dos_data_interpol', self.ctx.DOS_node.outputs.dos_data_interpol) if 'dos_data_lm' in self.ctx.DOS_node.outputs: self.out('dos_data_lm', self.ctx.DOS_node.outputs.dos_data_lm) diff --git a/aiida_kkr/workflows/kkr_STM.py b/aiida_kkr/workflows/kkr_STM.py index 46d78f9c..996f0fd9 100644 --- a/aiida_kkr/workflows/kkr_STM.py +++ b/aiida_kkr/workflows/kkr_STM.py @@ -13,9 +13,9 @@ __copyright__ = (u'Copyright (c), 2024, Forschungszentrum Jülich GmbH, ' 'IAS-1/PGI-1, Germany. All rights reserved.') __license__ = 'MIT license, see LICENSE.txt file' -__version__ = '0.1.1' +__version__ = '0.1.5' __contributors__ = (u'Raffaele Aliberti', u'David Antognini Silva', u'Philipp Rüßmann') -_VERBOSE_ = True +_VERBOSE_ = False class kkr_STM_wc(WorkChain): @@ -33,7 +33,7 @@ class kkr_STM_wc(WorkChain): :param kkr: (Code), KKR host code for the writing out kkrflex files :param kkrimp: (Code), KKR impurity code for the normal state impurity scf and BdG impurity DOS calculation :param gf_writeout.params_kkr_overwrite (Dict), overwrite parameters for the GF calculation - :param kkr_imp_sub.params_kkr_overwrite (Dict), overwrite parameters for the impurity calculation + :param kkr_imp_sub.params_overwrite (Dict), overwrite parameters for the impurity calculation returns:: @@ -45,8 +45,7 @@ class kkr_STM_wc(WorkChain): # TO DO: Add the initialize step. # TO DO: Add a better creation of the impurity cluster. # TO DO: Add check that between the ilayer and the actual number of layers in the structure. - # TO DO: Add to the outputs the calculated imp_info and imp_potential. - # TO DO: Add BdG options for the builder run + # TO DO: Don't run the clustering step if kkrflexfiles are given: It's redundant and it can lead to errors _wf_version = __version__ _wf_label = 'STM_wc' @@ -73,6 +72,18 @@ class kkr_STM_wc(WorkChain): # add defaults of dos_params since they are passed onto that workflow _wf_default['dos_params'] = kkr_imp_dos_wc.get_wf_defaults()['dos_params'] + # return default values (helpful for users) + @classmethod + def get_wf_defaults(cls, silent=False): + """Print and return _wf_default dictionary. + + Can be used to easily create set of wf_parameters. + returns _wf_default, _options_default + """ + if not silent: + print(f'Version of workflow: {cls._wf_version}') + return cls._wf_default + @classmethod def define(cls, spec): """ @@ -119,13 +130,6 @@ def define(cls, spec): required=True, help='Information of the impurity like position in the unit cell, screening cluster, atom type.' ) - spec.input( - 'host_calc', - valid_type=RemoteData, - required=False, - help='The information about the clean host structure is required in order to continue the cluster' - 'Inside a bigger host structure with empty sites.' - ) spec.input( 'host_remote', valid_type=RemoteData, @@ -138,12 +142,6 @@ def define(cls, spec): required=True, help='Impurity potential node', ) - spec.input( - 'remote_data', - valid_type=RemoteData, - required=False, - help='Remote data from a converged kkr calculation, required for the gf writeout step', - ) spec.input( 'kkrflex_files', valid_type=RemoteData, @@ -189,24 +187,21 @@ def define(cls, spec): cls.results ) - def combine_potentials(self, host_structure, impurity_to_combine, da, db): + def combine_imp_info(self, host_structure, impurity_to_combine, da, db): from aiida_kkr.tools.tools_STM_scan import get_imp_info_add_position import numpy as np # TO DO: optimize this call, only need append from numpy """ Here we want to combine the impurity information and the host information """ - tip_position = {} - - tip_position['ilayer'] = self.inputs.tip_position['ilayer'] - tip_position['da'] = da - tip_position['db'] = db + tip_position = self.get_tip_position_dict(da, db) imp_info = self.inputs.imp_info #(impurity to combine) combined_imp_info = get_imp_info_add_position(Dict(tip_position), host_structure, imp_info) - # Since the objects in AiiDA are immutable we have to create a new dictionary and then convert - # it to the right AiiDA type - #new_combined_imp_info = {} + # If the position already exists we simply return the old dictionary + if combined_imp_info is None: + return impurity_to_combine + # Add check to see if imp_cls is there if 'imp_cls' in impurity_to_combine: @@ -225,23 +220,25 @@ def combine_potentials(self, host_structure, impurity_to_combine, da, db): return new_combined_imp_info - def combine_nodes(self, host_calc, node_to_combine, da, db): + def combine_potentials(self, host_calc, node_to_combine, da, db): from aiida_kkr.tools.tools_STM_scan import create_combined_potential_node """ Here we create a combined potential node from the host potential (no impurity) and from the impurity potential """ - # Since the objects in AiiDA are immutable we have to create a new dictionary and then convert - # it to the right AiiDA type + tip_position = self.get_tip_position_dict(da, db) + combined_potential_node = create_combined_potential_node(tip_position, host_calc, node_to_combine) + return combined_potential_node + + def get_tip_position_dict(self, da, db): + # Since the objects in AiiDA are immutable we have to create a new dictionary + # and then convert it to the right AiiDA type tip_position = {} - # for now we require that the z position remains the same. tip_position['ilayer'] = self.inputs.tip_position['ilayer'] tip_position['da'] = da tip_position['db'] = db - - combined_node = create_combined_potential_node(tip_position, host_calc, node_to_combine) - return combined_node + return tip_position def start(self): """ @@ -309,14 +306,14 @@ def start(self): if _VERBOSE_: message = f""" -INFO: use the following parameter: -withmpi: {self.ctx.withmpi} -Resources: {self.ctx.resources} -Walltime (s): {self.ctx.max_wallclock_seconds} -queue name: {self.ctx.queue} -scheduler command: {self.ctx.custom_scheduler_commands} -description: {self.ctx.description_wf} -label: {self.ctx.label_wf} + INFO: use the following parameter: + withmpi: {self.ctx.withmpi} + Resources: {self.ctx.resources} + Walltime (s): {self.ctx.max_wallclock_seconds} + queue name: {self.ctx.queue} + scheduler command: {self.ctx.custom_scheduler_commands} + description: {self.ctx.description_wf} + label: {self.ctx.label_wf} """ self.report(message) @@ -339,9 +336,11 @@ def impurity_cluster_evaluation(self): used in self-consistency + the additional scanning sites. """ from aiida_kkr.tools import find_parent_structure + from aiida_kkr.tools.imp_cluster_tools import pos_exists_already + from aiida_kkr.tools.tools_STM_scan import get_imp_cls_add, convert_to_imp_cls, offset_clust2 + if _VERBOSE_: from time import time - # measure time at start t_start = time() @@ -361,29 +360,66 @@ def impurity_cluster_evaluation(self): # timing counters t_imp_info, t_pot = 0., 0. + _, imp_clust = convert_to_imp_cls(host_structure, impurity_info) + # construct impurity potential and imp_info for the impurity cluster + scanning area + + # Check if the impuirty cluster already exists, if so create a new entity that can be modified + if 'imp_cls' in impurity_info: + impurity_info_aux = impurity_info.clone() + imp_potential_node_aux = imp_potential_node.clone() + else: # Otherwise use the one from the inputs + impurity_info_aux = impurity_info + imp_potential_node_aux = imp_potential_node + + # Case in which we don't pass any element to embed in the impurity cluster, it + # uses the impurity files given in the inputs. + if len(coeff) == 0: + return impurity_info, imp_potential_node + for element in coeff: - if _VERBOSE_: - t0 = time() - tmp_imp_info = self.combine_potentials(host_structure, impurity_info, element[0], element[1]) - impurity_info = tmp_imp_info if _VERBOSE_: - t_imp_info += time() - t0 t0 = time() - # Aggregation the impurity nodes - tmp_imp_pot = self.combine_nodes(host_calc, imp_potential_node, element[0], element[1]) - imp_potential_node = tmp_imp_pot + # Check if the position is already in the cluster + # for this we need to first get the position + tmp_pos = self.get_tip_position_dict(element[0], element[1]) + _, tmp_clust = get_imp_cls_add(host_structure, tmp_pos) + clust_offset = offset_clust2(imp_clust, tmp_clust, host_structure, Dict(tmp_pos)) if _VERBOSE_: - t_pot += time() - t0 + t_cluster_offset += time() - t0 + + if pos_exists_already(imp_clust[:, :3], clust_offset[:, :3])[0]: + message = f'INFO: The position {tmp_pos} is already present in the system, skipping it' + self.report(message) + continue # If the position exists already skip the embedding + else: + # Aggregation of the impurity potential + tmp_imp_info = self.combine_imp_info(host_structure, impurity_info_aux, element[0], element[1]) + impurity_info_aux = tmp_imp_info + + if _VERBOSE_: + self.report('imp info has been embedded') + t_imp_info += time() - t0 + t0 = time() + + # Aggregation the impurity nodes + tmp_imp_pot = self.combine_potentials(host_calc, imp_potential_node_aux, element[0], element[1]) + imp_potential_node_aux = tmp_imp_pot + + if _VERBOSE_: + self.report('imp potential has been added') + t_pot += time() - t0 if _VERBOSE_: # report elapsed time for cluster generation - self.report(f'time for cluster generation (s): {time()-t_start}, imp_info={t_imp_info}, pot={t_pot}') + self.report( + f'time for cluster generation (s): {time()-t_start}, cluster generation={t_cluster_offset}, imp_info={t_imp_info}, pot={t_pot}' + ) - return impurity_info, imp_potential_node + return impurity_info_aux, imp_potential_node_aux def get_scanning_positions(self, host_remote): """ @@ -395,6 +431,7 @@ def get_scanning_positions(self, host_remote): Otherwise we use the 'nx', 'ny' input to define a scanning region where an automated symmetry analysis is done to reduce the scanning area to the irreducible part. """ + # TO DO update this tool to get scanning positions even in the presence of more than one impurity from aiida_kkr.tools import tools_STM_scan generate_scan_positions = True @@ -405,6 +442,12 @@ def get_scanning_positions(self, host_remote): # TODO: improve the validity check generate_scan_positions = False + if _VERBOSE_: + if generate_scan_positions: + self.report('The scanning positions have been given by the user') + else: + self.repotr('The scanning positions were automatically generated') + if generate_scan_positions: # Information of the host structure @@ -412,11 +455,13 @@ def get_scanning_positions(self, host_remote): # We now want to iterate over several in-plane positions. # These are the number of vectors in which we want to move the STM tip. - x = self.inputs.tip_position['nx'] - y = self.inputs.tip_position['ny'] + nx = self.inputs.tip_position['nx'] + ny = self.inputs.tip_position['ny'] # Path creation step. (The the identity operator is present, but will be excluded) - unused_pos, used_pos = tools_STM_scan.lattice_generation(x, y, symm_matrices, struc_info['plane_vectors']) + unused_pos, used_pos = tools_STM_scan.lattice_generation( + symm_matrices, struc_info['plane_vectors'], 0, 0, nx, ny + ) # Since the combine tools use the element already in the units of da and db, we use a helper function # to have the indices of the linear combination of the used position vectors in the base of the Bravais lattice. @@ -439,7 +484,8 @@ def STM_lmdos_run(self): # Check if the kkrflex files are already given in the outputs if 'kkrflex_files' in self.inputs: builder.gf_dos_remote = self.inputs.kkrflex_files - message = f'Remote host function is given in the outputs from the node: {self.inputs.kkrflex_files}' + message = f"""Remote host function is given in the outputs from the node: {self.inputs.kkrflex_files}. Please also make sure of + using the right impurity potentials from the already converged calculation.""" self.report(message) else: builder.kkr = self.inputs.kkr # needed to evaluate the kkr_flex files in the DOS step @@ -457,8 +503,8 @@ def STM_lmdos_run(self): # Update the BdG parameters if they are inserted in the workflow if 'BdG' in self.inputs: - if 'params_kkr_overwrite' in self.inputs.BdG: - builder.BdG.params_overwrite = self.inputs.BdG.params_kkr_overwrite # pylint: disable=no-member + if 'params_overwrite' in self.inputs.BdG: + builder.BdG.params_overwrite = self.inputs.BdG.params_overwrite # pylint: disable=no-member self.ctx.kkrimp_params_dict = Dict( dict={ @@ -489,8 +535,25 @@ def STM_lmdos_run(self): builder.host_remote = self.inputs.host_remote # Here we create the impurity cluster for the STM scanning tool + + #if 'Rcut' in self.inputs.imp_info.get_dict(): + # # If the data doesn't come from a previous calculation we create it + # impurity_info, imp_pot_sfd = self.impurity_cluster_evaluation() + #else: + # impurity_info = self.inputs.imp_info + # imp_pot_sfd = self.inputs.imp_potential_node + impurity_info, imp_pot_sfd = self.impurity_cluster_evaluation() + # With this we make sure that the actual number of angles is the same as the number of embedded impurity + if 'initial_noco_angles' in self.inputs: + inital_noco_angles_aux = self.inputs.initial_noco_angles.clone() + for imp in impurity_info.get_dict()['Zimp']: + if imp == 0: + inital_noco_angles_aux.get_dict()['phi'].append(0.0) + inital_noco_angles_aux.get_dict()['theta'].append(0.0) + inital_noco_angles_aux.get_dict()['fix_dir'].append(1) + # impurity info for the workflow builder.impurity_info = impurity_info builder.imp_pot_sfd = imp_pot_sfd @@ -500,12 +563,13 @@ def STM_lmdos_run(self): # print report message = f"""INFO: running DOS step for an STM measurement (pk: {calc.pk}) at position (ilayer: {self.inputs.tip_position['ilayer']})""" - if 'params_kkr_overwrite' in self.inputs.BdG: - if self.inputs.BdG.params_kkr_overwrite: + if 'params_overwrite' in self.inputs.BdG: + if self.inputs.BdG.params_overwrite: message += f'\nINFO: runnig DOS step (pk: {calc.pk}) BdG is present' self.report(message) # Save the calculated impurity cluster and impurity info in the context + self.ctx.impurity_info = impurity_info self.ctx.imp_pot_sfd = imp_pot_sfd diff --git a/aiida_kkr/workflows/kkr_imp_dos.py b/aiida_kkr/workflows/kkr_imp_dos.py index 2ddf23cc..93bcfdef 100644 --- a/aiida_kkr/workflows/kkr_imp_dos.py +++ b/aiida_kkr/workflows/kkr_imp_dos.py @@ -10,6 +10,7 @@ from aiida.engine import if_, ToContext, WorkChain, calcfunction from aiida.common import LinkType from aiida.common.folders import SandboxFolder +from aiida_kkr.tools import truncate_string from aiida_kkr.workflows.gf_writeout import kkr_flex_wc from aiida_kkr.workflows.kkr_imp_sub import kkr_imp_sub_wc from aiida_kkr.workflows.dos import kkr_dos_wc @@ -508,7 +509,8 @@ def run_imp_dos(self): ) builder = kkr_imp_sub_wc.get_builder() - builder.metadata.label = label_imp # pylint: disable=no-member + # Attention: label and description must not be loo long (255 characters max)! + builder.metadata.label = truncate_string(label_imp, 255) # pylint: disable=no-member builder.metadata.description = description_imp # pylint: disable=no-member builder.kkrimp = kkrimpcode builder.options = options @@ -735,9 +737,10 @@ def parse_impdosfiles(folder, natom, nspin, ef, use_lmdos): name0 = 'out_ldos' if use_lmdos.value: name0 = 'out_lmdos' - try: + fname = name0 + '.atom=%0.2i_spin%i.dat' % (iatom, ispin) + if fname in folder.list_object_names(): # old file names with 2 digits for atom index - with folder.open(name0 + '.atom=%0.2i_spin%i.dat' % (iatom, ispin)) as dosfile: + with folder.open(fname) as dosfile: tmp = loadtxt(dosfile) dos.append(tmp) @@ -745,8 +748,7 @@ def parse_impdosfiles(folder, natom, nspin, ef, use_lmdos): tmp = loadtxt(dosfile) if len(tmp) > 0: dos_int.append(tmp) - - except: + else: # new file names with 3 digits for atom numbers with folder.open(name0 + '.atom=%0.3i_spin%i.dat' % (iatom, ispin)) as dosfile: tmp = loadtxt(dosfile) @@ -756,7 +758,7 @@ def parse_impdosfiles(folder, natom, nspin, ef, use_lmdos): try: tmp = loadtxt(dosfile) except: - pass + tmp = [] # can happen if there are too few points to interpolate on if len(tmp) > 0: dos_int.append(tmp) @@ -804,7 +806,7 @@ def parse_impdosfiles(folder, natom, nspin, ef, use_lmdos): dosnode.set_y(ylists[0], ylists[1], ylists[2]) # node for interpolated DOS - if len(dos_int) > 0: + try: dosnode2 = XyData() dosnode2.label = 'dos_interpol_data' dosnode2.description = 'Array data containing iterpolated DOS (i.e. dos at finite imaginary part of energy). 3D array with (atoms, energy point, l-channel) dimensions.' @@ -817,7 +819,7 @@ def parse_impdosfiles(folder, natom, nspin, ef, use_lmdos): dosnode2.set_y(ylists[0], ylists[1], ylists[2]) output = {'dos_data': dosnode, 'dos_data_interpol': dosnode2} - else: + except: output = {'dos_data': dosnode} return output diff --git a/aiida_kkr/workflows/kkr_imp_sub.py b/aiida_kkr/workflows/kkr_imp_sub.py index a13bdea1..29cb8548 100644 --- a/aiida_kkr/workflows/kkr_imp_sub.py +++ b/aiida_kkr/workflows/kkr_imp_sub.py @@ -920,25 +920,32 @@ def inspect_kkrimp(self): self.report(message) if self.ctx.kkrimp_step_success and found_last_calc_output: - # check convergence - self.ctx.kkr_converged = last_calc_output['convergence_group']['calculation_converged'] - # check rms - self.ctx.rms.append(last_calc_output['convergence_group']['rms']) - rms_all_iter_last_calc = list(last_calc_output['convergence_group']['rms_all_iterations']) - # check rms of LDAU pot (if LDAU is set) - try: - rms_LDAU = last_calc_output['convergence_group']['rms_LDAU'] - self.ctx.rms_LDAU = rms_LDAU - if rms_LDAU != 0.0: - rms_LDAU_all_iter_last_calc = list(last_calc_output['convergence_group']['rms_LDAU_all_iterations']) - self.ctx.last_rms_LDAU_all = rms_LDAU_all_iter_last_calc - except: - pass + # The convergence group is not created if the impurity calculation is a dos calculation - # add lists of last iterations - self.ctx.last_rms_all = rms_all_iter_last_calc - if self.ctx.kkrimp_step_success and self.convergence_on_track(): - self.ctx.rms_all_steps += rms_all_iter_last_calc + if 'doscalc' in last_calc_output['convergence_group']: + self.ctx.kkr_converged = True + else: + # check convergence + self.ctx.kkr_converged = last_calc_output['convergence_group']['calculation_converged'] + # check rms + self.ctx.rms.append(last_calc_output['convergence_group']['rms']) + rms_all_iter_last_calc = list(last_calc_output['convergence_group']['rms_all_iterations']) + # check rms of LDAU pot (if LDAU is set) + try: + rms_LDAU = last_calc_output['convergence_group']['rms_LDAU'] + self.ctx.rms_LDAU = rms_LDAU + if rms_LDAU != 0.0: + rms_LDAU_all_iter_last_calc = list( + last_calc_output['convergence_group']['rms_LDAU_all_iterations'] + ) + self.ctx.last_rms_LDAU_all = rms_LDAU_all_iter_last_calc + except: + pass + + # add lists of last iterations + self.ctx.last_rms_all = rms_all_iter_last_calc + if self.ctx.kkrimp_step_success and self.convergence_on_track(): + self.ctx.rms_all_steps += rms_all_iter_last_calc else: self.ctx.kkr_converged = False @@ -1149,25 +1156,32 @@ def return_results(self): # report the status if self.ctx.successful: - self.report( - 'STATUS: Done, the convergence criteria are reached.\n' - 'INFO: The charge density of the KKR calculation pk= {} ' - 'converged after {} KKR runs and {} iterations to {} \n' - ''.format( - last_calc_pk, self.ctx.loop_count - 1, sum(self.ctx.KKR_steps_stats.get('isteps', [])), - self.ctx.last_rms_all[-1] + try: + self.report( + 'STATUS: Done, the convergence criteria are reached.\n' + 'INFO: The charge density of the KKR calculation pk= {} ' + 'converged after {} KKR runs and {} iterations to {} \n' + ''.format( + last_calc_pk, self.ctx.loop_count - 1, sum(self.ctx.KKR_steps_stats.get('isteps', [])), + self.ctx.last_rms_all[-1] + ) ) - ) + except: + self.report('A DOS calculation has been performed, the convergence data are superflous') else: # Termination ok, but not converged yet... - self.report( - 'STATUS/WARNING: Done, the maximum number of runs ' - 'was reached or something failed.\n INFO: The ' - 'charge density of the KKR calculation pk= ' - 'after {} KKR runs and {} iterations is {} "me/bohr^3"\n' - ''.format( - self.ctx.loop_count - 1, sum(self.ctx.KKR_steps_stats.get('isteps', [])), self.ctx.last_rms_all[-1] + try: + self.report( + 'STATUS/WARNING: Done, the maximum number of runs ' + 'was reached or something failed.\n INFO: The ' + 'charge density of the KKR calculation pk= ' + 'after {} KKR runs and {} iterations is {} "me/bohr^3"\n' + ''.format( + self.ctx.loop_count - 1, sum(self.ctx.KKR_steps_stats.get('isteps', [])), + self.ctx.last_rms_all[-1] + ) ) - ) + except: + self.report('A DOS calculation has been performed, the convergence data are superflous') # create results node and link all calculations message = 'INFO: create results nodes' diff --git a/aiida_kkr/workflows/kkr_scf.py b/aiida_kkr/workflows/kkr_scf.py index 3c780487..1ff5a150 100644 --- a/aiida_kkr/workflows/kkr_scf.py +++ b/aiida_kkr/workflows/kkr_scf.py @@ -162,7 +162,6 @@ class kkr_scf_wc(WorkChain): _options_default.custom_scheduler_commands = '' # some additional scheduler commands # intended to guide user interactively in setting up a valid wf_params node - @classmethod def get_wf_defaults(cls, silent=False): """Print and return _wf_default dictionary. diff --git a/examples/AiiDA-KKR_STM_Tutorial.ipynb b/examples/AiiDA-KKR_STM_Tutorial.ipynb new file mode 100644 index 00000000..2e1b60bc --- /dev/null +++ b/examples/AiiDA-KKR_STM_Tutorial.ipynb @@ -0,0 +1,503 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8c29ad25-99b7-4a05-a799-5b310b2a146e", + "metadata": {}, + "source": [ + "# Tutorial on the STM workflow" + ] + }, + { + "cell_type": "markdown", + "id": "50a57fb4-e027-45c6-a2de-106f2517c962", + "metadata": { + "tags": [] + }, + "source": [ + "### In this tutorial we present the characteristics of the STM workflow. The aim is to present the characteristics of this workflow and the various tools that accompany it" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "91c65cfa-9c82-4b50-87d1-abf0ff536d30", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from aiida import load_profile, orm, engine\n", + "import numpy as np\n", + "profile = load_profile()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d8b325c9-c7fe-4aab-93a9-ded851526d8b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def load_or_submit(builder, uuid=None):\n", + " if uuid is not None:\n", + " return load_node(uuid)\n", + " calc = engine.submit(builder)\n", + " print('submitted', calc)\n", + " return calc" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "9c23bc0d-1324-4659-af2a-6fc6ae3ea52d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from aiida_kkr.workflows import kkr_imp_sub_wc\n", + "\n", + "example_calc = orm.load_node('73e4ca38-38ed-4ec2-b11a-87b03e0f0635')\n", + "imp_scf = example_calc.inputs.imp_scf.startpot.get_incoming(node_class=kkr_imp_sub_wc).first().node.caller\n", + "last_imp_calc = orm.load_node(imp_scf.outputs.last_calc_info['last_calc_nodeinfo']['uuid'])\n", + "\n", + "# This must go to the wf inputs\n", + "imp_info = imp_scf.inputs.impurity_info\n", + "\n", + "# After this we retrieve the host calculation\n", + "host_calc = imp_scf.inputs.remote_data_host.get_incoming(node_class=orm.CalcJobNode).first().node\n", + "# In particular the remote folder, this contains the structural data of the system.\n", + "host_remote = host_calc.outputs.remote_folder\n", + "\n", + "imp_potential_node = imp_scf.outputs.converged_potential" + ] + }, + { + "cell_type": "markdown", + "id": "0f2f1325-bd2b-484f-97f1-7e309177f408", + "metadata": {}, + "source": [ + "### Now we show one of the tools of the workflow: The STM pathfinder, which is able to decide which sites are going to be scanned based on the symmetries of the system." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6d1f9f87-46e3-4f02-baf7-86bcfe5b2898", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from aiida_kkr.tools.tools_STM_scan import STM_pathfinder\n", + "\n", + "# The pathfinder is able to find the structural information of the system, and the matrix representation of the system's symmetries\n", + "struc_info, symm_matrices = STM_pathfinder(host_remote)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5d48ddae-95d4-47f3-b76c-b5aba9b64549", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAksAAAIdCAYAAAAtajnoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACuDklEQVR4nOydd3hUVf6435kJIQFCCCXU0LuAIAKCBXZBsa2iLioWELFjV1T2t4JlFdt+7Yq7KthYURdBMUCQAEGChBINgYABKQECAUIKhCRz557fHzGzmTQSuMPcOfm8zzNPzty55bz3c8/kzPnc4lBKKQRBEARBEIRKcQa6AoIgCIIgCHZGOkuCIAiCIAjVIJ0lQRAEQRCEapDOkiAIgiAIQjVIZ0kQBEEQBKEapLMkCIIgCIJQDdJZEgRBEARBqAbpLAmCIAiCIFSDdJYEQRAEQRCqQTpLgmABDoeDZ555JtDVqJQVK1bgcDhYsWJFoKtiGTo66chtt91Gx44dfabZsa3YsU6CvZDOkhD0zJ49G4fDwfr16097XQUFBTzzzDOV/hOOjY2tc1+oddE52KnuGK7LyLEsnA4hga6AINiJgoICnn32WQBGjBjh81lsbCzvvvtupV+4J06cICREv+ZUnbNgT6o7hu1AoNpKXWy/gnXIyJIgWEBYWJh82QpByfHjx8/o9mrSVuxYJ6FuI50loU5QXFzMtGnTGDhwIJGRkTRs2JALL7yQ5cuXe+fZtWsXLVq0AODZZ5/F4XB4z2W47bbbePfddwG80x0Oh3fZys552LdvH5MmTaJNmzbUr1+fTp06ce+991JcXOydJycnh4cffpiYmBjq169P165defnllzFN86ROHTt25MorryQuLo7+/fsTFhZG7969mTdvXo32yddff83AgQMJDw+nefPm3HLLLezbt8/7+cmcK2PBggVcccUVXucuXbrw/PPP4/F4fOYbMWIEffr0YcuWLfzpT3+iQYMGtG3blldeeaXCOvfu3cuYMWNo2LAh0dHRPPLIIxQVFdXIMT8/n4cffpiOHTtSv359oqOjufjii9m4cSMA06dPp169ehw6dKjCsnfddRdNmjShsLAQ+N/+XrFiBeeeey7h4eH07dvXm+6aN28effv2JSwsjIEDB5KcnOyzvttuu41GjRqxZ88errzySho1akTbtm29+3jTpk38+c9/pmHDhnTo0IE5c+ZUqNPJjpfqjuGyddixYweXX345ERER3HzzzbXaD1Uxf/58+vTpQ1hYGH369OHbb7+tdL7ybeWZZ57B4XCwZcsWbrrpJqKiorjgggu8n3/++efe47Rp06bceOONZGRkVFjv2rVrufzyy4mKiqJhw4b069ePN9980+td2/abnJzMZZddRuPGjWnUqBEjR47k559/9pmn9BSA1atX8+ijj9KiRQsaNmzINddcU2Ffrl+/ntGjR9O8eXPCw8Pp1KkTt99+e7X7VLAP0pUW6gR5eXl8+OGHjBs3jjvvvJP8/Hw++ugjRo8eTVJSEv3796dFixa8//773HvvvVxzzTVce+21APTr14/jx4+zf/9+li5dymeffXbS7e3fv5/BgweTk5PDXXfdRc+ePdm3bx/ffPMNBQUFhIaGUlBQwPDhw9m3bx9333037du3JzExkalTp5KZmckbb7xx0u2kp6dzww03cM899zBhwgRmzZrF2LFjWbx4MRdffHGVy82ePZuJEycyaNAgZsyYwcGDB3nzzTdZvXo1ycnJNGnShLvvvrtWzqXrbdSoEY8++iiNGjUiPj6eadOmkZeXx6uvvuoz79GjR7n00ku59tpruf766/nmm2948skn6du3L5dddhlQkh4ZOXIke/bs4cEHH6RNmzZ89tlnxMfH16g+99xzD9988w33338/vXv35siRI/z000+kpaVxzjnncOutt/Lcc88xd+5c7r//fu9yxcXFfPPNN1x33XWEhYV5p2/fvp2bbrqJu+++m1tuuYXXXnuNv/zlL8ycOZO//e1v3HfffQDMmDGD66+/nm3btuF0/u83qcfj4bLLLuOiiy7ilVde4YsvvuD++++nYcOG/L//9/+4+eabufbaa5k5cybjx49n6NChdOrUCaBGx0t1x3AphmEwevRoLrjgAl577TUaNGjA0KFDa7UfyhMXF8d1111H7969mTFjBkeOHGHixIm0a9euRnECGDt2LN26dePFF19EKQXACy+8wNNPP83111/PHXfcwaFDh3j77be56KKLvMcpwNKlS7nyyitp3bo1Dz30EK1atSItLY2FCxfy0EMP1fpY3rx5MxdeeCGNGzfmiSeeoF69enzwwQeMGDGClStXMmTIEJ/5H3jgAaKiopg+fTq7du3ijTfe4P7772fu3LkAZGVlcckll9CiRQueeuopmjRpwq5du2r8w0awAUoQgpxZs2YpQK1bt67KeQzDUEVFRT7Tjh49qlq2bKluv/1277RDhw4pQE2fPr3COiZPnqyqajLllxk/frxyOp2V1sk0TaWUUs8//7xq2LCh+u2333w+f+qpp5TL5VJ79uyp0kcppTp06KAA9d///tc7LTc3V7Vu3VoNGDDAO2358uUKUMuXL1dKKVVcXKyio6NVnz591IkTJ7zzLVy4UAFq2rRpNXKujIKCggrT7r77btWgQQNVWFjonTZ8+HAFqE8//dQ7raioSLVq1Updd9113mlvvPGGAtRXX33lnXb8+HHVtWtXH6eqiIyMVJMnT652nqFDh6ohQ4b4TJs3b16F9Zfu78TERO+0JUuWKECFh4er3bt3e6d/8MEHFZafMGGCAtSLL77onXb06FEVHh6uHA6H+vLLL73Tt27dWuGYqunxUt0xXFqHp5566pT3Q2X0799ftW7dWuXk5HinxcXFKUB16NDBZ97ydZs+fboC1Lhx43zm27Vrl3K5XOqFF17wmb5p0yYVEhLinW4YhurUqZPq0KGDOnr0qM+8pW1Nqdq13zFjxqjQ0FC1Y8cO77T9+/eriIgIddFFF3mnlX73jBo1ymdbjzzyiHK5XN798e233570O0qwN5KGE+oELpeL0NBQAEzTJDs7G8MwOPfcc70pGaswTZP58+fzl7/8hXPPPbfC56XD/19//TUXXnghUVFRHD582PsaNWoUHo+HhISEk26rTZs2XHPNNd73jRs3Zvz48SQnJ3PgwIFKl1m/fj1ZWVncd999PqMFV1xxBT179uSHH36orbKX8PBwbzk/P5/Dhw9z4YUXUlBQwNatW33mbdSoEbfccov3fWhoKIMHD+b333/3TouNjaV169b89a9/9U5r0KABd911V43q06RJE9auXcv+/furnGf8+PGsXbuWHTt2eKd98cUXxMTEMHz4cJ95e/fuzdChQ73vS0cY/vznP9O+ffsK08u6lHLHHXf41K9Hjx40bNiQ66+/3ju9R48eNGnSxGd5K46XUu69997T2g9lyczM5JdffmHChAlERkZ6p1988cX07t27xnW65557fN7PmzcP0zS5/vrrfXxbtWpFt27dvCn05ORkdu7cycMPP+wdaSrlZGnjyvB4PMTFxTFmzBg6d+7snd66dWtuuukmfvrpJ/Ly8nyWueuuu3y2deGFF+LxeNi9ezeAt14LFy7E7XbXuk5C4JHOklBn+OSTT+jXrx9hYWE0a9aMFi1a8MMPP5Cbm2vpdg4dOkReXh59+vSpdr709HQWL15MixYtfF6jRo0CSobuT0bXrl0r/EPo3r07UHL+SmWUfoH36NGjwmc9e/b0fn4qbN68mWuuuYbIyEgaN25MixYtvB2i8vu5Xbt2FeoeFRXF0aNHfepamWNlda+MV155hdTUVGJiYhg8eDDPPPNMhQ7MDTfcQP369fniiy+89Vy4cCE333xzhe2W7RAB3s5BTExMpdPLukDJicSl5xSVnbeyfREZGemzvBXHC0BISEil6bHa7IeylB4v3bp1q/BZTeMEeNONpaSnp6OUolu3bhWc09LSvL6lnbuTtbeacujQIQoKCiqte69evTBNs8I5U+WPi6ioKOB/8R8+fDjXXXcdzz77LM2bN+fqq69m1qxZNT73Tgg8cs6SUCf4/PPPue222xgzZgxTpkwhOjoal8vFjBkzfH5Jn0lM0+Tiiy/miSeeqPTz0k5PsJCTk8Pw4cNp3Lgxzz33HF26dCEsLIyNGzfy5JNPVjhp3eVyVboe9cf5KlZw/fXXc+GFF/Ltt98SFxfHq6++yssvv8y8efO850VFRUVx5ZVX8sUXXzBt2jS++eYbioqKfEa9TlbnmrqczvJWHS/169f3OY+qlNrsB39QdlQSSnwdDgeLFi2qdP80atTojNSrJpwsfg6Hg2+++Yaff/6Z77//niVLlnD77bfzz3/+k59//tlWLkLlSGdJqBN88803dO7cmXnz5vn8Sp4+fbrPfNX9gq7pkH6LFi1o3Lgxqamp1c7XpUsXjh075h0ZOBW2b9+OUsqnbr/99htAhTsnl9KhQwcAtm3bxp///Gefz7Zt2+b9HGqXxlixYgVHjhxh3rx5XHTRRd7pO3furPE6KqtrampqBcdt27bVeB2tW7fmvvvu47777iMrK4tzzjmHF154wdtZgpIU1NVXX826dev44osvGDBgAGedddYp19sf1PR4OZXUUymnsh9Kj5f09PQKn9UmTuXp0qULSik6depUbUewS5cuAKSmpla7b2rTfhs0aFBp3bdu3YrT6awwklhTzjvvPM477zxeeOEF5syZw80338yXX37pk5oV7Imk4YQ6Qekvv7K/1NeuXcuaNWt85mvQoAFQMkpSnoYNG1b5WVmcTidjxozh+++/r/Su4qV1uP7661mzZg1LliypME9OTg6GYVS7HSi56q7sJdp5eXl8+umn9O/fn1atWlW6zLnnnkt0dDQzZ870SQMsWrSItLQ0rrjiCu+0mjpD5fu4uLiY995776TLVsXll1/O/v37+eabb7zTCgoK+Ne//nXSZT0eT4XUX3R0NG3atKmQ/rjsssto3rw5L7/8MitXrjxjoym1oabHS3XH8Mk4lf3QunVr+vfvzyeffOKzv5cuXcqWLVtqXYdSrr32WlwuF88++2yFETqlFEeOHAHgnHPOoVOnTrzxxhsVnMsuV9Nj2eVycckll7BgwQKfVPbBgweZM2cOF1xwAY0bN66Vy9GjRys49O/fH0BScUGCjCwJ2vDxxx+zePHiCtMfeughrrzySubNm8c111zDFVdcwc6dO5k5cya9e/fm2LFj3nnDw8Pp3bs3c+fOpXv37jRt2pQ+ffrQp08fBg4cCMCDDz7I6NGjcblc3HjjjZXW5cUXXyQuLo7hw4dz11130atXLzIzM/n666/56aefaNKkCVOmTOG7777jyiuv5LbbbmPgwIEcP36cTZs28c0337Br1y6aN29erXP37t2ZNGkS69ato2XLlnz88cccPHiQWbNmVblMvXr1ePnll5k4cSLDhw9n3Lhx3lsHdOzYkUceecQ7b22chw0bRlRUFBMmTODBBx/E4XDw2WefnVZa7c477+Sdd95h/PjxbNiwgdatW/PZZ595OwTVkZ+fT7t27fjrX//K2WefTaNGjfjxxx9Zt24d//znPyvskxtvvJF33nkHl8vFuHHjTrnO/qKmx0t1x/DJONX9MGPGDK644gouuOACbr/9drKzs3n77bc566yzfNpXbejSpQv/+Mc/mDp1Krt27WLMmDFERESwc+dOvv32W+666y4ef/xxnE4n77//Pn/5y1/o378/EydOpHXr1mzdupXNmzd7O5e1OZb/8Y9/sHTpUi644ALuu+8+QkJC+OCDDygqKqr0XmAn45NPPuG9997jmmuuoUuXLuTn5/Pvf/+bxo0bc/nll5/S/hHOMGf+AjxBsJbSy3eremVkZCjTNNWLL76oOnTooOrXr68GDBigFi5cqCZMmFDh0ubExEQ1cOBAFRoa6nNJsWEY6oEHHlAtWrRQDofD5zJkKrlUe/fu3Wr8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7RQVFZGXl+fzAry9eo/HU2nZMAyfcundVisru1wuevTogcPhAMDtdnsvmywtK6UqlAGfsmmaPmXDMKotezwen7KVTqV1L+vn/GNYVQen8nEyTZOzzjoLp9OpjVNZj/J+OjiVL7scDs765BPvnYN1cCobJ5fLRe/evb3r0MGpfJxKz111uVzaOJX3UErRp08fHA6HNk4V/GbN8tbBaqeaoFVn6bLLLmPs2LH069eP0aNHExsbS05ODl999ZWl25kxYwaRkZHeV0xMDID3XKe0tDTS0tIASElJIT09HYDk5GR27twJQFJSEhkZGUDJuVOZmZkAJCQkcPjwYTweD0uWLCE7OxuAuLg4b6cvNjaWwsJCDMMgNjYWwzAoLCz0npyen59PXFwcADk5OcTHxwNw+PBhEhISAMjMzCQxMRGAjIwMkpKSANi5cyfJyclAyShdSkqKZU4A8fHx5OTk4PF4WLRoEbm5udo4VRan9evXU1RUpJVT2Tht3LiR48ePa+VUNk4e0yTp8ceJ279fG6eycfJ4PCQlJWnlVD5OBw8e9Lrq4lQ+TqtXryY5OZndu3dr4+QTp8OHSZ48md/z8ix3Wr16NTVCac65556rnnrqqUo/i4mJUa+//rrPtGnTpql+/fpVu87CwkKVm5vrfWVkZChAZWdnK6WUMgxDGYZRoex2u33KHo+nyrJhGGrz5s2quLhYKaVUcXGxMk3Tp2yaZoWyUsqn7PF4fMput7vasmEYPuXKPE7VqbTupX6pqanebengVD5OJ06cUFu2bFFut1sbp7Ie5f10cCpfNtavV1vGjVMnkpK0cSobJ8Mw1JYtW1RhYaE2TuXjVFxcrDZv3qwMw9DGqbxHYWGhSktLU8XFxdo4+fglJam0G29URevWWe505MgRBajc3FxVHVrflPLYsWO0b9+eZ555hgcffLDC5zfccAMFBQV8//333mnDhg2jX79+zJw5s8bbkZtSCoKmyE0pBSHwyE0preXxxx9n5cqV7Nq1i8TERK655hpcLhfjxo0DYPz48UydOtU7/0MPPcTixYv55z//ydatW3nmmWdYv349999/f6AUvBiGwbp162qcTw02dPcD/R119wMwTJN1U6ZgaPo09zoRQ3EMeuzQDrXqLO3du5dx48bRo0cPrr/+epo1a8bPP/9MixYtANizZ483Twklo0hz5szhX//6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QEaWBEGwBYcOHaJVq1a8+OKL3mmJiYmEhoaybNmyKpdLSkpiwIABhIWFce6555KcnOzzeflU2OzZs2nSpAnfffcdvXv3pn79+uzZs4eioiIef/xx2rZtS8OGDRkyZAgrVqzwWdfq1asZMWIEDRo0ICoqitGjR3P06FFuu+02Vq5cyZtvvonD4cDhcLBr165K03D//e9/Oeuss6hfvz4dO3bkn//8p882OnbsyIsvvsjtt99OREQE7du351//+tep7VRBECxBOkuCINiCFi1a8PHHH/PMM8+wfv168vPzufXWW7n//vsZOXJkpcscO3aMK6+8kt69e7NhwwaeeeYZHn/88ZNuq6CggJdffpkPP/yQzZs3Ex0dzf3338+aNWv48ssvSUlJYezYsVx66aWkp6cD8MsvvzBy5Eh69+7NmjVr+Omnn/jLX/6Cx+PhzTffZOjQodx5551kZmaSmZlJTExMhe1u2LCB66+/nhtvvJFNmzbxzDPP8PTTTzN79myf+f75z396O3733Xcf9957L9u2bav9ThUEwRosfSyvIAjCaXLfffep7t27q5tuukn17dtXFRYWVjnvBx98oJo1a6ZOnDjhnfb+++8rQCUnJyul/veU86NHjyqllJo1a5YC1C+//OJdZvfu3crlcql9+/b5rH/kyJFq6tSpSimlxo0bp84///wq6zJ8+HD10EMP+Uwrv+2bbrpJXXzxxT7zTJkyRfXu3dv7vkOHDuqWW27xvjdNU0VHR6v333+/ym0LguBfZGRJEARb8dprr2EYBl9//TVffPEF9evXr3LetLQ0+vXrR1hYmHfa0KFDT7qN0NBQ+vXr532/adMmPB4P3bt3p1GjRt7XypUr2bFjB/C/kaXTIS0tjfPPP99n2vnnn096ejoej8c7rWzdHA4HrVq1Iisr67S2LQjCqSMneAuCYCt27NjB/v37MU2TXbt20bdvX8u3ER4ejsPh8L4/duwYLpeLDRs24HK5fOZt1KiRd5kzRb169XzeOxwOTNM8Y9sXBMEXGVkSBME2FBcXc8stt3DDDTfw/PPPc8cdd1Q7otKrVy9SUlIoLCz0Tvv5559rvd0BAwbg8XjIysqia9euPq/Sq9n69etX7YnmoaGhPqNDVdV39erVPtNWr15N9+7dK3TSBEGwD9JZEgTBNvy///f/yM3N5a233uLJJ5+ke/fu3H777VXOf9NNN+FwOLjzzjvZsmULsbGxvPbaa7Xebvfu3bn55psZP3488+bNY+fOnSQlJTFjxgx++OEHAKZOncq6deu47777SElJYevWrbz//vscPnwYKLmKbe3atezatYvDhw9XOhL02GOPsWzZMp5//nl+++03PvnkE955550anZQuCELgkM6SIAi2YMWKFbzxxht89tlnNG7cGKfTyWeffcaqVat4//33K12mUaNGfP/992zatIkBAwbw//7f/+Pll18+pe3PmjWL8ePH89hjj9GjRw/GjBnDunXraN++PVDSoYqLi+PXX39l8ODBDB06lAULFhASUnI2w+OPP47L5aJ37960aNGCPXv2VNjGOeecw1dffcWXX35Jnz59mDZtGs899xy33XbbKdVZEIQzg0MppQJdCUEQBEEQBLsiI0uCIAiCIAjVIJ0lQRAEQRCEapDOkiAIgiAIQjVIZ0kQBEEQBKEapLMkCIIgCIJQDdJZEgRBEARBqAbpLAmCIAiCIFSDdJYEQRAEQRCqQTpLgiAIgiAI1SCdJUEQBEEQhGqQzpIgCIIgCEI1SGdJEARBEAShGqSzJAiCIAiCUA3SWRIEQRAEQagG6SwJgiAIgiBUg3SWBEEQBEEQqkE6S4IgCIIgCNUgnSVBEARBEIRqkM6SIAiCIAhCNUhnSRAEQRAEoRpCAl0BQRDA4/HgdrsDXQ1B0J569erhcrkCXQ0hyJDOkiAEEKUUBw4cICcnJ9BVEYQ6Q5MmTWjVqhUOhyPQVRGCBOksCUIAKe0oRUdH06BBA/nyFgQ/opSioKCArKwsAFq3bh3gGgnBgnSWBCFAeDweb0epWbNmga6OINQJwsPDAcjKyiI6OlpSckKNkBO8BSFAlJ6j1KBBgwDXRBDqFqVtTs4TFGqKdJYEIcBI6k0QzizS5oTaIp0lQRAEQRCEapDOkiAIgh945pln6N+/f8DXIQjC6SOdJUEQas2IESN4+OGHK0yfPXs2TZo0OeP1CVa+/fZbzjvvPCIjI4mIiOCss87y2a+PP/44y5Yt876/7bbbGDNmzJmvqCDUceRqOEEIcrbvPcTfP1yEMpV3msPp4B93XEbXdi0CWDOhOpYtW8YNN9zACy+8wFVXXYXD4WDLli0sXbrUO0+jRo1o1KhRAGspCALIyJIgBDVKKV6Zs5zf9x9hR5nX7/uP8Mqc5SilTr4SP1I6EvLaa6/RunVrmjVrxuTJk32uQnrvvffo1q0bYWFhtGzZkr/+9a/ez0zT5JVXXqFr167Ur1+f9u3b88ILL3g/f/LJJ+nevTsNGjSgc+fOPP300z7rLk1jffbZZ3Ts2JHIyEhuvPFG8vPzvfOMGDGCBx98kCeeeIKmTZvSqlUrnnnmGR+PnJwc7rjjDlq0aEHjxo3585//zK+//uozz0svvUTLli2JiIhg0qRJFBYWVrtvvv/+e84//3ymTJlCjx496N69O2PGjOHdd9+tUP/S8ieffMKCBQtwOBw4HA5WrFgBQEZGBtdffz1NmjShadOmXH311ezatcu7nhUrVjB48GAaNmxIkyZNOP/889m9e3e19RME4X9IZ0kQgpgVydvZ+NteTNO3U2Saio2/7WXlLzsCVLP/sXz5cnbs2MHy5cv55JNPmD17NrNnzwZg/fr1PPjggzz33HNs27aNxYsXc9FFF3mXnTp1Ki+99BJPP/00W7ZsYc6cObRs2dL7eUREBLNnz2bLli28+eab/Pvf/+b111/32f6OHTuYP38+CxcuZOHChaxcuZKXXnrJZ55PPvmEhg0bsnbtWl555RWee+45nxGesWPHkpWVxaJFi9iwYQPnnHMOI0eOJDs7G4CvvvqKZ555hhdffJH169fTunVr3nvvvWr3S6tWrdi8eTOpqak12o+PP/44119/PZdeeimZmZlkZmYybNgw3G43o0ePJiIiglWrVrF69WoaNWrEpZdeSnFxMYZhMGbMGIYPH05KSgpr1qzhrrvukivCBKEWSBpOEGzGrc9/wZG84yedTynFkdyCaud54r3vaRZZszuDN2vckM+evrnG9awpUVFRvPPOO7hcLnr27MkVV1zBsmXLuPPOO9mzZw8NGzbkyiuvJCIigg4dOjBgwAAA8vPzefPNN3nnnXeYMGECAF26dOGCCy7wrvvvf/+7t9yxY0cef/xxvvzyS5544gnvdNM0mT17NhEREQDceuutLFu2zGeEql+/fkyfPh2Abt268c4777Bs2TIuvvhifvrpJ5KSksjKyqJ+/foAvPbaa8yfP59vvvmGu+66izfeeINJkyYxadIkAP7xj3/w448/Vju69MADD7Bq1Sr69u1Lhw4dOO+887jkkku4+eabvdspS6NGjQgPD6eoqIhWrVp5p3/++eeYpsmHH37o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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from aiida_kkr.tools.tools_STM_scan import lattice_plot\n", + "\n", + "# We now use the lattice plot tool to have a clear representation of the scanning sites.\n", + "\n", + "symm_matrices = [np.array([[1., 0.],\n", + " [0., 1.]]),\n", + " np.array([[-1., 0.],\n", + " [ 0., -1.]]),\n", + " np.array([[ 1., 0.],\n", + " [ 0., -1.]]),\n", + " np.array([[-1., 0.],\n", + " [ 0., 1.]])]\n", + "\n", + "lattice_length_x = 10 # In Angstrom\n", + "lattice_length_y = 10 \n", + "\n", + "\n", + "# Save the positions to be scanned into an array, this can be used to manually submit the calculation\n", + "points = lattice_plot(struc_info['plane_vectors'], False, symm_matrices, lattice_length_x, lattice_length_y)" + ] + }, + { + "cell_type": "markdown", + "id": "99df2344-ad4a-4f74-8d07-3560c7dca2da", + "metadata": {}, + "source": [ + "## Manual tool for the retrival and use of the positions " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "3c7d4dcb-1cf0-4239-bd4d-b9c3df988258", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from aiida_kkr.tools.tools_STM_scan import lattice_generation, find_linear_combination_coefficients\n", + "\n", + "functioning_scanning = []\n", + "for element in points:\n", + " for pos in element:\n", + " functioning_scanning.append(pos)\n", + " \n", + "#eliminate, scan = lattice_generation(30, 30, mat, 0, 0, [[3.31, 0.0], [1.655, 2.3405234457275]])\n", + "\n", + "coeff = find_linear_combination_coefficients([[3.31, 0.0], [1.655, 2.3405234457275]], functioning_scanning)\n", + "\n", + "# Sometimes the number of point that need to be scanned is to big to be handled. In such cases it is advisiable to split the calculations\n", + "# in multiple sections, each containing a stripe of the region that need to be scanned, and then submit them with the Group submission\n", + "\n", + "#split_arr = np.array_split(coeff, 20)" + ] + }, + { + "cell_type": "markdown", + "id": "ddf86157-731b-4eb7-b32e-1b759713743b", + "metadata": {}, + "source": [ + "### Once we have a clearer idea of the positions we will investigate we can use the STM workflow itself" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "595d3b92-9d61-451c-89b1-7bf29aca6524", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "submitted uuid: 0a8f56ed-0d50-47c9-8322-337462fba208 (pk: 218454) (aiida_kkr.workflows.kkr_STM.kkr_STM_wc)\n" + ] + } + ], + "source": [ + "from aiida_kkr.workflows import kkr_STM_wc\n", + "\n", + "builder = kkr_STM_wc.get_builder()\n", + "\n", + "# The impurity info is a dictionary containing the impurity type, it's position and cluster radius\n", + "builder.imp_info = imp_info\n", + "\n", + "# Host remote is the RemoteData containing information about the structure of the host system\n", + "builder.host_remote = host_remote\n", + "\n", + "# The impurity potential node contains a file where all the information about the potential generated by an impurity.\n", + "builder.imp_potential_node = imp_potential_node\n", + "\n", + "# The tip position is where we want to scan with the STM. \n", + "# ilayer is the z position of the tip\n", + "# nx and ny are the number of sites to be scanned in the unit of the in-plane Bravais lattice (da and db respectively)\n", + "\n", + "# NOTE: The automatic submission is broken at this moment since the ASE tool doesn't return the correct rotation matrix\n", + "# Instead use the parameter \"scan positions\"\n", + "builder.tip_position = orm.Dict({'ilayer': 0, 'nx': 10, 'ny': 10, 'scan_positions':coeff})\n", + "\n", + "# This paramters controls the cluster radius on the KKR level, sometimes it must be increased in order to allow for\n", + "# the calculation of the impurity cluster\n", + "builder.gf_writeout.params_kkr_overwrite = orm.Dict(dict={'NSHELD': 1000})\n", + "\n", + "#builder.remote_data = remote_data_folder\n", + "#builder.kkrflex_files = kkrflex_files_folder\n", + "#builder = kkr_imp_dos_wc.get_builder()\n", + "\n", + "builder.wf_parameters = orm.Dict(dict={\n", + " 'jij_run': False,\n", + " 'lmdos': True,\n", + " 'retrieve_kkrflex': True,\n", + " 'dos_params': { \n", + " 'tempr': 210.0,\n", + " 'kmesh': [100, 100, 1],\n", + " 'RCLUSTZ': None},\n", + " 'nepts': 7, # These last three parameters are set to some default value if not specified\n", + " 'emin': 0-0.0005, # These are the default parameters, change emin and emax in order to specifiy \n", + " 'emax': 0+0.0005, # which energy region to scan, and adjust the nepts to select how many poits to use \n", + "})\n", + "\n", + "options = orm.Dict(dict={\n", + " 'max_wallclock_seconds': 4*3600, # max runtine\n", + " 'resources': {'num_machines': 1 , 'num_mpiprocs_per_machine': 7},\n", + " 'queue_name': 'c23ms',\n", + " 'custom_scheduler_commands': '#SBATCH --account=p0021406',\n", + " 'withmpi': True\n", + "})\n", + "builder.options = options\n", + "\n", + "#builder.kkr = orm.Code.get_from_string('KKRhost@JURECA-DC-STAGE2024')\n", + "#builder.kkrimp = orm.Code.get_from_string('KKRimp@JURECA-DC-STAGE2024')\n", + "\n", + "#builder.kkr = Code.get_from_string('kkrhost_BdG_AMD@iffslurm')\n", + "#builder.kkrimp = Code.get_from_string('KKRIMP_BdG@iffslurm')\n", + "\n", + "builder.kkr = orm.Code.get_from_string('kkrhost@claix18aiida')\n", + "builder.kkrimp = orm.Code.get_from_string('kkrimp@claix18aiida')\n", + "\n", + "STM_example = load_or_submit(builder, '0a8f56ed-0d50-47c9-8322-337462fba208')" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "880506ca-7fa5-43eb-834b-894147bbdc34", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "17310.25s - pydevd: Sending message related to process being replaced timed-out after 5 seconds\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[22mkkr_STM_wc<218454> Finished [0] [2:results]\n", + " └── kkr_imp_dos_wc<218459> Finished [0] [2:return_results]\n", + " ├── kkr_flex_wc<218462> Finished [0] [2:return_results]\n", + " │ ├── update_params_wf<218464> Finished [0]\n", + " │ ├── KkrCalculation<218467> Finished [0]\n", + " │ └── create_out_dict_node<218472> Finished [0]\n", + " ├── kkr_imp_sub_wc<218475> Finished [0] [2:error_handler]\n", + " │ ├── kick_out_corestates_wf<218478> Finished [0]\n", + " │ ├── KkrimpCalculation<218480> Finished [0]\n", + " │ ├── extract_imp_pot_sfd<218484> Finished [0]\n", + " │ └── create_out_dict_node<218487> Finished [0]\n", + " ├── parse_impdosfiles<218493> Finished [0]\n", + " ├── parse_impdosfiles<218500> Finished [0]\n", + " └── create_out_dict_node<218504> Finished [0]\u001b[0m\n" + ] + } + ], + "source": [ + "!verdi process status 218454" + ] + }, + { + "cell_type": "markdown", + "id": "61fb0149-acf9-47a4-9a1a-ef4ad3c0649a", + "metadata": {}, + "source": [ + "# Some tool for the plotting of the data " + ] + }, + { + "cell_type": "markdown", + "id": "b5c5366b-b44b-4232-942f-ed2fd52a72a7", + "metadata": {}, + "source": [ + "## This parser uses is able to get the calculation both from single node and groups. But it assumes a 2 spin channel " + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "18aecb43-3bf7-408f-b616-c8d0cdbf249b", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.3409569263458252\n" + ] + } + ], + "source": [ + "def STM_real_space_parser(STM_calc, energy_pos, N0):\n", + " \n", + " \"\"\" \n", + " STM_calc : Single calculcation node or group with calculations\n", + " energy_pos : energy that wants to be analysed\n", + " N0 : Number of atoms in the original impurity cluster, these will not be considered\n", + " \"\"\"\n", + " \n", + " from masci_tools.util.constants import BOHR_A\n", + " from time import time\n", + " \n", + " alat_ang = (6.267994199139 * BOHR_A)\n", + " \n", + " #plt.figure(figsize=(10,10))\n", + " t0 = time()\n", + " all_pos = [[], []]\n", + " all_dat_summed_rho = []\n", + " all_dat_summed_mu = []\n", + " \n", + " # grouping of node if a list of nodes is the input instead of a single node\n", + " groupmode = False\n", + " if type(STM_calc) == list:\n", + " if len(STM_calc) > 1:\n", + " groupmode = True\n", + " else:\n", + " STM_calc = STM_calc[0]\n", + " \n", + " if groupmode:\n", + " \n", + " for node in tqdm(STM_calc.nodes):\n", + " \n", + " with node.called[0].called[0].called[1].outputs.retrieved.open(\"kkrflex_atominfo\") as _f:\n", + " pos = np.loadtxt(_f, skiprows=3) * alat_ang\n", + " \n", + " try:\n", + " dat = node.outputs.STM_dos_data_lmdos.get_y()[0][1]\n", + " except:\n", + " print('No STM data found!')\n", + " \n", + " for i in [-1,1]:\n", + " for j in [-1,1]:\n", + " \n", + " all_pos[0] += list(i*pos[N0:,0])\n", + " all_pos[1] += list(j*pos[N0:,1])\n", + " all_dat_summed_rho += list(abs((dat[::2, energy_pos]+dat[1::2, energy_pos])[N0:])) #Both spin channels are considered here\n", + " all_dat_summed_mu += list(abs((dat[::2, energy_pos]-dat[1::2, energy_pos])[N0:]))\n", + " \n", + " else:\n", + " \n", + " with STM_calc.called[0].called[0].called[1].outputs.retrieved.open(\"kkrflex_atominfo\") as _f:\n", + " pos = np.loadtxt(_f, skiprows=3) * alat_ang\n", + " \n", + " try:\n", + " dat = STM_calc.outputs.STM_dos_data_lmdos.get_y()[0][1]\n", + " except:\n", + " print('No STM data found!')\n", + " \n", + " for i in [-1,1]:\n", + " for j in [-1,1]:\n", + " \n", + " all_pos[0] += list(i*pos[N0:,0])\n", + " all_pos[1] += list(j*pos[N0:,1])\n", + " all_dat_summed_rho += list(abs((dat[::2, energy_pos]+dat[1::2, energy_pos])[N0:])) #Both spin channels are considered here\n", + " all_dat_summed_mu += list(abs((dat[::2, energy_pos]-dat[1::2, energy_pos])[N0:]))\n", + " \n", + " \n", + " print(time()-t0)\n", + " \n", + " all_pos = np.array(all_pos)\n", + " all_dat_summed_rho = np.array(all_dat_summed_rho)\n", + " all_dat_summed_mu= np.array(all_dat_summed_mu)\n", + " \n", + " return all_pos, all_dat_summed_rho, all_dat_summed_mu\n", + "\n", + "pos, rho, mu = STM_real_space_parser(STM_example, 9, 15)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "90031b51-6607-4b1f-998a-977dd614239b", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA00AAANaCAYAAABV0KcPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB8f0lEQVR4nO3deZwT9f3H8XeS3ex9sDfHcqogIqAoFFSQiiJSq7XVSqnl8KxYK3ihrRwq4NGqraVFf1bRttajVluPooBYPBAv1gOBogW5F1jYzd5HMr8/pgm77O7sLiSTZPN6Ph55QJLJ5Lszycz38/18Ml+HYRiGAAAAAAAtcoa7AQAAAAAQyQiaAAAAAMACQRMAAAAAWCBoAgAAAAALBE0AAAAAYIGgCQAAAAAsEDQBAAAAgAWCJgAAAACwQNAEAAAAABYImgAExVtvvSWHw6G33noraOucN2+eHA7HEb126tSp6t27d9DaEk5Lly6Vw+HQ1q1bA4+deeaZOvPMMwP3t27dKofDoaVLlwb1vXv37q2pU6cGdZ3hsGzZMg0dOlSJiYlyOBwqLS0Nd5MAAFGEoAlAgL9z3tJt9uzZ4W7eUQtFUNEZvPfee5o3b16nDSRKSkp0ySWXKCkpSYsXL9af/vQnpaSkNFtuwoQJ6tKli4qLi5s9V1ZWpq5du2rEiBHy+XySzEB12rRp6tevnxITE1VQUKDRo0dr7ty5TV575plnNvkuJSUlafDgwXrooYcC6zpau3bt0rx581RUVHTE63jttdc0b968oLQHADqbuHA3AEDkufPOO9WnT58mjw0aNMjyNaNHj1Z1dbXcbncomxaTLrvsMl166aVKSEgIyfrfe+89zZ8/X1OnTlVmZmaT5zZt2iSnM7rH1z788EOVl5frrrvu0rhx41pd7ve//70GDRqkmTNn6umnn27y3O233679+/dr2bJlcjqd+uqrr3TqqacqKSlJ06dPV+/evbV792598sknuvfeezV//vwmr+/Ro4cWLVokSdq/f7+efvppzZw5U/v27dOCBQuO+m/ctWuX5s+fr969e2vo0KFHtI7XXntNixcvJnACgBYQNAFoZsKECTrllFPatWxNTY3cbrecTqcSExND3LLY5HK55HK5wvLeoQrU7LR3715JahYQHq5Pnz6aO3eubr31Vk2dOlXnnHOOJDPoWrJkiW666SYNGTJEkvTggw+qoqJCRUVF6tWrV4vv11hGRoZ+/OMfB+5fc801GjBggB5++GHdeeedYdu/AID2ie7hQwC28v9u6ZlnntEvf/lLde/eXcnJyfJ4PK3+pmnt2rU699xzlZGRoeTkZI0ZM0bvvvtus3W/8847OvXUU5WYmKh+/frpkUceabUdf/7znzVs2DAlJSUpKytLl156qbZv397hv6e8vFw33HCDevfurYSEBOXl5enss8/WJ5980uZr161bpwkTJig9PV2pqak666yz9P777zdZpr6+XvPnz9exxx6rxMREZWdn6/TTT9fy5cubLLdx40Zdcsklys3NVVJSkvr3769f/OIXgedb+k1Te3z22WeaOnWq+vbtGygfmz59ukpKSgLLzJs3TzfffLMkM2jwl5D536ul3zT997//1cUXX6ysrCwlJyfrW9/6ll599dUmy/g/D88995wWLFigHj16KDExUWeddZa++uqrJstu3rxZ3//+91VQUKDExET16NFDl156qcrKytr8G59//vnAZyEnJ0c//vGPtXPnzsDzZ555pqZMmSJJOvXUU+VwOCx/ozVr1iwNHjxY1157rWpqauT1enXNNdeoV69eTcruvv76a/Xo0aNZwCRJeXl5bbY7MTFRp556qsrLy1sMsg63fPlynX766crMzFRqaqr69++v22+/XZK5rU899VRJ0rRp0wL70F+K+vbbb+viiy9Wz549lZCQoMLCQs2cOVPV1dWB9U+dOlWLFy+WpCalhH4+n08PPfSQTjjhBCUmJio/P19XX321Dh482KSdH330kcaPH6+cnBwlJSWpT58+mj59ept/HwBEOjJNAJopKyvT/v37mzyWk5MT+P9dd90lt9utm266SbW1ta2W5L355puaMGGChg0bprlz58rpdOqJJ57Qt7/9bb399tsaPny4JOnzzz/XOeeco9zcXM2bN08NDQ2aO3eu8vPzm61zwYIFuuOOO3TJJZfoiiuu0L59+/Twww9r9OjRWrduXZvZhMauueYa/e1vf9N1112ngQMHqqSkRO+88442bNigk08+udXXrV+/XmeccYbS09N1yy23KD4+Xo888ojOPPNM/fvf/9aIESMkmQHJokWLdMUVV2j48OHyeDz66KOP9Mknn+jss8+WZAY2Z5xxhuLj43XVVVepd+/e+vrrr/Xyyy8fddnW8uXL9d///lfTpk1TQUGB1q9fr0cffVTr16/X+++/L4fDoYsuukj/+c9/9Ne//lUPPvhgYD/n5ua2uM7i4mKNGjVKVVVVuv7665Wdna0nn3xS3/3ud/W3v/1N3/ve95osf88998jpdOqmm25SWVmZ7rvvPk2ePFlr166VJNXV1Wn8+PGqra3Vz372MxUUFGjnzp165ZVXVFpaqoyMjFb/vqVLl2ratGk69dRTtWjRIhUXF+s3v/mN3n333cBn4Re/+IX69++vRx99NFB22q9fv1bXGRcXp0cffVSjRo3SXXfdpby8PH3yySdatmyZkpOTA8v16tVLK1as0Jtvvqlvf/vb7d4njfkv3tHWZ3b9+vX6zne+o8GDB+vOO+9UQkKCvvrqq8Dgw/HHH68777xTc+bM0VVXXaUzzjhDkjRq1ChJZmBZVVWln/70p8rOztYHH3yghx9+WDt27NDzzz8vSbr66qu1a9cuLV++XH/605+ateHqq68ObO/rr79eW7Zs0e9+9zutW7dO7777ruLj47V3797A93j27NnKzMzU1q1b9fe///2Itg8ARBQDAP7niSeeMCS1eDMMw1i1apUhyejbt69RVVXV5LX+51atWmUYhmH4fD7j2GOPNcaPH2/4fL7AclVVVUafPn2Ms88+O/DYhRdeaCQmJhrffPNN4LEvv/zScLlcRuPD1NatWw2Xy2UsWLCgyXt//vnnRlxcXJPHp0yZYvTq1cvy783IyDBmzJjRvo3TyIUXXmi43W7j66+/Djy2a9cuIy0tzRg9enTgsSFDhhgTJ060XNfo0aONtLS0Jn+7YRhNtpl/v2zZsiXw2JgxY4wxY8YE7m/ZssWQZDzxxBOBxw7fR4ZhGH/9618NScbq1asDj91///3N1u/Xq1cvY8qUKYH7N9xwgyHJePvttwOPlZeXG3369DF69+5teL1ewzAOfR6OP/54o7a2NrDsb37zG0OS8fnnnxuGYRjr1q0zJBnPP/98yxuoFXV1dUZeXp4xaNAgo7q6OvD4K6+8Ykgy5syZE3jMv/0+/PDDdq//uuuuM+Lj443U1FRj0qRJzZ7/4osvjKSkJEOSMXToUOPnP/+58dJLLxmVlZXNlh0zZowxYMAAY9++fca+ffuMjRs3GjfffLMhqc3Ph2EYxoMPPmhIMvbt29fqMh9++GGz/e/X0udg0aJFhsPhaPK5mzFjhtFSt+Dtt982JBl/+ctfmjy+bNmyJo+/+OKLHd7OABAtKM8D0MzixYu1fPnyJrfGpkyZoqSkJMt1FBUVafPmzfrRj36kkpIS7d+/X/v371dlZaXOOussrV69Wj6fT16vV6+//rouvPBC9ezZM/D6448/XuPHj2+yzr///e/y+Xy65JJLAuvbv3+/CgoKdOyxx2rVqlUd+jszMzO1du1a7dq1q92v8Xq9euONN3ThhReqb9++gce7du2qH/3oR3rnnXfk8XgC61+/fr02b97c4rr27dun1atXa/r06U3+dklHfKn1xhrvo5qaGu3fv1/f+ta3JKldJYgtee211zR8+HCdfvrpgcdSU1N11VVXaevWrfryyy+bLD9t2rQmmUh/FuS///2vJAUySa+//rqqqqra3Y6PPvpIe/fu1bXXXtvkt3QTJ07UgAEDmpULdtSCBQuUnZ0tp9OpBx98sNnzJ5xwgoqKivTjH/9YW7du1W9+8xtdeOGFys/P1//93/81W37jxo3Kzc1Vbm6uBgwYoPvvv1/f/e5323U1R38m6h//+McRXW2v8eegsrJS+/fv16hRo2QYhtatW9fm659//nllZGTo7LPPbvK9GzZsmFJTUwPfO387X3nlFdXX13e4nQAQyQiaADQzfPhwjRs3rsmtscOvrNcSf6AwZcqUQGfRf3vsscdUW1ursrIy7du3T9XV1Tr22GObraN///7N1mkYho499thm69ywYUO7fhvS2H333acvvvhChYWFGj58uObNmxfozLdm3759qqqqatY2yQz0fD5f4PdVd955p0pLS3XcccfpxBNP1M0336zPPvsssLz/vdq6MuGROnDggH7+858rPz9fSUlJys3NDey79vxeqCXffPNNq3+7//nGDg8Gu3TpIkmB38L06dNHs2bN0mOPPaacnByNHz9eixcvbrN9/vdpqS0DBgxo1o6OSk9PV//+/VVYWNhimagkHXfccfrTn/6k/fv367PPPtPChQsVFxenq666SitWrGiybO/evbV8+XK9/vrr+v3vf6/u3btr37597bp4yg9/+EOddtppuuKKK5Sfn69LL71Uzz33XLsDqG3btmnq1KnKyspSamqqcnNzNWbMGEnt+xxs3rxZZWVlysvLa/a9q6ioCHzvxowZo+9///uaP3++cnJydMEFF+iJJ55QbW1tu9oJAJGM3zQB6LC2skySAh26+++/v9VLIKempnaoQ+Xz+eRwOPSvf/2rxauNpaamtntdknTJJZfojDPO0Isvvqg33nhD999/v+699179/e9/14QJEzq0rpaMHj1aX3/9tf7xj3/ojTfe0GOPPaYHH3xQS5Ys0RVXXHHU62/LJZdcovfee08333yzhg4dqtTUVPl8Pp177rlBmx+oLa1dFc4wjMD/f/3rX2vq1KmB7XT99ddr0aJFev/999WjRw9b2nk0XC6XTjzxRJ144okaOXKkxo4dq7/85S9NBhtSUlKa3D/ttNN08skn6/bbb9dvf/tby/UnJSVp9erVWrVqlV599VUtW7ZMzz77rL797W/rjTfesLzyntfr1dlnn60DBw7o1ltv1YABA5SSkqKdO3dq6tSp7foc+Hw+5eXl6S9/+UuLz/t//+ZwOPS3v/1N77//vl5++WW9/vrrmj59un7961/r/fff7/D3EwAiCUETgJDw/9g+PT3dcm4c/xXjWiph27RpU7N1GoahPn366LjjjgtKO7t27aprr71W1157rfbu3auTTz5ZCxYsaDVoys3NVXJycrO2SWYJltPpVGFhYeCxrKwsTZs2TdOmTVNFRYVGjx6tefPm6YorrgiU933xxRdB+VsaO3jwoFauXKn58+drzpw5gcdb2s4dKQXs1atXq3+7//kj4Q86fvnLX+q9997TaaedpiVLlujuu+9utR2S+Rk5/EIMmzZtOuJ2HC3/pfp3795tudzgwYP14x//WI888ohuuummZhm5wzmdTp111lk666yz9MADD2jhwoX6xS9+oVWrVmncuHGt7sPPP/9c//nPf/Tkk0/qJz/5SeDxw0tupdY/B/369dOKFSt02mmntWvA5Fvf+pa+9a1vacGCBXr66ac1efJkPfPMM7YMFABAqFCeByAkhg0bpn79+ulXv/qVKioqmj2/b98+SeYo/fjx4/XSSy9p27Ztgec3bNig119/vclrLrroIrlcLs2fP79JpkIyMxeNL6XdFq/X26w0KS8vT926dbPMfrlcLp1zzjn6xz/+0eQS4MXFxXr66ad1+umnKz09XZKatSc1NVXHHHNMYP25ubkaPXq0Hn/88SZ/u//vORr+7MPh63nooYeaLZuSkiJJKi0tbXO95513nj744AOtWbMm8FhlZaUeffRR9e7dWwMHDuxQOz0e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GX369LFcJhY+T8OHDzdmzJgRuO/1eo1u3boZixYtanH5Sy65xJg4cWKTx0aMGGFcffXVIW1nJNm7d68hyfj3v//d6jKtHfM7u7lz5xpDhgxp9/J8nkw///nPjX79+hk+n6/F52P18yTJePHFFwP3fT6fUVBQYNx///2Bx0pLS42EhATjr3/9a6vr6ehxDrGNTFOEWrNmjU488UTl5+cHHhs/frw8Ho/Wr1/f6msyMzObZFzGjRsnp9OptWvXhrzN4fDPf/5TJSUlmjZtWpvL/uUvf1FOTo4GDRqk2267TVVVVTa0MLzuueceZWdn66STTtL9999vWd758ccfq76+XuPGjQs8NmDAAPXs2VNr1qyxo7kRoaysTFlZWW0u15k/T3V1dfr444+bfBacTqfGjRvX6mdhzZo1TZaXzGNWrH12JLX5+amoqFCvXr1UWFioCy64oNVjemezefNmdevWTX379tXkyZO1bdu2Vpfl82R+D//85z9r+vTpcjgcrS4Xq5+nxrZs2aI9e/Y0+cxkZGRoxIgRrX5mjuQ4h9gWF+4GoGV79uxpEjBJCtzfs2dPq6/Jy8tr8lhcXJyysrJafU20++Mf/6jx48erR48elsv96Ec/Uq9evdStWzd99tlnuvXWW7Vp0yb9/e9/t6ml9rv++ut18sknKysrS++9955uu+027d69Ww888ECLy+/Zs0dut7vZb+zy8/M77efncF999ZUefvhh/epXv7JcrrN/nvbv3y+v19viMWjjxo0tvqa1Y1asfHZ8Pp9uuOEGnXbaaRo0aFCry/Xv31+PP/64Bg8erLKyMv3qV7/SqFGjtH79+jaPY9FsxIgRWrp0qfr376/du3dr/vz5OuOMM/TFF18oLS2t2fKx/nmSpJdeekmlpaWaOnVqq8vE6ufpcP7PRUc+M0dynENsI2gKotmzZ+vee++1XGbDhg1t/vg1Fh3JttuxY4def/11Pffcc22uv/Hvuk488UR17dpVZ511lr7++mv169fvyBtus45sp1mzZgUeGzx4sNxut66++motWrRICQkJoW5qWB3J52nnzp0699xzdfHFF+vKK6+0fG1n+TwheGbMmKEvvvjC8nc6kjRy5EiNHDkycH/UqFE6/vjj9cgjj+iuu+4KdTPDZsKECYH/Dx48WCNGjFCvXr303HPP6fLLLw9jyyLXH//4R02YMEHdunVrdZlY/TwB4UDQFEQ33nij5YiQJPXt27dd6yooKGh2BRf/VcwKCgpafc3hP15saGjQgQMHWn1NpDiSbffEE08oOztb3/3udzv8fiNGjJBkZhaiqZN7NJ+xESNGqKGhQVu3blX//v2bPV9QUKC6ujqVlpY2yTYVFxdH/OfncB3dTrt27dLYsWM1atQoPfroox1+v2j9PLUmJydHLper2ZUTrT4LBQUFHVq+M7nuuusCF97p6Oh+fHy8TjrpJH311Vchal1kyszM1HHHHdfq3x3LnydJ+uabb7RixYoOZ69j9fPk/1wUFxera9eugceLi4s1dOjQFl9zJMc5xDaCpiDKzc1Vbm5uUNY1cuRILViwQHv37g2U3C1fvlzp6ekaOHBgq68pLS3Vxx9/rGHDhkmS3nzzTfl8vkCnLlJ1dNsZhqEnnnhCP/nJTxQfH9/h9ysqKpKkJgfXaHA0n7GioiI5nc5mJZx+w4YNU3x8vFauXKnvf//7kqRNmzZp27ZtTUYyo0FHttPOnTs1duxYDRs2TE888YSczo7/1DNaP0+tcbvdGjZsmFauXKkLL7xQkll+tnLlSl133XUtvmbkyJFauXKlbrjhhsBjy5cvj7rPTkcYhqGf/exnevHFF/XWW2+pT58+HV6H1+vV559/rvPOOy8ELYxcFRUV+vrrr3XZZZe1+Hwsfp4ae+KJJ5SXl6eJEyd26HWx+nnq06ePCgoKtHLlykCQ5PF4tHbt2lavUnwkxznEuHBfiSJWffPNN8a6deuM+fPnG6mpqca6deuMdevWGeXl5YZhGEZDQ4MxaNAg45xzzjGKioqMZcuWGbm5ucZtt90WWMfatWuN/v37Gzt27Ag8du655xonnXSSsXbtWuOdd94xjj32WGPSpEm2/32htmLFCkOSsWHDhmbP7dixw+jfv7+xdu1awzAM46uvvjLuvPNO46OPPjK2bNli/OMf/zD69u1rjB492u5m2+a9994zHnzwQaOoqMj4+uuvjT//+c9Gbm6u8ZOf/CSwzOHbyTAM45prrjF69uxpvPnmm8ZHH31kjBw50hg5cmQ4/gRb7NixwzjmmGOMs846y9ixY4exe/fuwK3xMrH4eXrmmWeMhIQEY+nSpcaXX35pXHXVVUZmZmbgip6XXXaZMXv27MDy7777rhEXF2f86le/MjZs2GDMnTvXiI+PNz7//PNw/Qkh99Of/tTIyMgw3nrrrSafnaqqqsAyh2+n+fPnG6+//rrx9ddfGx9//LFx6aWXGomJicb69evD8SfY5sYbbzTeeustY8uWLca7775rjBs3zsjJyTH27t1rGAafp8a8Xq/Rs2dP49Zbb232XCx/nsrLywN9JUnGAw88YKxbt8745ptvDMMwjHvuucfIzMw0/vGPfxifffaZccEFFxh9+vQxqqurA+v49re/bTz88MOB+20d54DGCJrCZMqUKYakZrdVq1YFltm6dasxYcIEIykpycjJyTFuvPFGo76+PvD8qlWrDEnGli1bAo+VlJQYkyZNMlJTU4309HRj2rRpgUCsM5k0aZIxatSoFp/bsmVLk225bds2Y/To0UZWVpaRkJBgHHPMMcbNN99slJWV2dhie3388cfGiBEjjIyMDCMxMdE4/vjjjYULFxo1NTWBZQ7fToZhGNXV1ca1115rdOnSxUhOTja+973vNQkgOpsnnniixe9h4/GkWP48Pfzww0bPnj0Nt9ttDB8+3Hj//fcDz40ZM8aYMmVKk+Wfe+4547jjjjPcbrdxwgknGK+++qrNLbZXa5+dJ554IrDM4dvphhtuCGzT/Px847zzzjM++eQT+xtvsx/+8IdG165dDbfbbXTv3t344Q9/aHz11VeB5/k8HfL6668bkoxNmzY1ey6WP0/+Ps/hN//28Pl8xh133GHk5+cbCQkJxllnndVsG/bq1cuYO3duk8esjnNAYw7DMAxbUloAAAAAEIWYpwkAAAAALBA0AQAAAIAFgiYAAAAAsEDQBAAAAAAWCJoAAAAAwAJBEwAAAABYIGgCAAAAAAsETQAAAABggaAJAAAAACwQNAEAQu6VV15Rnz59NHz4cG3evDnczQEAoEMchmEY4W4EAKBz69+/vxYvXqz169drzZo1euaZZ8LdJAAA2o1MEwAg5LKzs3XMMceod+/ecrvd4W4OAAAdEhfuBgAAOr9p06apX79+ys/P1xdffBHu5gAA0CGU5wEAQqqhoUFDhw7V+eefr8WLF6usrEwOhyPczQIAoN0ozwOAGPfXv/5VSUlJ2r17d+CxadOmafDgwSorKzvq9S9ZskR9+/bVjBkzVF5erv/+979HvU4AAOxE0AQAMe7SSy/Vcccdp4ULF0qS5s6dqxUrVuhf//qXMjIyjmrdBw4c0F133aV7771XPXr0UEZGhoqKioLQagAA7MNvmgAgxjkcDi1YsEA/+MEPVFBQoIcfflhvv/22unfvftTrnjt3rr73ve/p+OOPlyQNHDhQn376qb7//e8f9boBALALv2kCAEiSTj75ZK1fv15vvPGGxowZc9Tr+/LLL3Xaaadpw4YNKigokCRdddVV2rNnj/75z38e9foBALALmSYAgJYtW6aNGzfK6/UqPz8/KOucOXOmSktL1aNHj8BjPp9PhYWFQVk/AAB2IdMEADHuk08+0ZlnnqlHHnlES5cuVXp6up5//vmjWucrr7yiqVOnasWKFYqLOzQ+9+GHH2r69Ok6cOCAunTpcrRNBwDAFmSaACCGbd26VRMnTtTtt9+uSZMmqW/fvho5cqQ++eQTnXzyyUe0zvr6et144426+eabNXTo0CbPpaenS5I+/fRTnXnmmUfZegAA7MHV8wAgRh04cEDnnnuuLrjgAs2ePVuSNGLECE2YMEG33377Ea/34YcfVmlpqa677rpmzxUWFio5OZkr6AEAogrleQAAAABggUwTAAAAAFggaAIAAAAACwRNAAAAAGCBoAkAAAAALBA0AQAAAIAFgiYAAAAAsEDQBAAAAAAWCJoAAAAAwAJBEwAAAABYIGgCAAAAAAsETQAAAABg4f8BWQ8Rc9SYLXgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib.colors as colors\n", + "\n", + "\n", + "R0 = 0 # internal radius (in Angstroms) that we don't want to plot\n", + "R1 = 0 # from which radius we want to exclude to take the mean\n", + "\n", + "all_dat_aux = rho.copy()\n", + "all_dat3 = rho[np.sqrt(pos[0]**2+pos[1]**2)>R1] # support array, of these positions we will take the average to make the plot clearer \n", + "\n", + "all_dat_aux[np.sqrt(pos[0]**2+pos[1]**2)': True, + '': 1e-2, + 'NPAN_LOG': 3, + 'NPAN_EQ': 7, + 'NCHEB': 6, + }) + + # now run calculation + with enable_archive_cache(data_dir / 'imp_BdG_wc.aiida'): + out, node = run_get_node(builder) + print(node) + print(out) + + check_dict = {'x': node.outputs.dos_data.get_x()[1][0], 'y': node.outputs.dos_data.get_y()[0][1]} + ndarrays_regression.check(check_dict) diff --git a/tests/workflows/test_kkrimp_BdG_wc/test_imp_BdG_wc.npz b/tests/workflows/test_kkrimp_BdG_wc/test_imp_BdG_wc.npz new file mode 100644 index 00000000..7dbaa8e9 Binary files /dev/null and b/tests/workflows/test_kkrimp_BdG_wc/test_imp_BdG_wc.npz differ diff --git a/tests/workflows/test_kkrimp_full_wc.py b/tests/workflows/test_kkrimp_full_wc.py index eb30458c..a81d126a 100755 --- a/tests/workflows/test_kkrimp_full_wc.py +++ b/tests/workflows/test_kkrimp_full_wc.py @@ -91,86 +91,87 @@ def test_kkrimp_full_wc( assert rms[-1] < 1.40 -# @pytest.mark.timeout(900, method='thread') -# def test_kkrimp_full_Ag_Cu_onsite( -# clear_database_before_test, voronoi_local_code, kkrhost_local_code, kkrimp_local_code, enable_archive_cache -# ): -# """ -# Simple Ag_Cu (bulk) noSOC, FP, lmax2 example where impurity cluster contains only the impurity atom -# """ -# from aiida.orm import Code, load_node, Dict, StructureData, load_group -# from aiida.orm.querybuilder import QueryBuilder -# from masci_tools.io.kkr_params import kkrparams -# from aiida_kkr.workflows.kkr_imp import kkr_imp_wc -# from numpy import array - -# # settings for workflow -# options, wfd, voro_aux_settings = kkr_imp_wc.get_wf_defaults() - -# # workflow behavior -# wfd['nsteps'] = 10 -# wfd['strmix'] = 0.05 -# wfd['do_final_cleanup'] = False -# wfd['convergence_criterion'] = 10**-4 -# # computer settings -# options = { -# 'queue_name': queuename, -# 'resources': { -# 'num_machines': 1 -# }, -# 'max_wallclock_seconds': 5 * 60, -# 'withmpi': False, -# 'custom_scheduler_commands': '' -# } -# options = Dict(options) -# # voronoi settings for impurity startpot -# voro_aux_settings['check_dos'] = False -# voro_aux_settings['natom_in_cls_min'] = 50 -# voro_aux_settings['rclustz'] = 1.5 - -# # make cluster radius small so that only the impurity is inside -# imp_info = Dict({'Rcut': 3.5, 'ilayer_center': 0, 'Zimp': [47.]}) - -# # import parent calculation (converged host system) -# group_pk = import_with_migration('data_dir/kkr_scf_wc-nodes-31a2e00e231215133475de79d47f7c0b.tar.gz') -# for node in load_group(group_pk).nodes: -# if node.label == 'KKR-scf for Cu bulk': -# kkr_scf_wc = node -# kkr_converged = load_node(kkr_scf_wc.outputs.output_kkr_scf_wc_ParameterResults['last_calc_nodeinfo']['uuid']) -# kkrhost_calc_remote = kkr_converged.outputs.remote_folder - -# # give workflow label and description -# label = 'kkrimp_scf full Cu host_in_host' -# descr = 'kkrimp_scf full workflow for Cu bulk inlcuding GF writeout and vorostart for starting potential' - -# # create process builder to set parameters -# builder = kkr_imp_wc.get_builder() -# builder.metadata.description = descr -# builder.metadata.label = label -# builder.kkrimp = kkrimp_local_code -# builder.voronoi = voronoi_local_code -# builder.kkr = kkrhost_local_code -# builder.options = options -# builder.voro_aux_parameters = Dict(voro_aux_settings) -# builder.wf_parameters = Dict(wfd) -# builder.impurity_info = imp_info -# builder.remote_data_host = kkrhost_calc_remote -# builder.scf.params_overwrite = Dict({'TOL_ALAT_CHECK': 1e-8}) - -# # now run calculation -# with enable_archive_cache(data_dir / 'kkrimp_full_Ag_Cu_onsite.aiida'): -# out, node = run_get_node(builder) -# print(out) - -# # check outcome -# n = out['workflow_info'] -# n = n.get_dict() -# print(n) -# for sub in 'auxiliary_voronoi gf_writeout kkr_imp_sub'.split(): -# assert sub in list(n.get('used_subworkflows').keys()) - -# kkrimp_sub = load_node(n['used_subworkflows']['kkr_imp_sub']) -# assert kkrimp_sub.outputs.workflow_info.get_dict().get('successful') +@pytest.mark.timeout(900, method='thread') +def test_kkrimp_full_Ag_Cu_onsite( + clear_database_before_test, voronoi_local_code, kkrhost_local_code, kkrimp_local_code, enable_archive_cache +): + """ + Simple Ag_Cu (bulk) noSOC, FP, lmax2 example with Lloyd where impurity cluster contains only the impurity atom + """ + from aiida.orm import Code, load_node, Dict, StructureData, load_group + from aiida.orm.querybuilder import QueryBuilder + from masci_tools.io.kkr_params import kkrparams + from aiida_kkr.workflows.kkr_imp import kkr_imp_wc + from numpy import array + + # settings for workflow + options, wfd, voro_aux_settings = kkr_imp_wc.get_wf_defaults() + + # workflow behavior + wfd['nsteps'] = 10 + wfd['strmix'] = 0.05 + wfd['do_final_cleanup'] = False + wfd['convergence_criterion'] = 10**-4 + # computer settings + options = { + 'queue_name': queuename, + 'resources': { + 'num_machines': 1 + }, + 'max_wallclock_seconds': 5 * 60, + 'withmpi': False, + 'custom_scheduler_commands': '' + } + options = Dict(options) + # voronoi settings for impurity startpot + voro_aux_settings['check_dos'] = False + voro_aux_settings['natom_in_cls_min'] = 50 + voro_aux_settings['rclustz'] = 1.5 + + # make cluster radius small so that only the impurity is inside + imp_info = Dict({'Rcut': 3.5, 'ilayer_center': 0, 'Zimp': [47.]}) + + # import parent calculation (converged host system) + group_pk = import_with_migration('data_dir/scf_wc_Cu_simple.aiida') + for node in load_group(group_pk).nodes: + if node.label == 'KKR-scf for Cu bulk': + kkr_scf_wc = node + kkr_converged = load_node(kkr_scf_wc.outputs.output_kkr_scf_wc_ParameterResults['last_calc_nodeinfo']['uuid']) + kkrhost_calc_remote = kkr_converged.outputs.remote_folder + + # give workflow label and description + label = 'kkrimp_scf full Cu host_in_host' + descr = 'kkrimp_scf full workflow for Cu bulk inlcuding GF writeout and vorostart for starting potential' + + # create process builder to set parameters + builder = kkr_imp_wc.get_builder() + builder.metadata.description = descr + builder.metadata.label = label + builder.kkrimp = kkrimp_local_code + builder.voronoi = voronoi_local_code + builder.kkr = kkrhost_local_code + builder.options = options + builder.voro_aux_parameters = Dict(voro_aux_settings) + builder.wf_parameters = Dict(wfd) + builder.impurity_info = imp_info + builder.remote_data_host = kkrhost_calc_remote + builder.scf.params_overwrite = Dict({'TOL_ALAT_CHECK': 1e-8}) + + # now run calculation + with enable_archive_cache(data_dir / 'kkrimp_full_Ag_Cu_onsite.aiida'): + out, node = run_get_node(builder) + print(out) + + # check outcome + n = out['workflow_info'] + n = n.get_dict() + print(n) + for sub in 'auxiliary_voronoi gf_writeout kkr_imp_sub'.split(): + assert sub in list(n.get('used_subworkflows').keys()) + + kkrimp_sub = load_node(n['used_subworkflows']['kkr_imp_sub']) + assert kkrimp_sub.outputs.workflow_info.get_dict().get('successful') + #run test manually if __name__ == '__main__': diff --git a/tests/workflows/test_stm.py b/tests/workflows/test_stm.py new file mode 100644 index 00000000..29bf842a --- /dev/null +++ b/tests/workflows/test_stm.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import pytest +from ..dbsetup import * +from aiida.engine import run_get_node +from ..conftest import voronoi_local_code, kkrhost_local_code, test_dir, data_dir, import_with_migration + + +@pytest.mark.timeout(900, method='thread') +def test_stm_wc( + clear_database_before_test, voronoi_local_code, kkrhost_local_code, kkrimp_local_code, enable_archive_cache, + ndarrays_regression +): + """ + STM workflow test for a simple Cu host in host impurity adding a minimal scanning region + """ + from aiida.orm import Code, load_node, Dict, StructureData, load_group + from masci_tools.io.kkr_params import kkrparams + from aiida_kkr.workflows import kkr_STM_wc + from numpy import array + + options = { + 'queue_name': queuename, + 'resources': { + 'num_machines': 1 + }, + 'max_wallclock_seconds': 5 * 60, + 'withmpi': False, + 'custom_scheduler_commands': '' + } + options = Dict(options) + + # import parent calculation (converged host system) + group_pk = import_with_migration('data_dir/kkrimp_full_wc.aiida') + kkr_imp_scf = [n for n in load_group(group_pk).nodes if n.label == 'kkrimp_scf full Cu host_in_host'][0] + + # create process builder to set parameters + builder = kkr_STM_wc.get_builder() + builder.metadata.label = 'stm test' + builder.kkrimp = kkrimp_local_code + builder.voronoi = voronoi_local_code + builder.kkr = kkrhost_local_code + builder.options = options + + builder.host_remote = kkr_imp_scf.inputs.remote_data_host + builder.imp_info = kkr_imp_scf.inputs.impurity_info + builder.imp_potential_node = kkr_imp_scf.outputs.converged_potential + + builder.tip_position = Dict({'ilayer': 0, 'nx': 2, 'ny': 2}) + + builder.wf_parameters = Dict({ + 'jij_run': False, + 'lmdos': True, + 'retrieve_kkrflex': True, + 'dos_params': { + 'nepts': 7, + 'tempr': 200.0, + 'emin': -1.0, + 'emax': 1.0, + 'kmesh': [5, 5, 5] + } + }) + + # now run calculation + with enable_archive_cache(data_dir / 'stm.aiida'): + out, node = run_get_node(builder) + print(out) + print(list(node.called)) + + # check outcome + assert 'STM_dos_data_lmdos' in out + + # check dos data + check_dict = { + 'x': out['STM_dos_data_lmdos'].get_x()[1], + 'y': out['STM_dos_data_lmdos'].get_y()[5][1], + } + print(check_dict) + ndarrays_regression.check(check_dict) + + # print('x', out['STM_dos_data_lmdos'].get_x()[1]) + # print('y', out['STM_dos_data_lmdos'].get_y()[5][1]) diff --git a/tests/workflows/test_stm/test_stm_wc.npz b/tests/workflows/test_stm/test_stm_wc.npz new file mode 100644 index 00000000..24fc1bc9 Binary files /dev/null and b/tests/workflows/test_stm/test_stm_wc.npz differ