Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 76 additions & 89 deletions cscs-checks/apps/namd/namd_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,80 @@

import reframe as rfm
import reframe.utility.sanity as sn
from hpctestlib.apps.namd.base_check import Namd_BaseCheck

REFERENCE_GPU_PERFORMANCE = {
'dom:gpu': {
'small': (0.15, None, 0.05, 'days/ns'),
},
'daint:gpu': {
'small': (0.15, None, 0.05, 'days/ns'),
'large': (0.07, None, 0.05, 'days/ns')
},
}

@rfm.simple_test
class NamdCheck(rfm.RunOnlyRegressionTest):
scale = parameter(['small', 'large'])
variant = parameter(['maint', 'prod'])
arch = parameter(['gpu', 'cpu'])
REFERENCE_CPU_PERFORMANCE = {
'dom:mc': {
'small': (0.51, None, 0.05, 'days/ns'),
},
'daint:mc': {
'small': (0.51, None, 0.05, 'days/ns'),
'large': (0.28, None, 0.05, 'days/ns')
},
'eiger:mc': {
'small': (0.12, None, 0.05, 'days/ns'),
'large': (0.05, None, 0.05, 'days/ns')
},
'pilatus:mc': {
'small': (0.12, None, 0.05, 'days/ns'),
'large': (0.05, None, 0.05, 'days/ns')
},
}

valid_prog_environs = ['builtin', 'cpeIntel']
modules = ['NAMD']
executable = 'namd2'
use_multithreading = True
num_tasks_per_core = 2

@rfm.simple_test
class NamdCheckCSCS(Namd_BaseCheck):
maintainers = ['CB', 'LM']
tags = {'scs', 'external-resources'}
modules = ['NAMD']
extra_resources = {
'switches': {
'num_switches': 1
}
}
use_multithreading = True
num_tasks_per_core = 2
platform_name = parameter(['gpu', 'cpu'])
scale = parameter(['small', 'large'])
mode = parameter(['maint', 'prod'])

@run_after('init')
def adapt_description(self):
self.descr = f'NAMD check ({self.arch}, {self.variant})'
self.tags |= {
'maintenance' if self.variant == 'maint' else 'production'
}
def env_define(self):
if self.current_system.name in ['eiger', 'pilatus']:
self.valid_prog_environs = ['cpeGNU']
else:
self.valid_prog_environs = ['builtin']

@run_after('init')
def adapt_valid_systems(self):
if self.arch == 'gpu':
self.valid_systems = ['daint:gpu']
if self.scale == 'small':
self.valid_systems += ['dom:gpu']
else:
self.valid_systems = ['daint:mc', 'eiger:mc', 'pilatus:mc']
if self.scale == 'small':
self.valid_systems += ['dom:mc']
def set_description(self):
self.mydescr = f'NAMD check ({self.platform_name}, {self.mode})'

@run_after('init')
def adapt_valid_prog_environs(self):
if self.current_system.name == 'pilatus':
self.valid_prog_environs.remove('builtin')
else:
self.valid_prog_environs.remove('cpeIntel')
def set_tags(self):
self.tags |= {'maintenance' if self.mode == 'maint'
else 'production'}

@run_after('init')
def setup_parallel_run(self):
if self.arch == 'gpu':
self.executable_opts = ['+idlepoll', '+ppn 23', 'stmv.namd']
self.num_cpus_per_task = 24
self.num_gpus_per_node = 1
def set_valid_systems(self):
if self.platform_name == 'gpu':
self.valid_systems = ['daint:gpu']
else:
# On Eiger a no-smp NAMD version is the default
if self.current_system.name in ['eiger', 'pilatus']:
self.executable_opts = ['+idlepoll', 'stmv.namd']
else:
self.executable_opts = ['+idlepoll', '+ppn 71', 'stmv.namd']
self.num_cpus_per_task = 72
self.valid_systems = ['daint:mc',
'eiger:mc',
'pilatus:mc']

@run_after('setup')
def set_num_tasks(self):
if self.scale == 'small':
# On Eiger a no-smp NAMD version is the default
if self.current_system.name in ['eiger', 'pilatus']:
Expand All @@ -82,56 +97,28 @@ def setup_parallel_run(self):
self.num_tasks = 16
self.num_tasks_per_node = 1

@run_before('compile')
def prepare_build(self):
# Reset sources dir relative to the SCS apps prefix
self.sourcesdir = os.path.join(self.current_system.resourcesdir,
'NAMD', 'prod')

@sanity_function
def validate_energy(self):
energy = sn.avg(sn.extractall(
r'ENERGY:([ \t]+\S+){10}[ \t]+(?P<energy>\S+)',
self.stdout, 'energy', float)
)
energy_reference = -2451359.5
energy_diff = sn.abs(energy - energy_reference)
return sn.all([
sn.assert_eq(sn.count(sn.extractall(
r'TIMING: (?P<step_num>\S+) CPU:',
self.stdout, 'step_num')), 50),
sn.assert_lt(energy_diff, 2720)
])
@run_after('setup')
def set_executable_opts(self):
if self.platform_name == 'gpu':
self.executable_opts = ['+idlepoll', '+ppn 23', 'stmv.namd']
self.num_cpus_per_task = 24
self.num_gpus_per_node = 1
else:
# On Eiger a no-smp NAMD version is the default
if self.current_system.name in ['eiger', 'pilatus']:
self.executable_opts = ['+idlepoll', 'stmv.namd']
self.num_tasks_per_core = 2
else:
self.executable_opts = ['+idlepoll', '+ppn 71', 'stmv.namd']
self.num_cpus_per_task = 72

