Default parser change on fly

I am refactoring Quantum Package (QP) plugin. QP has a new feature Quantum Monte Carlo. when this type of run is executed the inputs look very same as for any other calculation, however, the outputs and mainly processing of the outputs is completly different. So my idea is to have one Calculator class, but two Parser classes.

My question is if there is way how to set as a default parser the QMC one if the QMC calculation is executed while otherwise the base one will be use. This thing should be owerwritable by builder.metadata.options.parser_name='...', So I dont think it is possible to do it in prepare_for_submition phase

Any ideas? Worst case scenario I will put both into one parser.

Alternativelly, I can just create a calcjob, but then I will have two calcjobs and have to pick correct one based on the inputs:

class QP2QmcchemRunCalculation(QP2RunCalculation):
    """ AiiDA calculation plugin wrapping the Quantum Package code.
    """

    @classmethod
    def define(cls, spec):
        """ Define inputs and outputs of the calculation."""
        # yapf: disable
        super().define(spec)

        spec.inputs['metadata']['options']['parser_name'].default = 'qp2.qmcchemrun'

The parser that is going to be used is defined in the process inputs itself, as you said in the metadata.options.parser_name input. Most CalcJobs define a default as you already showed. It is not possible to have the plugin override this value in the prepare_for_submission because at that point the process has already been created and the inputs are frozen. However, the user can specify a different parser when launching the CalcJob. So for example, you could set the default parser to the non-QMC one, and then when the user submits a QMC calculation, they do

inputs = {
    ...
    'metadata': {
        'options': {
            'parser_name': 'qp2.qmcchemrun'
        }
    }
}
submit(QP2RunCalculation, **inputs)

I would suggest another option. You write most of the parser logic in two parser functions, say parser_logic_1() and parser_logic_2(). But then, the actual “main” parser (say, MainParser) would do the following, when it starts:

  • check the input for the one/two flags that determine which kind of calculation you are running
  • delegate the correct parser function (parser_logic_1() or parser_logic_2()) to do the parsing job, and return the output of those.

Practically, you are defining a single parser, but keeping the logic well separated.
You can (if you really want/need) also define additional parser classes (e.g. Parser1 and Parser2, each calling only the corresponding parser (parser_logic_1() or parser_logic_2() respectively), in case you want to let the user select explicitly only one parser at submission time, using the approach that @sphuber suggested.

Up to you if you like this idea - in general, anyway, I prefer to keep the main parsing logic in a function by itself, independent of AiiDA (e.g. returning a dict with information of an output crystal structure), and then just call that function in that Parser class, to just wrap objects into corresponding AiiDA objects (e.g. convert the dictionary representation of a structure into a StructureData object)

Thank you @sphuber and @giovannipizzi

  • check the input for the one/two flags that determine which kind of calculation you are running
  • delegate the correct parser function (parser_logic_1() or parser_logic_2()) to do the parsing job, and return the output of those.

This is the solution I went with.