Skip to content

Commit

Permalink
Merge pull request #193 from smoors/cp2k_mixin
Browse files Browse the repository at this point in the history
use mixin class for cp2k
  • Loading branch information
casparvl authored Nov 13, 2024
2 parents 0ebe11b + 20631a4 commit d7800c7
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 62 deletions.
24 changes: 18 additions & 6 deletions eessi/testsuite/eessi_mixin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from reframe.core.builtins import parameter, run_after
from reframe.core.builtins import parameter, run_after, variable
from reframe.core.exceptions import ReframeFatalError
from reframe.core.pipeline import RegressionMixin
from reframe.utility.sanity import make_performance_function

from eessi.testsuite import hooks
from eessi.testsuite.constants import DEVICE_TYPES, SCALES, COMPUTE_UNIT
from eessi.testsuite.constants import DEVICE_TYPES, SCALES, COMPUTE_UNIT, TAGS


# Hooks from the Mixin class seem to be executed _before_ those of the child class
Expand All @@ -25,19 +25,21 @@ class EESSI_Mixin(RegressionMixin):
All EESSI tests should derive from this mixin class unless they have a very good reason not to.
To run correctly, tests inheriting from this class need to define variables and parameters that are used here.
That definition needs to be done 'on time', i.e. early enough in the execution of the ReFrame pipeline.
Here, we list which class attributes need to be defined by the child class, and by (the end of) what phase:
Here, we list which class attributes must be defined by the child class, and by (the end of) what phase:
- Init phase: device_type, scale, module_name
- Init phase: device_type, scale, module_name, bench_name (if bench_name_ci is set)
- Setup phase: compute_unit, required_mem_per_node
The child class may also overwrite the following attributes:
- Init phase: time_limit, measure_memory_usage
- Init phase: time_limit, measure_memory_usage, bench_name_ci
"""

# Set defaults for these class variables, can be overwritten by child class if desired
measure_memory_usage = False
measure_memory_usage = variable(bool, value=False)
scale = parameter(SCALES.keys())
bench_name = None
bench_name_ci = None

# Note that the error for an empty parameter is a bit unclear for ReFrame 4.6.2, but that will hopefully improve
# see https://github.com/reframe-hpc/reframe/issues/3254
Expand Down Expand Up @@ -113,6 +115,16 @@ def measure_mem_usage(self):
# instead of the @performance_function decorator
self.perf_variables['memory'] = make_performance_function(hooks.extract_memory_usage, 'MiB', self)

@run_after('init', always_last=True)
def set_tag_ci(self):
"Set CI tag if bench_name_ci and bench_name are set and are equal"
if self.bench_name_ci:
if not self.bench_name:
msg = "Attribute bench_name_ci is set, but bench_name is not set"
raise ReframeFatalError(msg)
if self.bench_name == self.bench_name_ci:
self.tags.add(TAGS['CI'])

@run_after('setup')
def validate_setup(self):
"""Check that all variables that have to be set for subsequent hooks in the setup phase have been set"""
Expand Down
74 changes: 18 additions & 56 deletions eessi/testsuite/tests/apps/cp2k/cp2k.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
from reframe.core.builtins import parameter, run_after, performance_function, sanity_function
import reframe.utility.sanity as sn

from eessi.testsuite import hooks
from eessi.testsuite.constants import SCALES, TAGS, COMPUTE_UNIT, DEVICE_TYPES, CPU
from eessi.testsuite.utils import find_modules, log
from eessi.testsuite.constants import SCALES, COMPUTE_UNIT, DEVICE_TYPES, CPU
from eessi.testsuite.eessi_mixin import EESSI_Mixin
from eessi.testsuite.utils import find_modules


@rfm.simple_test
class EESSI_CP2K(rfm.RunOnlyRegressionTest):
class EESSI_CP2K(rfm.RunOnlyRegressionTest, EESSI_Mixin):

benchmark_info = parameter([
# (bench_name, energy_ref, energy_tol)
Expand All @@ -24,8 +24,18 @@ class EESSI_CP2K(rfm.RunOnlyRegressionTest):

executable = 'cp2k.popt'
time_limit = '2h'
valid_systems = ['*']
valid_prog_environs = ['default']
device_type = DEVICE_TYPES[CPU]
compute_unit = COMPUTE_UNIT[CPU]
bench_name_ci = 'QS/H2O-32' # set CI on smallest benchmark

def required_mem_per_node(self):
mems = {
'QS/H2O-32': {'intercept': 0.5, 'slope': 0.15},
'QS/H2O-128': {'intercept': 5, 'slope': 0.15},
'QS/H2O-512': {'intercept': 34, 'slope': 0.20},
}
mem = mems[self.bench_name]
return (self.num_tasks_per_node * mem['slope'] + mem['intercept']) * 1024

@run_after('init')
def prepare_test(self):
Expand All @@ -51,39 +61,9 @@ def assert_energy(self):
def time(self):
return sn.extractsingle(r'^ CP2K(\s+[\d\.]+){4}\s+(?P<time>\S+)', self.stdout, 'time', float)

@run_after('init')
def run_after_init(self):
"""Hooks to run after the init phase"""

# Filter on which scales are supported by the partitions defined in the ReFrame configuration
hooks.filter_supported_scales(self)

# Make sure that GPU tests run in partitions that support running on a GPU,
# and that CPU-only tests run in partitions that support running CPU-only.
# Also support setting valid_systems on the cmd line.
hooks.filter_valid_systems_by_device_type(self, required_device_type=DEVICE_TYPES[CPU])

# Support selecting modules on the cmd line.
hooks.set_modules(self)

# Support selecting scales on the cmd line via tags.
hooks.set_tag_scale(self)

@run_after('init')
def set_tag_ci(self):
"""Set tag CI on smallest benchmark, so it can be selected on the cmd line via --tag CI"""
if self.bench_name == 'QS/H2O-32':
self.tags.add(TAGS['CI'])
log(f'tags set to {self.tags}')

@run_after('setup')
def run_after_setup(self):
"""Hooks to run after the setup phase"""

# Calculate default requested resources based on the scale:
# 1 task per CPU for CPU-only tests, 1 task per GPU for GPU tests.
# Also support setting the resources on the cmd line.
hooks.assign_tasks_per_compute_unit(test=self, compute_unit=COMPUTE_UNIT[CPU])
def skip_tests(self):
"""Skip tests that are not suited for the requested resources"""

# Skip QS/H2O-512 benchmark if not enough cores requested
min_cores = 16
Expand All @@ -94,21 +74,3 @@ def run_after_setup(self):
max_nodes = 8
self.skip_if(self.bench_name == 'QS/H2O-512' and self.num_nodes > max_nodes,
f'Skipping benchmark {self.bench_name}: more than {max_nodes} nodes requested ({self.num_nodes})')

# Set OMP_NUM_THREADS environment variable
hooks.set_omp_num_threads(self)

# Set compact process binding
hooks.set_compact_process_binding(self)

@run_after('setup')
def request_mem(self):
mems = {
'QS/H2O-32': {'intercept': 0.5, 'slope': 0.15},
'QS/H2O-128': {'intercept': 5, 'slope': 0.15},
'QS/H2O-512': {'intercept': 34, 'slope': 0.20},
}

mem = mems[self.bench_name]
mem_required = self.num_tasks_per_node * mem['slope'] + mem['intercept']
hooks.req_memory_per_node(self, app_mem_req=mem_required * 1024)

0 comments on commit d7800c7

Please sign in to comment.