# -*- coding: utf-8 -*-
"""
Input plug-in for a voronoi calculation.
"""
from __future__ import print_function
from __future__ import absolute_import
from aiida.engine import CalcJob
from aiida.orm import CalcJobNode, Dict, StructureData, RemoteData, SinglefileData
from aiida.common.utils import classproperty
from aiida.common.exceptions import (InputValidationError, ValidationError)
from aiida.common.datastructures import (CalcInfo, CodeInfo)
from aiida_kkr.tools.common_workfunctions import generate_inputcard_from_structure, check_2Dinput_consistency, vca_check
from aiida.common.exceptions import UniquenessError
import os
import six
__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'
__contributors__ = ('Jens Broeder', 'Philipp Rüßmann')
[docs]class VoronoiCalculation(CalcJob):
"""
AiiDA calculation plugin for a voronoi calculation (creation of starting potential and shapefun).
"""
####################
# File names etc.
####################
# calculation plugin version
_CALCULATION_PLUGIN_VERSION = __version__
# Default input and output files
_DEFAULT_INPUT_FILE = 'inputcard' # will be shown with inputcat
_DEFAULT_OUTPUT_FILE = 'out_voronoi' #'shell output will be shown with outputca
# List of mandatory input files
_INPUT_FILE_NAME = 'inputcard'
# List of output files that should always be present
_OUTPUT_FILE_NAME = 'out_voronoi'
# template.product entry point defined in setup.json
_default_parser = 'kkr.voroparser'
# File names
_ATOMINFO = 'atominfo.txt'
_RADII = 'radii.dat'
_SHAPEFUN = 'shapefun'
_VERTICES = 'vertices.dat'
_OUT_POTENTIAL_voronoi = 'output.pot'
_POTENTIAL_IN_OVERWRITE = 'overwrite_potential'
[docs] @classmethod
def define(cls, spec):
"""
define internals and inputs / outputs of calculation
"""
# reuse base class (i.e. CalcJob) functions
super(VoronoiCalculation, cls).define(spec)
# now define input files and parser
spec.input(
'metadata.options.parser_name', valid_type=six.string_types, default=cls._default_parser, non_db=True
)
spec.input(
'metadata.options.input_filename',
valid_type=six.string_types,
default=cls._DEFAULT_INPUT_FILE,
non_db=True
)
spec.input(
'metadata.options.output_filename',
valid_type=six.string_types,
default=cls._DEFAULT_OUTPUT_FILE,
non_db=True
)
# define input nodes (optional ones have required=False)
spec.input('parameters', valid_type=Dict, help='Use a node that specifies the input parameters')
spec.input(
'structure',
valid_type=StructureData,
required=False,
help='Use a node that specifies the input crystal structure'
)
spec.input(
'parent_KKR',
valid_type=RemoteData,
required=False,
help='Use a node that specifies a parent KKR calculation'
)
spec.input(
'potential_overwrite',
valid_type=SinglefileData,
required=False,
help='Use a node that specifies the potential which is used instead of the voronoi output potential'
)
# define outputs
spec.output('output_parameters', valid_type=Dict, required=True, help='results of the calculation')
spec.default_output_node = 'output_parameters'
# define exit codes, also used in parser
spec.exit_code(301, 'ERROR_NO_OUTPUT_FILE', message='Voronoi output file not found')
spec.exit_code(302, 'ERROR_VORONOI_PARSING_FAILED', message='Voronoi parser retuned an error')
[docs] def prepare_for_submission(self, tempfolder):
"""Create the input files from the input nodes passed to this instance of the `CalcJob`.
:param tempfolder: an `aiida.common.folders.Folder` to temporarily write files on disk
:return: `aiida.common.datastructures.CalcInfo` instance
"""
# Check inputdict
parameters = self.inputs.parameters
if 'structure' in self.inputs:
structure = self.inputs.structure
found_structure = True
else:
found_structure = False
vca_structure = False
if found_structure:
# for VCA: check if input structure and parameter node define VCA structure
vca_structure = vca_check(structure, parameters)
code = self.inputs.code
# check if a parent folder containing a potential file (out_potential) is given
if 'parent_KKR' in self.inputs:
parent_calc_folder = self.inputs.parent_KKR
found_parent = True
else:
found_parent = False
if found_parent:
# 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 and not vca_structure:
raise InputValidationError(
'parent_KKR and structure found in input. '
'Can only use either parent_KKR or structure in input.'
)
else:
structure_remote_KKR, voro_parent = self.find_parent_structure(parent_calc)
if not vca_structure:
structure = structure_remote_KKR
else:
# check consistency of input vca structure and structure from remote KKR folder
# TODO check consistency
pass
else:
overwrite_potential = False
if not found_structure:
raise InputValidationError('Neither structure nor parent_KKR specified for this '
'calculation')
# check if overwrite potential is given explicitly
if 'potential_overwrite' in self.inputs:
potfile_overwrite = self.inputs.potential_overwrite
has_potfile_overwrite = True
else:
has_potfile_overwrite = False
if has_potfile_overwrite:
overwrite_potential = True
if not found_structure:
raise InputValidationError(
'Input structure needed for this calculation '
"(using 'potential_overwrite' input node)"
)
###################################
# 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
with tempfolder.open(self._INPUT_FILE_NAME, u'w') as input_file:
try:
use_alat_input = parameters.get_dict().get('use_input_alat', False)
use_alat_input = parameters.get_dict().get('USE_INPUT_ALAT', use_alat_input)
natom, nspin, newsosol, warnings_write_inputcard = generate_inputcard_from_structure(
parameters,
structure,
input_file,
isvoronoi=True,
vca_structure=vca_structure,
use_input_alat=use_alat_input,
)
except ValueError as e:
raise InputValidationError(f'Input Dict not consistent: {e}')
# Decide what files to copy
local_copy_list = []
if overwrite_potential:
# 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
copylist = [parent_calc.process_class._OUT_POTENTIAL]
elif has_potfile_overwrite:
outfolder = potfile_overwrite # copy from potential sfd
copylist = [potfile_overwrite.filename]
else:
copylist = []
for file1 in copylist:
filename = file1
if (found_parent or has_potfile_overwrite) and file1 == copylist[0]:
filename = self._POTENTIAL_IN_OVERWRITE
local_copy_list.append((outfolder.uuid, file1, 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_incoming(node_class=CalcJobNode)
n_parents = len(parent_calcs.all_link_labels())
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.first().node
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
if calc.process_type == 'aiida.calculations:kkr.kkr':
retrieved_node = calc.get_retrieved_node()
if 'out_potential' in retrieved_node.list_object_names():
is_KKR = True
return is_KKR
[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)
This is a copy of the find_parent_structure that moved to tools.find_parent to keep backwards compatibility.
"""
from aiida_kkr.tools.find_parent import find_parent_structure
return find_parent_structure(parent_folder)