Protocol Groups

The ProtocolGroup class represents a collection of protocols which have been grouped together. All protocols within a group will be executed together on a single compute resources, i.e. there is currently no support for executing protocols within a group in parallel.

Protocol groups have a specialised ProtocolGroupSchema which is essentially a collection of ProtocolSchema objects.

Conditional Protocol Groups

A ConditionalGroup is a special class of ProtocolGroup which will execute all of the grouped protocols again and again until a set of conditions has been met or until a maximum number of iterations (see max_iterations) has been performed. They can be thought of as being a protocol representation of a while statement.

Each condition to be met is represented by a Condition object:

condition = ConditionalGroup.Condition()

# Set the left and right hand values.
condition.left_hand_value = ...
condition.right_hand_value = ...

# Choose the type of condition
condition.type = ConditionalGroup.Condition.Type.LessThan

The left and right hand values can either be constants, or come from the output of another protocol (including grouped protocols) using a ProtocolPath. Currently a condition can either check that a value is less than or greater than another value.

Conditional groups expose a current_iteration attribute which tracks how many times the grouped protocols have been executed. This can be used as input by any of the grouped protocols and is useful, for example, to run a simulation for longer and longer until the groups condition has been met:

conditional_group = ConditionalGroup("conditional_group")

# Set up protocols to run a simulation and then to extract the
# value of the density and its uncertainty.
simulation = OpenMMSimulation("simulation")
simulation.input_coordinate_file = "coords.pdb"
simulation.parameterized_system = ...

extract_density = AverageObservable("extract_density")
extract_density.observable = simulation.observables["Density"]

# Set the total number of iterations the simulation should perform to be equal
# to the current iteration of the group. I.e the simulation should perform a
# new iteration at each group iteration.
simulation.total_number_of_iterations = ProtocolPath(
    "current_iteration", conditional_group.id
)

# Add the protocols to the group.
conditional_group.add_protocols(production_simulation, analysis_protocol)

# Set up a condition which will check if the uncertainty is less than
# some threshold.
condition = ConditionalGroup.Condition()
condition.condition_type = groups.ConditionalGroup.Condition.Type.LessThan

condition.right_hand_value = 0.5 * unit.gram / unit.millilitre
condition.left_hand_value = ProtocolPath(
    "value.error", conditional_group.id, analysis_protocol.id
)

# Add the condition.
conditional_group.add_condition(condition)

It is this idea which is used to continue running a molecular simulations until an observable of interest (such as the density) has been calculated to within a specified uncertainty.