From e5917197a3e775306b8f8744b11b21ce3fda2b93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Philipp=20R=C3=BC=C3=9Fmann?=
Date: Fri, 10 May 2024 07:59:39 +0000
Subject: [PATCH] Pre-commit fixes
---
aiida_kkr/tools/tools_STM_scan.py | 148 ++++++------
aiida_kkr/workflows/kkr_STM.py | 349 ++++++++++++++---------------
aiida_kkr/workflows/kkr_imp_sub.py | 2 +-
tests/test_entrypoints.py | 4 +-
4 files changed, 248 insertions(+), 255 deletions(-)
diff --git a/aiida_kkr/tools/tools_STM_scan.py b/aiida_kkr/tools/tools_STM_scan.py
index f8999497..65e7b4b4 100644
--- a/aiida_kkr/tools/tools_STM_scan.py
+++ b/aiida_kkr/tools/tools_STM_scan.py
@@ -19,6 +19,7 @@
##############################################################################
# combine impurty clusters
+
def convert_to_imp_cls(host_structure, imp_info):
"""
convert imp info to rcls form
@@ -230,77 +231,76 @@ def create_combined_potential_node_cf(add_position, host_remote, imp_potential_n
#####################################################################
-# STM pathfinder
+# STM pathfinder
def STM_pathfinder(host_structure):
from aiida_kkr.tools import find_parent_structure
from ase.spacegroup import Spacegroup
-
- """This function is used to help visualize the scanned positions
+ """This function is used to help visualize the scanned positions
and the symmetries that are present in the system """
-
"""
- inputs:
+ inputs:
host_struture : RemoteData : The Remote data contains all the information needed to create the path to scan
-
- outputs:
+
+ 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
-
+
"""
-
+
def info_creation(structure):
from ase.spacegroup import get_spacegroup
- # List of the Bravais vectors
+ # List of the Bravais vectors
vec_list = structure.cell.tolist()
-
+
# Find the Bravais vectors that are in plane vectors
- plane_vectors = {'plane_vectors':[], 'space_group':''}
+ 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])
-
+
space_symmetry = get_spacegroup(ase_struc)
plane_vectors['space_group'] = space_symmetry.no
-
+
return plane_vectors
-
+
def symmetry_finder(struc_info):
# Here we get the symmetry operations that are possible
symmetry_matrices = Spacegroup(struc_info['space_group'])
-
+
# Reduce the dimensionality, we only want the 2D matrices
matrices = []
for element in symmetry_matrices.get_rotations():
matrices.append(element[:2, :2])
-
+
# 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)
-
+
return unique_matrices
-
+
struc = find_parent_structure(host_structure)
# clone the structure since it has already been saved in AiiDA and cannot be modified
supp_struc = struc.clone()
-
+
# If the structure is not periodic in every direction we force it to be.
supp_struc.pbc = (True, True, True)
-
- # ASE struc
+
+ # 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)
-
- return struc_info, symm_matrices
+
+ return struc_info, symm_matrices
+
@engine.calcfunction
def STM_pathfinder_cf(host_structure):
@@ -318,50 +318,52 @@ def STM_pathfinder_cf(host_structure):
def lattice_generation(x_len, y_len, rot, vec):
- import math
-
+ 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.
"""
-
+
# Here we create a grid in made of points whic are the linear combination of the lattice vectors
lattice_points = []
-
- for i in range(-x_len, x_len+1):
+
+ 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])]
+ 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)
-
+
# Eliminiatio of the symmetrical sites
points_to_eliminate = []
-
- for i in range(-x_len, x_len+1):
- for j in range(-y_len, y_len+1):
+
+ 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 and lattice_points[i][j][1] >= 0:
for element in rot[1:]:
point = np.dot(element, lattice_points[i][j])
- if point[0] >= 0 and point[1] >=0:
+ if point[0] >= 0 and point[1] >= 0:
continue
else:
points_to_eliminate.append(point)
-
+
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)):
+ 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])
-
+
return points_to_eliminate, points_to_scan
@@ -373,48 +375,50 @@ def lattice_plot(plane_vectors, symm_vec, symm_matrices, grid_length_x, grid_len
from aiida_kkr.tools import lattice_generation
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
-
- origin = np.array([[0, 0],[0, 0]])
+
+ 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(grid_length_x, grid_length_y, symm_matrices, plane_vectors)
+
# Plotting of the points
for element in unused:
plt.scatter(element[0], element[1], marker='s', s=130, c='#33638DFF')
-
+
for element in used:
- plt.scatter(element[0], element[1], marker='D', s=130, c='#FDE725FF')
-
- # Plot of the crystal symmetry directions, tag must be activated.
+ plt.scatter(element[0], element[1], marker='D', s=130, c='#FDE725FF')
+
+ # Plot of the crystal symmetry directions, tag must be activated.
if symm_vec:
import numpy.linalg as lin
-
+
for element in symm_matrices:
eig_val, eig_vec = lin.eig(element)
-
+
for element in eig_vec:
- plt.quiver(*origin, element[0], element[1], alpha=1, color='#B8DE29FF', angles='xy', scale_units='xy', scale=1)
-
+ plt.quiver(
+ *origin, element[0], element[1], alpha=1, color='#B8DE29FF', angles='xy', scale_units='xy', scale=1
+ )
+
# Plot of the Bravais lattice
for element in plane_vectors:
plt.quiver(*origin, element[0], element[1], color='#3CBB75FF', angles='xy', scale_units='xy', scale=1)
-
+
legend_elements = [
- Line2D([0], [0], color='#33638DFF', lw=2, label='Unscanned Sites', marker='s'),
- Line2D([0], [0], color='#FDE725FF', lw=2, label='Scanned Sites', marker='D'),
- Line2D([0], [0], color='#3CBB75FF', lw=2, label='Bravais lattice'),
+ Line2D([0], [0], color='#33638DFF', lw=2, label='Unscanned Sites', marker='s'),
+ Line2D([0], [0], color='#FDE725FF', lw=2, label='Scanned Sites', marker='D'),
+ Line2D([0], [0], color='#3CBB75FF', lw=2, label='Bravais lattice'),
]
plt.legend(handles=legend_elements, bbox_to_anchor=(0.75, -0.15))
-
+
plt.title('Lattice plot and symmetry directions')
plt.ylabel('y direction')
plt.xlabel('x direction')
#plt.xticks(np.arange(-grid_length, grid_length, float(plane_vectors[0][0])))
#plt.set_cmap(cmap)
- plt.grid(linestyle='--')
+ plt.grid(linestyle='--')
plt.show()
-
-
+
+
##########################################################
# find linear combination coefficients
@@ -423,31 +427,31 @@ 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"""
-
+
# Formulate the system of equations Ax = b
A = np.vstack((plane_vectors[0], plane_vectors[1])).T
# Solve the system of equations using least squares method
data = []
for element in vectors:
- b = element
+ 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))
-
+
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.
+ # 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))
-
+ # on the x axis and then from the lowest to the highest on the y axis.
+
+ indices = sorted(indices, key=itemgetter(0, 1))
+
return indices
diff --git a/aiida_kkr/workflows/kkr_STM.py b/aiida_kkr/workflows/kkr_STM.py
index 098f8492..ffa3714c 100644
--- a/aiida_kkr/workflows/kkr_STM.py
+++ b/aiida_kkr/workflows/kkr_STM.py
@@ -16,68 +16,70 @@
__contributors__ = (u'Raffaele Aliberti, David Antognini Silva, Philipp Rüßmann')
_VERBOSE_ = True
+
class kkr_STM_wc(WorkChain):
"""
Workchain for the Simulation of a (superconducting) STM tip around a magnetic impurity.
The workchain uses the (converged) impurity calculation of a host system and combines them
- with vacuum sites in positions
+ with vacuum sites in positions
inputs::
:param options: (Dict), computer options
:param tip_position: (Dict), specify the position of the STM tip
:param impurity_info: (Dict), information about the impurity cluster
- :param host_calc: (RemoteData), information about the host structure of the sample
+ :param host_calc: (RemoteData), information about the host structure of the sample
:param wf_parameters: (Dict), parameters that are used to run the workflow
: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
-
+
returns::
-
+
:return workflow_info: (Dict), Information of workflow results
like success, last result node, list with convergence behavior
- :return STM_dos_data: (XYData), Returns the plot of the lmDOS of the calculation
+ :return STM_dos_data: (XYData), Returns the plot of the lmDOS of the calculation
:retrun STM_lmdos_data: (XYData), Returns the interpolated lmDOS of the calculation"""
-
+
# TO DO: Add the initialize step.
# 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
-
+
_wf_version = __version__
_wf_label = 'STM_wc'
_wf_description = 'Workflow for simulating an STM measurement'
_options_default = {
'queue_name': '', # Queue name to submit jobs too
- 'resources': {'num_machines': 1, 'num_mpiprocs_per_machine': 48, 'num_cores_per_mpiproc': 1}, # resources to allocate for the job
- 'max_wallclock_seconds': 3600*2, # walltime after which the job gets killed (gets parsed to KKR)}
+ 'resources': {
+ 'num_machines': 1,
+ 'num_mpiprocs_per_machine': 48,
+ 'num_cores_per_mpiproc': 1
+ }, # resources to allocate for the job
+ 'max_wallclock_seconds': 3600 * 2, # walltime after which the job gets killed (gets parsed to KKR)}
'custom_scheduler_commands': '', # some additional scheduler commands
'withmpi': True
} # execute KKR with mpi or without
_wf_default = {
'jij_run': False, # calculate Jij's energy resolved
- 'lmdos': True, # calculate also (l,m) or only l-resolved DOS, for the STM wf this is alwyas set on True as default
+ 'lmdos':
+ True, # calculate also (l,m) or only l-resolved DOS, for the STM wf this is alwyas set on True as default
'retrieve_kkrflex': False, # retrieve kkrflex files to repository or leave on remote computer only
}
# 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']
-
+
@classmethod
def define(cls, spec):
"""
Layout of the workflow, defines the input nodes and the outline of the workchain
"""
super(kkr_STM_wc, cls).define(spec)
-
- spec.input('kkr', valid_type=Code, required=False,
- help='KKRhost code, needed if gf_dos_remote is not given.'
- )
-
- spec.input('kkrimp', valid_type=Code, required=True,
- help='KKRimp code, always needed.'
- )
-
+
+ spec.input('kkr', valid_type=Code, required=False, help='KKRhost code, needed if gf_dos_remote is not given.')
+
+ spec.input('kkrimp', valid_type=Code, required=True, help='KKRimp code, always needed.')
+
spec.input(
'options',
valid_type=Dict,
@@ -85,7 +87,7 @@ def define(cls, spec):
default=lambda: Dict(dict=cls._options_default),
help='Computer options (resources, quene name, etc.).'
)
-
+
spec.input(
'wf_parameters',
valid_type=Dict,
@@ -93,147 +95,133 @@ def define(cls, spec):
default=lambda: orm.Dict(dict=cls._wf_default),
help='Workflow parameter (see `kkr_dos_wc.get_wf_defaults()`).'
)
-
+
spec.input(
'tip_position',
valid_type=Dict,
required=False,
- # Find a way to create an area of this position.
- default=lambda:Dict({'ilayer': 0, 'nx': 0, 'ny': 0}),
+ # Find a way to create an area of this position.
+ default=lambda: Dict({
+ 'ilayer': 0,
+ 'nx': 0,
+ 'ny': 0
+ }),
# In the previous line we have set to study the first layer
# nx is the number of (symmetric) steps that we take in the x-direction starting from the impurity
# ny is the number of (symmetric) steps that we take in the y-direction starting from the impurity
# (0,0) correspond to calculate the DOS only on the impurity site
- help=
- 'How many sites will be scanned in the x and y direction. And the layer that is being scanned.'
+ help='How many sites will be scanned in the x and y direction. And the layer that is being scanned.'
)
-
+
spec.input(
'imp_info',
valid_type=Dict,
required=True,
- help=
- 'Information of the impurity like position in the unit cell, screening cluster, atom type.'
+ 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'
+ 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,
required=True,
help='Remote Data containing the remote folder from the outputs of the host calculation',
)
-
+
spec.input(
'imp_potential_node',
valid_type=SinglefileData,
required=True,
- help=
- 'Impurity potential node',
+ 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',
+ help='Remote data from a converged kkr calculation, required for the gf writeout step',
)
-
+
spec.input(
'kkrflex_files',
valid_type=RemoteData,
required=False,
- help=
- 'with this input we can directly load the gf_dos files without calculating them'
+ help='with this input we can directly load the gf_dos files without calculating them'
)
-
- # Here we expose the inputs for the GF calculations step.
+
+ # Here we expose the inputs for the GF calculations step.
# One parameter which is crucial is the NSHELD, which determines the impurity cluster radius.
spec.expose_inputs(kkr_flex_wc, namespace='gf_writeout', include=('params_kkr_overwrite'))
-
+
# Specify the possible outputs
spec.output('tip_position', valid_type=Dict)
-
+
spec.output('STM_dos_data', valid_type=XyData, required=True)
-
+
spec.output('STM_dos_data_lmdos', valid_type=XyData, required=True)
-
+
#spec.output('workflow_info', valid_type=Dict)
-
+
spec.output('kkrflexfiles', valid_type=RemoteData)
-
+
spec.output('combined_imp_info', valid_type=Dict)
-
+
spec.output('combined_imp_potential', valid_type=SinglefileData)
-
+
# Define all possible error messages
-
- spec.exit_code(
- 100, 'ERROR_STM_POSITION_NOT_VALID', 'The position provided for the STM probe are incorrect'
- )
- spec.exit_code(
- 101, 'ERROR_IMP_INFO_NOT_CORRECT', 'The node provided for the impurity info is not valid'
- )
- spec.exit_code(
- 102, 'ERROR_NO_IMP_POT_SFD', 'No impurity node has been given in the intput'
- )
- spec.exit_code(
- 103, 'ERROR_NO_IMPURITY_INFO', 'No impurity info has been given in the input'
- )
+
+ spec.exit_code(100, 'ERROR_STM_POSITION_NOT_VALID', 'The position provided for the STM probe are incorrect')
+ spec.exit_code(101, 'ERROR_IMP_INFO_NOT_CORRECT', 'The node provided for the impurity info is not valid')
+ spec.exit_code(102, 'ERROR_NO_IMP_POT_SFD', 'No impurity node has been given in the intput')
+ spec.exit_code(103, 'ERROR_NO_IMPURITY_INFO', 'No impurity info has been given in the input')
spec.exit_code(
- 104, 'ERROR_NO_DATA_FOR_THE_GF_STEP', """Neither the kkrflex files nor the KKR builder have been given. Please
+ 104, 'ERROR_NO_DATA_FOR_THE_GF_STEP',
+ """Neither the kkrflex files nor the KKR builder have been given. Please
provide already converged kkrflex files, or the kkr builder to evaluate them"""
)
- spec.exit_code(
- 201, 'ERROR_IMP_SUB_WORKFLOW_FAILURE', 'A step in the kkr_imp_dos workflow has failed'
- )
-
-
+ spec.exit_code(201, 'ERROR_IMP_SUB_WORKFLOW_FAILURE', 'A step in the kkr_imp_dos workflow has failed')
+
spec.outline(
# For initializing workflow
cls.start,
# We first aggregate all the impurity data
# The gf is then used to evaluate the STM lmdos
#cls.gf_writeout_run,
-
- cls.STM_lmdos_run,
+ cls.STM_lmdos_run,
# Data aggregator, used to make the final result more user friendly
# cls.finalize_results,
-
cls.results
)
-
+
def combine_potentials(self, impurity_to_combine, da, db):
from aiida_kkr.tools.tools_STM_scan import get_imp_info_add_position_cf
"""
- Here we want to combine the impurity information and the host information
+ Here we want to combine the impurity information and the host information
"""
-
- imp_info = self.inputs.imp_info #(impurity to combine)
+
+ imp_info = self.inputs.imp_info #(impurity to combine)
host_remote = self.inputs.host_remote
- # Since the objects in AiiDA are immutable we have to create a new dictionary and then convert
+ # 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 = {}
tip_position['ilayer'] = self.inputs.tip_position['ilayer']
tip_position['da'] = da
tip_position['db'] = db
-
+
combined_imp_info = get_imp_info_add_position_cf(tip_position, host_remote, imp_info)
# Add check to see if imp_cls is there
if 'imp_cls' in impurity_to_combine:
-
+
new_combined_imp_info = {}
-
+
for key, value in impurity_to_combine.items():
if key == 'Zimp':
new_combined_imp_info[key] = impurity_to_combine[key]
@@ -242,17 +230,17 @@ def combine_potentials(self, impurity_to_combine, da, db):
# Here we have lists of list that we need to confront
new_combined_imp_info[key] = impurity_to_combine[key]
set_tmp = [set(row) for row in impurity_to_combine[key]]
-
+
new_combined_imp_info[key] += [row for row in combined_imp_info[key] if set(row) not in set_tmp]
-
+
new_combined_imp_info = orm.Dict(dict=new_combined_imp_info)
else:
-
- new_combined_imp_info = combined_imp_info
-
+
+ new_combined_imp_info = combined_imp_info
+
return new_combined_imp_info
-
+
def combine_nodes(self, node_to_combine, da, db):
from aiida_kkr.tools.tools_STM_scan import create_combined_potential_node_cf
"""
@@ -260,47 +248,48 @@ def combine_nodes(self, node_to_combine, da, db):
and from the impurity potential
"""
#imp_potential_node = self.inputs.imp_potential_node # (node_to_combine).
- host_remote = self.inputs.host_remote # the remote host structure remains the same.
-
- # Since the objects in AiiDA are immutable we have to create a new dictionary and then convert
+ host_remote = self.inputs.host_remote # the remote host structure remains the same.
+
+ # 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 = {}
- tip_position['ilayer'] = self.inputs.tip_position['ilayer'] # for now we require that the z position remains the same.
+ tip_position['ilayer'] = self.inputs.tip_position['ilayer'
+ ] # for now we require that the z position remains the same.
tip_position['da'] = da
tip_position['db'] = db
- combined_node = create_combined_potential_node_cf(tip_position, host_remote, node_to_combine)
+ combined_node = create_combined_potential_node_cf(tip_position, host_remote, node_to_combine)
return combined_node
-
+
def start(self):
"""
Initialise context and some parameters
"""
-
+
self.report(f'INFO: started STM workflow version {self._wf_version}')
if _VERBOSE_:
self.report(f'inputs: {self.inputs}')
-
+
# Input both wf and options parameters
# We check if the inputs for the wc where given
- # Otherwise we assign the default values
+ # Otherwise we assign the default values
if 'options' in self.inputs:
options_dict = self.inputs.options.get_dict()
# empty dictionary evaluate as false to Python
if options_dict == {}:
options_dict = self._options_default
self.report('INFO: using default options parameters')
-
+
if 'wf_parameters' in self.inputs:
wf_param_dict = self.inputs.wf_parameters.get_dict()
# empty dictionary evaluate as false to Python
if wf_param_dict == {}:
options_dict = self._wf_default
self.report('INFO: usign defalut wf parameters')
-
+
# In this section we assign the computational resources to the builder
-
+
self.ctx.withmpi = options_dict.get('withmpi', self._options_default['withmpi'])
self.ctx.resources = options_dict.get('resources', self._options_default['resources'])
self.ctx.max_wallclock_seconds = options_dict.get(
@@ -321,11 +310,11 @@ def start(self):
# Set workflow parameters for the KKR imputrity calculations
"""This part is really important, this should always be set to True for an STM calculation"""
self.ctx.lmdos = wf_param_dict.get('lmdos', self._wf_default['lmdos'])
-
+
self.ctx.retrieve_kkrflex = wf_param_dict.get('retrieve_kkrflex', self._wf_default['retrieve_kkrflex'])
self.ctx.dos_params_dict = wf_param_dict.get('dos_params', self._wf_default['dos_params'])
-
+
# fill missing key, value pairs with defaults
for k, v in self._wf_default['dos_params'].items():
if k not in self.ctx.dos_params_dict.keys():
@@ -337,7 +326,7 @@ def start(self):
# set workflow label and description
self.ctx.description_wf = self.inputs.get('description', self._wf_description)
self.ctx.label_wf = self.inputs.get('label', self._wf_label)
-
+
message = f"""
INFO: use the following parameter:
withmpi: {self.ctx.withmpi}
@@ -355,169 +344,169 @@ def start(self):
self.ctx.successful = True
self.ctx.errors = []
self.ctx.formula = ''
-
-
+
def validate_input(self):
-
+
inputs = self.inputs
inputs_ok = True
gf_writeout_calc = None
-
+
if not 'imp_potential_node' in inputs:
inputs_ok = False
return self.exit_codes.ERROR_NO_IMP_POT_SFD
-
+
if not 'imp_info' in inputs:
inputs_ok = False
return self.exit_codes.ERROR_NO_IMP_INFO
-
+
if not 'kkrflex_files' and 'kkr' in inputs:
inputs_ok = False
return self.exit_codes.ERROR_NO_DATA_FOR_THE_GF_STEP
-
+
def impurity_cluster_evaluation(self):
from aiida_kkr.tools import tools_STM_scan
-
+
# Here we create an impurity cluster that has inside all the positions on which the STM will scan
-
- # We now want to iterate over several in-plane positions.
+
+ # 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']
-
- impurity_info = self.inputs.imp_info # for the first step we combine the impurity info from the input
- imp_potential_node = self.inputs.imp_potential_node # for the first step we combine the impurity node from the input
-
- # Information of the host structure
+
+ impurity_info = self.inputs.imp_info # for the first step we combine the impurity info from the input
+ imp_potential_node = self.inputs.imp_potential_node # for the first step we combine the impurity node from the input
+
+ # Information of the host structure
struc_info, symm_matrices = tools_STM_scan.STM_pathfinder(host_remote)
-
+
# 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'])
-
+
# 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.
-
+
coeff = tools_STM_scan.find_linear_combination_coefficients(struc_info['plane_vectors'], used_pos)
-
- for element in coeff:
+
+ for element in coeff:
tmp_imp_info = self.combine_potentials(impurity_info, element[0], element[1])
impurity_info = tmp_imp_info
-
+
# Aggregation the impurity nodes
tmp_imp_pot = self.combine_nodes(imp_potential_node, element[0], element[1])
imp_potential_node = tmp_imp_pot
-
- return impurity_info, imp_potential_node
+
+ return impurity_info, imp_potential_node
def STM_lmdos_run(self):
"""In this part of the worflow we want to simulate the lmdos which a STM is able to measure """
-
- # First we would like to distinguish between an impurity dos and a normal state calculation
+
+ # First we would like to distinguish between an impurity dos and a normal state calculation
builder = kkr_imp_dos_wc.get_builder()
-
+
# Code loading
- builder.kkrimp = self.inputs.kkrimp # needed for the kkr_imp_dos_wc
-
+ builder.kkrimp = self.inputs.kkrimp # needed for the kkr_imp_dos_wc
+
# Builder options
builder.options = self.ctx.options_params_dict
-
+
# 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
else:
- builder.kkr = self.inputs.kkr # needed to evaluate the kkr_flex files in the DOS step
-
+ builder.kkr = self.inputs.kkr # needed to evaluate the kkr_flex files in the DOS step
+
# NSHELD is the parameter that controls the radius in the impurity cluster.
- # The bigger the scanning position, the greater it must be set.
+ # The bigger the scanning position, the greater it must be set.
if 'gf_writeout' in self.inputs:
if 'params_kkr_overwrite' in self.inputs.gf_writeout:
builder.gf_writeout.params_kkr_overwrite = self.inputs.gf_writeout.params_kkr_overwrite
else:
# This is a big value of NSHELD to make sure that most calculations work
builder.gf_writeout.params_kkr_overwrite = Dict(dict={'NSHELD': 1500})
-
- self.ctx.kkrimp_params_dict = Dict(dict={
- 'nsteps': 1,
- 'kkr_runmax': 1,
- 'dos_run': True,
- 'lmdos': self.ctx.lmdos,
- 'jij_run': self.ctx.jij_run,
- 'dos_params': self.ctx.dos_params_dict
- })
-
+
+ self.ctx.kkrimp_params_dict = Dict(
+ dict={
+ 'nsteps': 1,
+ 'kkr_runmax': 1,
+ 'dos_run': True,
+ 'lmdos': self.ctx.lmdos,
+ 'jij_run': self.ctx.jij_run,
+ 'dos_params': self.ctx.dos_params_dict
+ }
+ )
+
# We want to set the energy to the Fermi level
- self.ctx.kkrimp_params_dict['dos_params']['emin'] = 0-0.005
- self.ctx.kkrimp_params_dict['dos_params']['emax'] = 0+0.005
-
+ self.ctx.kkrimp_params_dict['dos_params']['emin'] = 0 - 0.005
+ self.ctx.kkrimp_params_dict['dos_params']['emax'] = 0 + 0.005
+
# Finally we overwrite the number of energy points to 1
# This is because we want many epoints around the impurity position
-
- self.ctx.kkrimp_params_dict['dos_params']['nepts'] = 7 # Here 7 because of the interpolated files that aren't generated
-
+
+ self.ctx.kkrimp_params_dict['dos_params'][
+ 'nepts'] = 7 # Here 7 because of the interpolated files that aren't generated
+
#builder.metadata.label = label_imp # pylint: disable=no-member
#builder.metadata.description = description_imp # pylint: disable=no-member
-
- builder.wf_parameters = self.ctx.kkrimp_params_dict
+
+ builder.wf_parameters = self.ctx.kkrimp_params_dict
# Host remote files that will be used for the actual plot step.
builder.host_remote = self.inputs.host_remote
-
-
+
# Here we create the impurity cluster for the STM scanning tool
impurity_info, imp_pot_sfd = self.impurity_cluster_evaluation()
-
+
# impurity info for the workflow
- builder.impurity_info = impurity_info
+ builder.impurity_info = impurity_info
builder.imp_pot_sfd = imp_pot_sfd
-
+
x = self.inputs.tip_position['nx']
y = self.inputs.tip_position['ny']
-
+
calc = self.submit(builder)
message = f"""INFO: running DOS step for an STM measurement (pk: {calc.pk}) at position
(ilayer: {self.inputs.tip_position['ilayer']}, da: {x}, db: {y} )"""
-
+
print(message)
self.report(message)
-
- # Save the calculated impurity cluster and impurity info in the context
+
+ # 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
-
- return ToContext(STM_data = calc)
-
+
+ return ToContext(STM_data=calc)
+
def results(self):
-
+
if not self.ctx.STM_data.is_finished_ok:
-
+
message = 'ERROR: sub workflow for STM calculation failed'
print(message)
self.report(message)
return self.exit_codes.ERROR_IMP_SUB_WORKFLOW_FAILURE
-
+
else:
# Declaring the output
- self.out("STM_dos_data", self.ctx.STM_data.outputs.dos_data)
- self.out("STM_dos_data_lmdos", self.ctx.STM_data.outputs.dos_data_lm)
+ self.out('STM_dos_data', self.ctx.STM_data.outputs.dos_data)
+ self.out('STM_dos_data_lmdos', self.ctx.STM_data.outputs.dos_data_lm)
#self.out("workflow_info", self.ctx.STM_lmdos.outputs.workflow_info)
- self.out("tip_position", self.inputs.tip_position)
+ self.out('tip_position', self.inputs.tip_position)
try:
- self.out("kkrflexfiles",self.ctx.STM_data.outputs.gf_dos_remote)
+ self.out('kkrflexfiles', self.ctx.STM_data.outputs.gf_dos_remote)
except:
pass
-
- self.out("combined_imp_info", self.ctx.impurity_info)
- self.out("combined_imp_potential", self.ctx.imp_pot_sfd)
-
-
+
+ self.out('combined_imp_info', self.ctx.impurity_info)
+ self.out('combined_imp_potential', self.ctx.imp_pot_sfd)
+
message = 'INFO: created output nodes for KKR STM workflow.'
print(message)
self.report(message)
-
+
self.report(
'\n'
'|------------------------------------------------------------------------------------------------------------------|\n'
'|-----------------------------------------| Done with the STM workflow! |------------------------------------------|\n'
'|------------------------------------------------------------------------------------------------------------------|'
- )
\ No newline at end of file
+ )
diff --git a/aiida_kkr/workflows/kkr_imp_sub.py b/aiida_kkr/workflows/kkr_imp_sub.py
index bc9b810d..a71f7863 100644
--- a/aiida_kkr/workflows/kkr_imp_sub.py
+++ b/aiida_kkr/workflows/kkr_imp_sub.py
@@ -933,7 +933,7 @@ def inspect_kkrimp(self):
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
+ pass
# add lists of last iterations
self.ctx.last_rms_all = rms_all_iter_last_calc
diff --git a/tests/test_entrypoints.py b/tests/test_entrypoints.py
index 934ff150..36830e2b 100644
--- a/tests/test_entrypoints.py
+++ b/tests/test_entrypoints.py
@@ -205,10 +205,10 @@ def test_kkrimp_BdG_workchain_entry_point(self):
wf = WorkflowFactory('kkr.imp_BdG')
assert wf == kkrimp_BdG_wc
-
+
def test_kkr_STM_workchain_entry_point(self):
from aiida_kkr.workflows.kkr_STM import kkr_STM_wc
from aiida.plugins import WorkflowFactory
-
+
wf = WorkflowFactory('kkr.STM')
assert wf == kkr_STM_wc