Custom Action Features

Overview

Questions

  • How do I access simulation state information?

  • How do I create loggable quantities in custom actions?

  • What are other features provided by the custom action/operation API?

Objectives

  • Explain how to access simulation state in a custom action.

  • Explain how to expose loggable quantities in a custom action.

  • Demonstrate other miscellaneous features of custom actions.

Boilerplate Code

[1]:
import hoomd

cpu = hoomd.device.CPU()
sim = hoomd.Simulation(device=cpu)

snap = hoomd.Snapshot()
snap.particles.N = 1
snap.particles.position[:] = [0, 0, 0]
snap.particles.types = ['A']
snap.particles.typeid[:] = [0]
snap.configuration.box = [10, 10, 10, 0, 0, 0]

sim.create_state_from_snapshot(snap)

How do I access simulation state?

By the time that a custom action will have its act method called it will have an attribute _state accessible to it which is the simulation state for the simulation it is associated with. The behavior of this is controlled in the hoomd.custom.Action.attach method. The method takes in a simulation object and performs any necessary set-up for the action call act. By default, the method stores the simulation state in the _state attribute.

We will create two custom actions class to show this. In one, we will not modify the attach method, and in the other we will make attach method also print out some information.

[2]:
class PrintTimestep(hoomd.custom.Action):

    def act(self, timestep):
        print(timestep)


class NotifyAttachWithPrint(hoomd.custom.Action):

    def attach(self, simulation):
        print(f"Has '_state' attribute {hasattr(self, '_state')}.")
        super().attach(simulation)
        print(f"Has '_state' attribute {hasattr(self, '_state')}.")

    def act(self, timestep):
        print(timestep)

Like in the previous section these are both writers. We will go ahead and wrap them and see what happens when we try to run the simulation.

[3]:
print_timestep = PrintTimestep()
print_timestep_operation = hoomd.write.CustomWriter(
    action=print_timestep, trigger=hoomd.trigger.Periodic(10))
sim.operations.writers.append(print_timestep_operation)
sim.run(0)
[4]:
sim.operations -= print_timestep_operation
print_timestep_with_notify = NotifyAttachWithPrint()
sim.operations.writers.append(
    hoomd.write.CustomWriter(action=print_timestep_with_notify,
                             trigger=hoomd.trigger.Periodic(10)))
sim.run(0)
Has '_state' attribute False.
Has '_state' attribute True.

Loggable Quantities in Custom Actions

Custom actions can hook into HOOMD-blue’s logging subsystem by using the hoomd.logging.log decorator to document which methods/properties of a custom action are loggable. See the documentation on hoomd.logging.log and hoomd.logging.TypeFlags for complete documenation of the decorator and loggable types.

In general, log as a decorator takes optional arguments that control whether to make a method a property, what type the loggable quantity is, and whether the quantity should be logged by default.

Rather than elaborate, we will use an example to explain these attributes.

[5]:
class ActionWithLoggables(hoomd.custom.Action):

    @hoomd.logging.log
    def scalar_property_loggable(self):
        return 42

    @hoomd.logging.log(category='string')
    def string_loggable(self):
        return "I am a string loggable."

    def act(self, timestep):
        pass


action = ActionWithLoggables()
[6]:
action.scalar_property_loggable
[6]:
42
[7]:
action.string_loggable
[7]:
'I am a string loggable.'

Custom Operation Wrapping

Another feature of the custom action API is that when an object is wrapped by a custom operation object (which is necessary to add a custom action to a simulation), the action’s attributes are available through the operation object as if the operation were the action. For example, we will wrap action from the previous code block in a CustomWriter and access its attributes that way.

Due to this wrapping the attribute trigger should not exist in your custom action.

[8]:
custom_op = hoomd.write.CustomWriter(action=action, trigger=100)
custom_op.scalar_property_loggable
[8]:
42
[9]:
custom_op.string_loggable
[9]:
'I am a string loggable.'

Summary

These summarize most of the unique features of custom actions in Python. They are - Accessing simulation state through _state - Exposing loggable quantities - Accessing action attributes through custom operation wrapper

With this information, you could write almost any action that is possible to write in Python for use in HOOMD-blue. The remain tutorial sections will focus on concrete examples and show some tricks to get the best performance. For the full list of accessible features for custom actions see the reference documentation.