Source code for aiida_gromacs.calculations.genericMD

"""
Generic calculation used to track input and output files of a 
generic command.
"""
import os

from aiida.common import datastructures
from aiida.engine import CalcJob
from aiida.orm import List, SinglefileData, Str



[docs] class GenericCalculation(CalcJob): """ AiiDA calculation plugin wrapping an executable with user defined input and output files. """
[docs] @classmethod def define(cls, spec): """ AiiDA plugin wrapper for running an arbitrary command. """ # yapf: disable # defines the inputs and outputs that are common to all CalcJobs. super().define(spec) # calls define method of the CalcJob parent class # Define inputs and outputs of the calculation. spec.input('command', valid_type=Str, required=False, help='The command used to execute the job.') # dynamic dict of inputs instead of specifying each one. spec.input_namespace( 'input_files', valid_type=SinglefileData, required=False, help='Dictionary of input files.', dynamic=True, # can take num of values unknown at time of definition ) # set the list of output file names as an input so that it can be # iterated over in the parser later. spec.input('output_files', valid_type=List, required=False, help='List of output file names.') # define the schema for metadata.options spec.input('metadata.options.output_filename', valid_type=str, default='file.out', help='name of file produced by default.') spec.input('metadata.options.output_dir', valid_type=str, default=os.getcwd(), help='Directory where output files will be saved ' 'when parsed.') # set the computational resources used for this calculation. spec.inputs['metadata']['options']['resources'].default = { 'num_machines': 1, 'num_mpiprocs_per_machine': 1, } # set name of the default parser. spec.inputs['metadata']['options']['parser_name'].default = \ 'gromacs.genericMD' # spec.input('metadata.options.parser_name', # valid_type=Str, required=False, default=Str('genericMD'), # help='The name of the parser to use.') # ensure code is set spec.inputs['code'].required = True # IMPORTANT: # Use spec.outputs.dynamic = True to make the entire output namespace # fully dynamic. This means any number of output files # can be linked to a node. spec.outputs.dynamic = True spec.inputs.dynamic = True spec.inputs['metadata']['options'].dynamic = True # Used by parsers, which communicate errors through exit codes. spec.exit_code(300, 'ERROR_MISSING_OUTPUT_FILES', message='Calculation did not produce all expected '\ 'output files.') spec.exit_code(301, 'ERROR_UNTRACKED_OUTPUT_FILES', message='Specified output file not produced by command.')
[docs] def prepare_for_submission(self, folder): """ Create input files in the format the code external to AiiDA expects and return CalcInfo object that contains instructions for AiiDA engine on how the code should be run. :param folder: an `aiida.common.folders.Folder` where the plugin should temporarily place all files needed by the calculation. :return: `aiida.common.datastructures.CalcInfo` instance """ # create a CodeInfo object that lets AiiDA know how to run the code codeinfo = datastructures.CodeInfo() # split strings in command codeinfo.cmdline_params = str(self.inputs.command.value).split() # if a command uses bash as code, add -c before it to run # (allows gmx genion to be run for example) if self.inputs.code.label == "bash": codeinfo.cmdline_params = ["-c", self.inputs.command.value] # If an input redirection is included in the command, then remove # this and set the stdin_name as the filename used in the command if "<" in self.inputs.command.value: stdin_file = self.inputs.command.value.split()[-1] codeinfo.stdin_name = stdin_file codeinfo.cmdline_params = str(self.inputs.command.value.split('<')[0]).split() #codeinfo.cmdline_params = [] # the UUID of the AbstractCode to run codeinfo.code_uuid = self.inputs.code.uuid # redirect standard output to the specified output filename. codeinfo.stdout_name = self.metadata.options.output_filename # create a CalcInfo object that lets AiiDA know which files to # copy back and forth. calcinfo = datastructures.CalcInfo() calcinfo.codes_info = [codeinfo] # get a list of input files to be copied to remote. copy_list = [] if "input_files" in self.inputs: for name, obj in self.inputs.input_files.items(): copy_list.append((obj.uuid, obj.filename, obj.filename)) # input files are already stored in the AiiDA file repository # and we can use the local_copy_list to pass them along. calcinfo.local_copy_list = copy_list # The retrieve_list tells the engine which files to retrieve # from the directory where the job ran after it has finished. retrieve_list = [self.metadata.options.output_filename] if "output_files" in self.inputs: # check there are output files. for name in self.inputs.output_files: retrieve_list.append(str(name)) # save output filename to list calcinfo.retrieve_list = retrieve_list calcinfo.retrieve_temporary_list = retrieve_list return calcinfo