A portable test leaves the machine in the config¶
Questions
Why is
valid_systems = ['snellius']a trap?Where does a portable test read the core count from?
What does the EESSI mixin replace in the class body?
What you will be able to do
Point at the three lines that make a ReFrame test specific to one cluster, and say where each line belongs instead.
Read an EESSI mixin test and say what
device_typeandcompute_unitcause the hooks to set.Skip a test when the node cannot run it, rather than hardcoding a size that node will never have.
Before this chapter
The configuration file is the machine, for the configuration.
Parameters multiply the suite, for the product those hooks run on.
The mpi4py test in Parameters multiply the suite runs on Snellius and on no other system, due to three separate reasons:
valid_systems = ['snellius']
self.num_tasks_per_node = min(self.num_tasks, 128)
module_name = parameter([
'mpi4py/3.1.4-gompi-2023a',
'mpi4py/3.1.5-gompi-2023b',
])
The first binds the test to a single system name.
The second provides the system’s core count as a constant.
The third supplies two module strings present in that site’s tree.
A 64-core node running EESSI’s mpi4py/3.1.5-gompi-2023b still fails the first filter and oversubscribes when the name is patched manually.
EESSI’s writing-tests page presents a three-step tutorial: write the Slurm scripts for one machine, write the ReFrame test that encodes those scripts, then remove the machine.
The third step is a mixin.
The test class retains the workload.
The configuration retains the node.
What the mixin changes¶
@rfm.simple_test
class EESSI_MPI4PY(rfm.RunOnlyRegressionTest, EESSI_Mixin):
device_type = DEVICE_TYPES[CPU]
compute_unit = COMPUTE_UNIT[CPU]
module_name = parameter(find_modules('mpi4py'))
n_iterations = variable(int, value=1000)
executable = 'python3'
time_limit = '5m00s'
readonly_files = ['mpi4py_reduce.py']
def required_mem_per_node(self):
return self.num_tasks_per_node * 100 + 250
EESSI_Mixin provides hooks that read the partition.
The expression “One full node” corresponds to compute_unit = COMPUTE_UNIT[CPU], and the hook assigns num_tasks from processor.num_cpus in the configuration.
A node with 64 cores receives 64 tasks.
A node with 128 cores receives 128 tasks.
The test file lacks both numbers.
find_modules('mpi4py') queries the modules tool for what is loaded on this machine.
The parameter list is no longer two strings from last year’s Snellius tree.
A site with a single mpi4py module yields one case.
A site with four mpi4py modules yields four cases.
valid_systems is no longer a hostname.
The mixin requests the configuration features declared: CPU, GPU, and a minimum memory.
A partition that does not advertise those features skips the test.
That skip constitutes a result, and it is the correct result.
required_mem_per_node follows the same concept as a residual in Inspect writes a work queue, not a recipe.
The test defines a requirement.
The framework compares the requirement to the partition.
A mismatch results in a skip rather than a job that OOM‑kills the node.
The configuration is now the only machine file¶
The configuration file is the machine’s processor.num_cpus and features are the values read by these hooks.
A site that omits them produces tests that cannot compute a task count, causing them to skip or fall back to a conservative option.
A site that misrepresents the configuration causes tests to follow the same misrepresentation.
The portable suite reflects the accuracy of that file.
CSCS’s webinar demonstrates the same expansion: STREAM as a local binary, followed by the same class on any partition described by the configuration.
ExCALIBUR-tests applies this in item 3 of its outline, “Configuring ReFrame to run tests on HPC systems”, before creating a second test.
The order conveys the lesson.
The machine is entered into the configuration first.
EB-ReFrame-45-1 — What to move
A GROMACS test contains these four assignments. For each, say whether it stays in the test, moves to the configuration, or becomes a hook that reads the configuration.
executable = 'gmx'valid_systems = ['snellius:gpu']num_tasks = 128modules = ['GROMACS/2024.4-foss-2023b']
Solution
Stays. The workload names the binary.
Moves. The configuration marks the GPU partition with a
gpufeature. The test requests+gpu. Binding the cluster name makes every other site skip the test.Becomes a hook.
compute_unitof one node, or of one GPU, readsnum_cpusor the device count from the partition. 128 is Snellius.Becomes
parameter(find_modules('GROMACS')), or a singlefind_modulesresult if the suite is checking one installation. The version string is a fact about one tree.
What to remember
A cluster name, a core count and a module string in the test are three facts about one machine.
The mixin reads the partition. “One full node” is a request; the configuration supplies the number.
find_modulesbuilds the parameter list from the tree that is actually loaded.A missing feature or too little memory is a skip, not a failed job.