@run_before('performance')
def setup_perf_vars(self):
self.perf_patterns = {
'days_ns': sn.avg(sn.extractall(
r'Info: Benchmark time: \S+ CPUs \S+ '
r's/step (?P<days_ns>\S+) days/ns \S+ MB memory',
self.stdout, 'days_ns', float))
}
if self.arch == 'gpu':
@run_after('setup')
def set_reference(self):
if self.platform_name == 'gpu':
self.reference = REFERENCE_GPU_PERFORMANCE
if self.scale == 'small':
self.reference = {
'dom:gpu': {'days_ns': (0.15, None, 0.05, 'days/ns')},
'daint:gpu': {'days_ns': (0.15, None, 0.05, 'days/ns')}
}
else:
self.reference = {
'daint:gpu': {'days_ns': (0.07, None, 0.05, 'days/ns')}
}
self.valid_systems += ['dom:gpu']
else:
if self.scale == 'small':
self.reference = {
'dom:mc': {'days_ns': (0.51, None, 0.05, 'days/ns')},
'daint:mc': {'days_ns': (0.51, None, 0.05, 'days/ns')},
'eiger:mc': {'days_ns': (0.12, None, 0.05, 'days/ns')},
'pilatus:mc': {'days_ns': (0.12, None, 0.05, 'days/ns')},
}
else:
self.reference = {
'daint:mc': {'days_ns': (0.28, None, 0.05, 'days/ns')},
'eiger:mc': {'days_ns': (0.05, None, 0.05, 'days/ns')},
'pilatus:mc': {'days_ns': (0.05, None, 0.05, 'days/ns')}
}
self.valid_systems += ['dom:mc']
self.reference = REFERENCE_CPU_PERFORMANCE
94 changes: 94 additions & 0 deletions hpctestlib/apps/namd/base_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright 2016-2021 Swiss National Supercomputing Centre (CSCS/ETH Zurich)
# ReFrame Project Developers. See the top-level LICENSE file for details.
#
# SPDX-License-Identifier: BSD-3-Clause

import os

import reframe as rfm
import reframe.utility.sanity as sn


class Namd_BaseCheck(rfm.RunOnlyRegressionTest):
'''Base class for the NAMD Test.

NAMD is a parallel molecular dynamics code designed for
high-performance simulation of large biomolecular systems.
Based on Charm++ parallel objects, NAMD scales to hundreds of
cores for typical simulations and beyond 500,000 cores for the
largest simulations. NAMD uses the popular molecular graphics
program VMD for simulation setup and trajectory analysis,
but is also file-compatible with AMBER, CHARMM, and X-PLOR.
(see ks.uiuc.edu/Research/namd/)

The presented abstract run-only class checks the NAMD perfomance.
To do this, it is necessary to define in tests the reference values
of energy and possible deviations from this value. This data is used
to check if the task is being executed correctly, that is, the final
energy is correct (approximately the reference). The default
assumption is that NAMD is already installed on the device under test.
'''

#: Reference value of energy, that is used for the comparison
#: with the execution ouput on the sanity step. The absolute
#: difference between final energy value and reference value
#: should be smaller than energy_tolerance
#:
#: :type: str
#: :default: :class:`required`
energy_value = variable(float)

#: Maximum deviation from the reference value of energy,
#: that is acceptable.
#:
#: :default: :class:`required`
energy_tolerance = variable(float)

#: :default: :class:`required`
executable = required

executable = 'namd2'
energy_value = -2451359.5
energy_tolerance = 2720.

@run_after('init')
def source_install(self):
# Reset sources dir relative to the SCS apps prefix
self.sourcesdir = os.path.join(self.current_system.resourcesdir,
'NAMD', 'prod')

@run_before('performance')
def set_the_performance_dict(self):
self.perf_variables = {self.scale:
sn.make_performance_function(
sn.avg(
sn.extractall(
r'Info: Benchmark time: \S+ CPUs'
r' \S+ s/step (?P<days_ns>\S+) '
r'days/ns \S+ MB memory',
self.stdout, 'days_ns', float)),
'days/ns')}

@performance_function('days/ns', perf_key='perf')
def set_perf_patterns(self):
return sn.avg(sn.extractall(
r'Info: Benchmark time: \S+ CPUs \S+ '
r's/step (?P<days_ns>\S+) days/ns \S+ MB memory',
self.stdout, 'days_ns', float))

@sanity_function
def assert_energy_readout(self):
'''Assert the obtained energy meets the specified tolerances.'''

energy = sn.avg(sn.extractall(
r'ENERGY:([ \t]+\S+){10}[ \t]+(?P<energy>\S+)',
self.stdout, 'energy', float)
)
energy_diff = sn.abs(energy - self.energy_value)
ref_ener_diff = sn.abs(self.energy_tolerance)
return sn.all([
sn.assert_eq(sn.count(sn.extractall(
r'TIMING: (?P<step_num>\S+) CPU:',
self.stdout, 'step_num')), 50),
sn.assert_lt(energy_diff, self.energy_tolerance)
])