Skip to content

Commit

Permalink
ProcessBuilder: Include metadata inputs in get_builder_restart
Browse files Browse the repository at this point in the history
The `get_builder_restart` method on the `ProcessNode` base class would
return a `ProcessBuilder` with the inputs set to those attached to that
node instance. The `CalcJobNode` would override this to add the metadata
options as well. This would be ok for `CalcJobNode`s, but if a restart
builder was created from a `WorkChainNode` that calls a `CalcJobNode` it
would have also received options, but those would not be restored. One
could think that when calling `get_restart_builder` on a `WorkChainNode`
that we can just go down the callstack, find all the `CalcJobNode`s and
set the options in the respective input namespaces. But this would not
exactly reproduce the original inputs, as the options that a calculation
job has received could have been a modified version of the original
options passed to the workchain, changed in the logic of the workchain.

Instead, now that the exact metadata inputs for each process are stored
in the attribute of the node, added in the previous commit, it is this
dictionary that is used to restore the exact original inputs. It not
only addresses the problem of incorrect `CalcJob` options, but it also
restores any other metadata inputs such as the `label` and `description`.
  • Loading branch information
sphuber committed Dec 14, 2022
1 parent 6577228 commit a0cf2ba
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 32 deletions.
15 changes: 0 additions & 15 deletions aiida/orm/nodes/process/calculation/calcjob.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,6 @@ def _hash_ignored_attributes(cls) -> Tuple[str, ...]: # pylint: disable=no-self
'max_memory_kb',
)

def get_builder_restart(self) -> 'ProcessBuilder':
"""Return a `ProcessBuilder` that is ready to relaunch the same `CalcJob` that created this node.
The process class will be set based on the `process_type` of this node and the inputs of the builder will be
prepopulated with the inputs registered for this node. This functionality is very useful if a process has
completed and you want to relaunch it with slightly different inputs.
In addition to prepopulating the input nodes, which is implemented by the base `ProcessNode` class, here we
also add the `options` that were passed in the `metadata` input of the `CalcJob` process.
"""
builder = super().get_builder_restart()
builder.metadata.options = self.get_options() # type: ignore[attr-defined]
return builder

@property
def is_imported(self) -> bool:
"""Return whether the calculation job was imported instead of being an actual run."""
Expand Down
39 changes: 22 additions & 17 deletions tests/engine/processes/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from aiida import orm
from aiida.common import LinkType
from aiida.engine import Process, WorkChain
from aiida.engine import Process, WorkChain, run_get_node
from aiida.engine.processes.builder import ProcessBuilderNamespace
from aiida.plugins import CalculationFactory

Expand Down Expand Up @@ -289,27 +289,32 @@ def test_port_names_overlapping_mutable_mapping_methods(): # pylint: disable=in
assert builder.boolean == orm.Bool(False)


def test_calc_job_node_get_builder_restart(aiida_localhost):
def test_calc_job_node_get_builder_restart(aiida_local_code_factory):
"""Test the `CalcJobNode.get_builder_restart` method."""
original = orm.CalcJobNode(
computer=aiida_localhost, process_type='aiida.calculations:core.arithmetic.add', label='original'
)
original.set_option('resources', {'num_machines': 1, 'num_mpiprocs_per_machine': 1})
original.set_option('max_wallclock_seconds', 1800)

original.base.links.add_incoming(orm.Int(1).store(), link_type=LinkType.INPUT_CALC, link_label='x')
original.base.links.add_incoming(orm.Int(2).store(), link_type=LinkType.INPUT_CALC, link_label='y')
original.store()
code = aiida_local_code_factory('core.arithmetic.add', '/bin/bash')
inputs = {
'metadata': {
'label': 'some-label',
'description': 'some-description',
'options': {
'resources': {
'num_machines': 1,
'num_mpiprocs_per_machine': 1
},
'max_wallclock_seconds': 1800
}
},
'x': orm.Int(1),
'y': orm.Int(2),
'code': code,
}

builder = original.get_builder_restart()
_, node = run_get_node(CalculationFactory('core.arithmetic.add'), **inputs)
builder = node.get_builder_restart()

assert 'x' in builder
assert 'y' in builder
assert 'metadata' in builder
assert 'options' in builder.metadata
assert builder.x == orm.Int(1)
assert builder.y == orm.Int(2)
assert builder._inputs(prune=True)['metadata']['options'] == original.get_options()
assert builder._inputs(prune=True)['metadata'] == inputs['metadata']


def test_code_get_builder(aiida_localhost):
Expand Down

0 comments on commit a0cf2ba

Please sign in to comment.