Modifier Definition Developers Guide
Modifier definition files represent standardized objects which can be composed
with applications and experiments to modify their behavior. These can
encapsulate many types of changes, including collecting additional system
information (such as the output of lscpu) or injecting a performance
analysis tool into the experiment workflow.
This guide will provide general steps for creating a new modifier definition file.
NOTE: Modifiers are considered a more advanced feature of Ramble. Writing a new modifier definition file in Ramble largely follows the same workflow as writing an application definition file, although the intent and behavior are different. It is recommended that you review application-def-guide before writing a modifier.
Preparation
Before writing a modifier definition file, it is helpful to determine what aspects of the modification need to be grouped together.
It can be useful to think of a modifier as some pattern you would like to apply to many experiments, that doesn’t actually have anything to do with the behavior of an application directly. Depending on your goal for modifiers and the application of these patterns, you could write one modifier to apply all of the changes, or encapsulate a smaller set of changes into many modifiers.
Some of the information you need to gather before writing your modifier include:
Commands you need to execute as part of your changes
Environment variables that need to be changed (set, appended, prepended, or unset)
An example of output the modifier would create (if any)
Compilation / Installation
Some modifiers require specific software packages to be available to function properly. Modifiers can define software packages that will be install, or required to be available when a supported package manager is used. See the Compilation / Installation section of the application definition developers guide.
Testing execution and Output information
For this step, it can be useful to manually execute the commands / steps you want to encapsulate into a modifier. This step helps you understand how the modification will actually behave, and can be used to get example output to create modifier figures of merit and success criteria.
Modifier Definition Creation
Modifier definition files are stored within object repositories. These
repositories generally store modifiers within a directory named modifiers.
Individual repositories can control this through their own config file (
repo.yaml or modifier_repo.yaml ).
Within the repository, each modifier definition file is a python module that is
stored within a directory named for the modifier. As an example, Ramble comes
with a repository named builtin. This repository contains several standard
modifier definitions that are provided to the community. One of the modifier
definition files provided is
lscpu.
The lscpu modifier definition file is named modifier.py and is stored
within a directory named lscpu. Within the modifier.py file, a python
class is defined with a similar name to the modifier directory. Ramble’s
modifier definition naming syntax follows
Spack’s package naming rules.
Base Classes
Ramble provides base classes which can be inherited when creating new modifier
definition files. These encapsulate most of the basic modifier functionality,
and allow new modifiers to function with only a little syntax. These can be
seen in more detail in ramble.modifier_types.
New modifier definitions can also inherit their behavior from other modifier classes to replicate aspects of their behavior.
Existing modifier classes can be referenced using the:
from ramble.mod.builtin.<modifier_name> import <modifier_class> syntax.
Writing a modifier definition
After a modifier’s modifier.py file is created, Ramble’s language features
can be used to define the bahvior of the modifier. These language features
provide directives which define specific portions of the modifier’s
functionality.
There are many language features available to modifiers, and the ones you use
will change based on the specific modifier you need to implement. Some of the
language features are shared with applications and package managers, but some
are specific to modifiers. For more information see ramble.language.
The directives from Ramble’s modifier language are placed alongside class variables, as in:
class Lscpu(BasicModifier):
mode(...)
default_mode(...)
modifier_variable(...)
variable_modification(...)
register_builtin(...)
register_phase(...)
executable_modification(...)
env_var_modification(...)
Modes
Modifiers can have multiple modes which are used to change the behavior of
the modifier. This can be helpful if the general modifier stays the same, but
some aspects of the modifier change under different usage models.
If a modifier only has a single mode defined, this becomes the default
mode. The default mode can be specified using the default_mode directive.
Every modifier has a disabled mode added by default, which cannot be passed
into the default_mode directive. This mode allows turning off modifiers
without having to remove them from the workspace configuration file.
Modifier Variables and Variable Modifications
Sometimes a modifier needs to define or manipulate variable definitions inside an
experiment. This could include something like adding arguments to a command
(like mpirun) or defining new variables entirely.
The modifier_variable and variable_modification directives can be used
to define or edit variables within experiments.
Builtins
A builtin is a specific command that is injected within an experiment. Builtins
can have dependencies, and and be injected either at the beginning or end of
the experiments. Builtins are written as class methods that return a list of
strings which are explicit commands to add into an experiment. The
register_builtin directive can be used to add a builtin into an experiment.
Phase Registration
In Ramble there are several different pipelines which are groupings of
phases to perform a specific action (such as setup or analyze). Some
modifiers need to inject phases into one or more of these pipelines. The
register_phase directive can be used to add a phase into a pipeline. Phases
are written as class methods with a specific signature, and will be
automatically executed as part of the pipeline they belong to.
Executable Modification
One of the most powerful modifications available within modifier definitions is
the executable_modifier directive . Some modifiers will require the
ability to inject commands around commands that exist in the experiments
already. A good example of this is a performance analysis tool, which needs to
modify the execution command to profile the experiment, and generate a summary
of the performance characteristics of the experiment after it is complete.
To accomplish this goal, the executable_modifier directive can be used,
which is implemented as a class method which returns two lists of
CommandExecutable objects, which are injected before and after each
executable.
Environment Variable Modification
Some modifiers need to edit environment variables within an experiment. The
env_var_modification directive can be used to change existing environment
variables.
Testing a Modifier
Modifiers are added into experiments using the modifiers configuration section.
Modifiers are used within dry-run pipeline executions in Ramble. As an
example, it can be useful to verify the behvior of the modifier is functioning
correctly by using ramble workspace setup --dry-run. The output from the
preparation steps can be copied into the experiment directory to verify the
ramble workspace analyze pipeline works, without having to execute the
experiment itself.