# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved. #
# This file is part of the AiiDA code. #
# #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################
"""
This is a simple plugin that takes two node inputs, both of type ParameterData,
with the following labels: template and parameters.
You can also add other SinglefileData nodes as input, that will be copied according to
what is written in 'template' (see below).
* parameters: a set of parameters that will be used for substitution.
* template: can contain the following parameters:
* input_file_template: a string with substitutions to be managed with the format()\
function of python, i.e. if you want to substitute a variable called 'varname', you write\
{varname} in the text. See http://www.python.org/dev/peps/pep-3101/ for more\
details. The replaced file will be the input file.
* input_file_name: a string with the file name for the input. If it is not provided, no\
file will be created.
* output_file_name: a string with the file name for the output. If it is not provided, no\
redirection will be done and the output will go in the scheduler output file.
* cmdline_params: a list of strings, to be passed as command line parameters.\
Each one is substituted with the same rule of input_file_template. Optional
* input_through_stdin: if True, the input file name is passed via stdin. Default is\
False if missing.
* files_to_copy: if defined, a list of tuple pairs, with format ('link_name', 'dest_rel_path');\
for each tuple, an input link to this calculation is looked for, with link labeled 'link_label',\
and with file type 'Singlefile', and the content is copied to a remote file named 'dest_rel_path'\
Errors are raised in the input links are non-existent, or of the wrong type, or if there are \
unused input files.
TODO: probably use Python's Template strings instead??
TODO: catch exceptions
"""
from aiida.orm.calculation.job import JobCalculation
from aiida.common.exceptions import InputValidationError
from aiida.common.datastructures import CalcInfo, CodeInfo
from aiida.common.utils import classproperty
from aiida.orm.data.parameter import ParameterData
# TODO: write a 'input_type_checker' routine to automatically check the existence
# and type of inputs + default values etc.
[docs]class TemplatereplacerCalculation(JobCalculation):
"""
Simple stub of a plugin that can be used to replace some text in a given
template. Can be used for many different codes, or as a starting point
to develop a new plugin.
"""
@classproperty
def _use_methods(cls):
retdict = JobCalculation._use_methods
retdict.update({
"template": {
'valid_types': ParameterData,
'additional_parameter': None,
'linkname': 'template',
'docstring': "A template for the input file",
},
"parameters": {
'valid_types': ParameterData,
'additional_parameter': None,
'linkname': 'parameters',
'docstring': "Parameters used to replace placeholders in the template",
},
})
return retdict
def _prepare_for_submission(self, tempfolder, inputdict):
"""
This is the routine to be called when you want to create
the input files and related stuff with a plugin.
:param tempfolder: a aiida.common.folders.Folder subclass where
the plugin should put all its files.
:param inputdict: a dictionary with the input nodes, as they would
be returned by get_inputs_dict (with the Code!)
"""
import StringIO
from aiida.orm.data.parameter import ParameterData
from aiida.orm.data.singlefile import SinglefileData
from aiida.orm.data.remote import RemoteData
from aiida.common.utils import validate_list_of_string_tuples
from aiida.common.exceptions import ValidationError
parameters_node = inputdict.pop('parameters', None)
if parameters_node is None:
parameters = {}
else:
parameters = parameters_node.get_dict()
template_node = inputdict.pop('template', None)
template = template_node.get_dict()
input_file_template = template.pop('input_file_template', "")
input_file_name = template.pop('input_file_name', None)
output_file_name = template.pop('output_file_name', None)
cmdline_params_tmpl = template.pop('cmdline_params', [])
input_through_stdin = template.pop('input_through_stdin', False)
files_to_copy = template.pop('files_to_copy', [])
if template:
raise InputValidationError("The following keys could not be "
"used in the template node: {}".format(
template.keys()))
try:
validate_list_of_string_tuples(files_to_copy, tuple_length=2)
except ValidationError as e:
raise InputValidationError("invalid file_to_copy format: {}".format(e.message))
local_copy_list = []
remote_copy_list = []
for link_name, dest_rel_path in files_to_copy:
try:
fileobj = inputdict.pop(link_name)
except KeyError:
raise InputValidationError("You are asking to copy a file link {}, "
"but there is no input link with such a name".format(link_name))
if isinstance(fileobj, SinglefileData):
local_copy_list.append((fileobj.get_file_abs_path(), dest_rel_path))
elif isinstance(fileobj, RemoteData): # can be a folder
remote_copy_list.append(
(fileobj.get_computer().uuid, fileobj.get_remote_path(), dest_rel_path)
)
else:
raise InputValidationError("If you ask to copy a file link {}, "
"it must be either a SinglefileData or a RemoteData; it is instead of type {}".format(
link_name, fileobj.__class__.__name__))
code = inputdict.pop('code', None)
if code is None:
raise InputValidationError("No code in input")
if len(inputdict) > 0:
raise InputValidationError("The input nodes with the following labels could not be "
"used by the templatereplacer plugin: {}".format(
inputdict.keys()))
if input_file_name is not None and not input_file_template:
raise InputValidationError("If you give an input_file_name, you "
"must also specify a input_file_template")
if input_through_stdin and input_file_name is None:
raise InputValidationError("If you ask for input_through_stdin you have to "
"specify a input_file_name")
input_file = StringIO.StringIO(input_file_template.format(**parameters))
if input_file_name:
tempfolder.create_file_from_filelike(input_file, input_file_name)
else:
if input_file_template:
self.logger.warning("No input file name passed, but a input file template is present")
cmdline_params = [i.format(**parameters) for i in cmdline_params_tmpl]
calcinfo = CalcInfo()
calcinfo.retrieve_list = []
calcinfo.uuid = self.uuid
calcinfo.local_copy_list = local_copy_list
calcinfo.remote_copy_list = remote_copy_list
codeinfo = CodeInfo()
codeinfo.cmdline_params = cmdline_params
if input_through_stdin is not None:
codeinfo.stdin_name = input_file_name
if output_file_name:
codeinfo.stdout_name = output_file_name
calcinfo.retrieve_list.append(output_file_name)
codeinfo.code_uuid = code.uuid
calcinfo.codes_info = [codeinfo]
return calcinfo