Source code for aiida.orm.calculation.job.simpleplugins.templatereplacer

# -*- 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               #
###########################################################################
from aiida.common.exceptions import InputValidationError
from aiida.common.datastructures import CalcInfo, CodeInfo
from aiida.common.utils import classproperty
from aiida.orm.calculation.job import JobCalculation
from aiida.orm.data.parameter import ParameterData


[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. This simple plugin takes two node inputs, both of type ParameterData, with the labels 'parameters' and 'template' 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. * retrieve_temporary_files: a list of relative filepaths, that if defined, will be retrieved and temporarily stored in an unstored FolderData node that will be available during the Parser.parser_with_retrieved call under the key specified by the Parser.retrieved_temporary_folder key TODO: probably use Python's Template strings instead?? TODO: catch exceptions TODO: write a 'input_type_checker' routine to automatically check the existence and type of inputs + default values etc. """ @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.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', []) retrieve_temporary_files = template.pop('retrieve_temporary_files', []) 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.retrieve_temporary_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) if retrieve_temporary_files: calcinfo.retrieve_temporary_list = retrieve_temporary_files codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] return calcinfo