# -*- coding: utf-8 -*-
"""
Input plug-in for a voronoi calculation.
"""
from aiida.orm.calculation.job import JobCalculation
from aiida.common.utils import classproperty
from aiida.common.exceptions import (InputValidationError, ValidationError)
from aiida.common.datastructures import (CalcInfo, CodeInfo)
from aiida.orm import DataFactory
from aiida_kkr.tools.common_workfunctions import generate_inputcard_from_structure, check_2Dinput_consistency
from aiida.common.exceptions import UniquenessError
import os
__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.4"
__contributors__ = ("Jens Broeder", "Philipp Rüßmann")
ParameterData = DataFactory('parameter')
StructureData = DataFactory('structure')
RemoteData = DataFactory('remote')
[docs]class VoronoiCalculation(JobCalculation):
"""
AiiDA calculation plugin for a voronoi calculation (creation of starting potential and shapefun)
.
"""
[docs] def _init_internal_params(self):
"""
Init internal parameters at class load time
"""
# reuse base class (i.e. JobCalculation) functions
super(VoronoiCalculation, self)._init_internal_params()
# calculation plugin version
self._CALCULATION_PLUGIN_VERSION = __version__
# Default input and output files
self._DEFAULT_INPUT_FILE = 'inputcard' # will be shown with inputcat
self._DEFAULT_OUTPUT_FILE = 'out_voronoi' #'shell output will be shown with outputca
# List of mandatory input files
self._INPUT_FILE_NAME = 'inputcard'
#self._INPUTCARD = 'inputcard'
# List of output files that should always be present
self._OUTPUT_FILE_NAME = 'out_voronoi'
# template.product entry point defined in setup.json
self._default_parser = 'kkr.voroparser'
# File names
self._ATOMINFO = 'atominfo.txt'
self._RADII = 'radii.dat'
self._SHAPEFUN = 'shapefun'
self._VERTICES = 'vertices.dat'
self._OUT_POTENTIAL_voronoi = 'output.pot'
self._POTENTIAL_IN_OVERWRITE = 'overwrite_potential'
@classproperty
def _use_methods(cls):
"""
Add use_* methods for calculations.
Code below enables the usage
my_calculation.use_parameters(my_parameters)
"""
use_dict = JobCalculation._use_methods
use_dict.update({
"parameters": {
'valid_types': ParameterData,
'additional_parameter': None,
'linkname': 'parameters',
'docstring':
("Use a node that specifies the input parameters ")
},
"structure": {
'valid_types': StructureData,
'additional_parameter': None,
'linkname': 'structure',
'docstring':
("Use a node that specifies the input crystal structure ")
},
"parent_KKR": {
'valid_types': RemoteData,
'additional_parameter': None,
'linkname': 'parent_calc',
'docstring':
("Use a node that specifies a parent KKR calculation ")
},
})
return use_dict
[docs] def _prepare_for_submission(self, tempfolder, inputdict):
"""
Create input files.
:param tempfolder: aiida.common.folders.Folder subclass where
the plugin should put all its files.
:param inputdict: dictionary of the input nodes as they would
be returned by get_inputs_dict
"""
# Check inputdict
try:
parameters = inputdict.pop(self.get_linkname('parameters'))
except KeyError:
raise InputValidationError("No parameters specified for this "
"calculation")
if not isinstance(parameters, ParameterData):
raise InputValidationError("parameters not of type "
"ParameterData")
try:
structure = inputdict.pop(self.get_linkname('structure'))
found_structure = True
except KeyError:
found_structure = False
if found_structure:
if not isinstance(structure, StructureData):
raise InputValidationError("structure not of type "
"StructureData")
try:
code = inputdict.pop(self.get_linkname('code'))
except KeyError:
raise InputValidationError("No code specified for this "
"calculation")
# check if a parent folder containing a potential file (out_potential) is given
try:
parent_calc_folder = inputdict.pop(self.get_linkname('parent_KKR'))
found_parent = True
except KeyError:
found_parent = False
if found_parent:
if not isinstance(parent_calc_folder, RemoteData):
raise InputValidationError("parent_KKR must be of type RemoteData")
# check if parent is either Voronoi or previous KKR calculation
overwrite_potential, parent_calc = self._check_valid_parent(parent_calc_folder)
#cross check if no structure was given and extract structure from parent
if found_structure:
raise InputValidationError("parent_KKR and structure found in input. "
"Can only use either parent_KKR or structure in input.")
else:
structure, voro_parent = self.find_parent_structure(parent_calc)
else:
overwrite_potential = False
if not found_structure:
raise InputValidationError("Neither structure not parent_KKR specified for this "
"calculation")
# finally check if something else was given as input (should not be the case)
if inputdict:
raise ValidationError("Unknown inputs: {}".format(inputdict))
###################################
# Check for 2D case
twoDimcheck, msg = check_2Dinput_consistency(structure, parameters)
if not twoDimcheck:
raise InputValidationError(msg)
# Prepare inputcard from Structure and input parameter data
input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME)
try:
natom, nspin, newsosol = generate_inputcard_from_structure(parameters, structure, input_filename, isvoronoi=True)
except ValueError as e:
raise InputValidationError("Input ParameterData not consistent: {}".format(e))
local_copy_list = []
# Decide what files to copy
if overwrite_potential:
# copy the right files #TODO check first if file, exists and throw
# warning, now this will throw an error
outfolderpath = parent_calc.out.retrieved.folder.abspath
self.logger.info("out folder path {}".format(outfolderpath))
copylist = []
if self._is_KkrCalc(parent_calc):
copylist = [parent_calc._OUT_POTENTIAL]
for file1 in copylist:
filename = file1
if file1 == parent_calc._OUT_POTENTIAL:
filename = self._POTENTIAL_IN_OVERWRITE
local_copy_list.append((
os.path.join(outfolderpath, 'path', file1),
os.path.join(filename)))
# Prepare CalcInfo to be returned to aiida
calcinfo = CalcInfo()
calcinfo.uuid = self.uuid
calcinfo.local_copy_list = local_copy_list
calcinfo.remote_copy_list = []
calcinfo.retrieve_list = [self._OUTPUT_FILE_NAME, self._ATOMINFO,
self._RADII, self._SHAPEFUN, self._VERTICES,
self._INPUT_FILE_NAME]
# pass on overwrite potential if this was given in input
# (KkrCalculation checks if this file is there and takes this file instead of _OUT_POTENTIAL_voronoi
# if given)
if overwrite_potential:
calcinfo.retrieve_list += [self._POTENTIAL_IN_OVERWRITE]
else:
calcinfo.retrieve_list += [self._OUT_POTENTIAL_voronoi]
codeinfo = CodeInfo()
codeinfo.cmdline_params = []
codeinfo.stdout_name = self._OUTPUT_FILE_NAME
codeinfo.code_uuid = code.uuid
calcinfo.codes_info = [codeinfo]
return calcinfo
[docs] def _check_valid_parent(self, parent_calc_folder):
"""
Check that calc is a valid parent for a FleurCalculation.
It can be a VoronoiCalculation, KKRCalculation
"""
overwrite_pot = False
# extract parent calculation
parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation)
n_parents = len(parent_calcs)
if n_parents != 1:
raise UniquenessError("Input RemoteData is child of {} "
"calculation{}, while it should have a single parent"
"".format(n_parents, "" if n_parents == 0 else "s"))
else:
parent_calc = parent_calcs[0]
overwrite_pot = True
if ((not self._is_KkrCalc(parent_calc)) ):
raise ValueError("Parent calculation must be a KkrCalculation")
return overwrite_pot, parent_calc
[docs] def _is_KkrCalc(self, calc):
"""
check if calc contains the file out_potential
"""
is_KKR = False
ret = calc.get_retrieved_node()
ret_path = ret.get_abs_path()
ret_path = os.path.join(ret_path, 'path')
if 'out_potential' in os.listdir(ret_path):
is_KKR = True
return is_KKR
[docs] @classmethod
def _get_struc(self, parent_calc):
"""
Get structure from a parent_folder (result of a calculation, typically a remote folder)
"""
return parent_calc.inp.structure
[docs] @classmethod
def _has_struc(self, parent_folder):
"""
Check if parent_folder has structure information in its input
"""
success = True
try:
parent_folder.inp.structure
except:
success = False
if success:
print('struc found')
else:
print('no struc found')
return success
[docs] @classmethod
def _get_remote(self, parent_folder):
"""
get remote_folder from input if parent_folder is not already a remote folder
"""
parent_folder_tmp0 = parent_folder
try:
parent_folder_tmp = parent_folder_tmp0.inp.remote_folder
print('input has remote folder')
except:
#TODO check if this is a remote folder
parent_folder_tmp = parent_folder_tmp0
print('input is remote folder')
return parent_folder_tmp
[docs] @classmethod
def _get_parent(self, input_folder):
"""
get the parent folder of the calculation. If not parent was found return input folder
"""
input_folder_tmp0 = input_folder
try:
parent_folder_tmp = input_folder_tmp0.inp.parent_calc_folder
print('input has parent folder')
except:
try:
parent_folder_tmp = input_folder_tmp0.inp.parent_calc
print('input has parent folder')
except:
parent_folder_tmp = input_folder_tmp0
print('input is parent folder')
return parent_folder_tmp
[docs] @classmethod
def find_parent_structure(self, parent_folder):
"""
Find the Structure node recuresively in chain of parent calculations (structure node is input to voronoi calculation)
"""
iiter = 0
Nmaxiter = 100
parent_folder_tmp = self._get_remote(parent_folder)
while not self._has_struc(parent_folder_tmp) and iiter<Nmaxiter:
parent_folder_tmp = self._get_remote(self._get_parent(parent_folder_tmp))
iiter += 1
print(iiter)
if self._has_struc(parent_folder_tmp):
struc = self._get_struc(parent_folder_tmp)
return struc, parent_folder_tmp
else:
print('struc not found')