Coverage for gpaw/utilities/hardware.py: 11%
108 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
1import os
2import re
4moab = {
5 'cmdstr': '#MSUB ',
6 'jobid': '$MOAB_JOBID',
7 'mailtype': '-m bea',
8 'mpirun': 'mpiexec -np ',
9 'name': '-N ',
10 'nodes': '-l nodes=',
11 'ppn': ':ppn=',
12 'walltime': '-l walltime=',
13}
14sbatch = {
15 'cmdstr': '#SBATCH ',
16 'jobid': '$SLURM_JOB_ID',
17 'mailtype': '--mail-type=ALL',
18 'mpirun': 'mpiexec -np ',
19 'name': '--job-name=',
20 'nodes': '--nodes=',
21 'tasks': '--tasks-per-node=',
22 'walltime': '--time=',
23}
25_hardware_info = {
26 "bwUniCluster": {
27 "cores_per_node": 16,
28 "loginnodes": [r'uc1n*'],
29 'scheduler': moab,
30 },
31 "jureca": {
32 "cores_per_node": 24,
33 "loginnodes": ["jureca"],
34 'scheduler': {
35 'cmdstr': '#SBATCH ',
36 'jobid': '$SLURM_JOBID',
37 'mpirun': 'srun -n ',
38 'mail': '--mail-user=',
39 'mailtype': '--mail-type=ALL',
40 'name': '--job-name=',
41 'nodes': '--nodes=',
42 'walltime': '--time=',
43 },
44 },
45 "nemo": {
46 "cores_per_node": 20,
47 "loginnodes": [r"login1.nemo.privat"],
48 'scheduler': moab,
49 },
50 "justus2": {
51 "cores_per_node": 48,
52 "loginnodes": [r"login??"],
53 'scheduler': sbatch,
54 }
55}
58def dhms(secs):
59 """return days,hours,minutes and seconds"""
60 dhms = [0, 0, 0, 0]
61 dhms[0] = int(secs // 86400)
62 s = secs % 86400
63 dhms[1] = int(s // 3600)
64 s = secs % 3600
65 dhms[2] = int(s // 60)
66 s = secs % 60
67 dhms[3] = int(s + .5)
68 return dhms
71def hms(secs):
72 """return hours,minutes and seconds"""
73 hms = [0, 0, 0]
74 hms[0] = int(secs // 3600)
75 s = secs % 3600
76 hms[1] = int(s // 60)
77 s = secs % 60
78 hms[2] = int(s + 0.5)
79 return hms
82def hms_string(secs):
83 """return hours,minutes and seconds string, e.g. 02:00:45"""
84 l = hms(secs)
86 def extend10(n):
87 if n < 10:
88 return '0' + str(n)
89 else:
90 return str(n)
92 return extend10(l[0]) + ':' + extend10(l[1]) + ':' + extend10(l[2])
95class ComputeCluster:
96 def __init__(self, architecture=None):
97 if architecture:
98 self.arch = architecture
99 try:
100 self.data = _hardware_info[architecture]
101 return
102 except KeyError:
103 raise KeyError(
104 'Architecture {} unknown, known are\n'.format(
105 architecture) + self.list_architectures())
107 def get_hostname():
108 if os.path.isfile('/etc/FZJ/systemname'):
109 with open('/etc/FZJ/systemname') as f:
110 return f.read().strip()
112 if 'HOSTNAME' in list(os.environ.keys()):
113 return os.environ['HOSTNAME']
115 try:
116 import socket
117 return socket.gethostname().split('-')[0]
118 except Exception:
119 dummy, hostname = os.popen4('hostname -s')
120 return hostname.readline().split()
122 def has_key_regexp(dictionary, expression):
123 for key in dictionary:
124 if re.match(key, expression):
125 return True
126 return False
128 hostname = get_hostname()
129 for host in _hardware_info:
130 d = _hardware_info[host]
131 if has_key_regexp(d['loginnodes'], hostname):
132 self.arch = host
133 self.data = d
134 return
135 raise KeyError(f'Host {hostname} unknown, try -a option.\n' +
136 self.list_architectures())
138 def list_architectures(self):
139 string = ''
140 for arch in _hardware_info:
141 string += f' {arch}\n'
142 return string
144 def write(self, filename=None, **set):
145 if filename is None:
146 filename = 'run.' + self.arch
147 f = open(filename, 'w')
149 env = os.environ
150 d = self.data['scheduler']
151 c = d['cmdstr']
153 print('#!/bin/bash -x', file=f)
155 print(c + d['name'] + set['name'].replace('+', ''), file=f)
156 cores = set['cores']
157 cores_per_node = self.data['cores_per_node']
158 if set['smt']:
159 cores_per_node *= 2
160 nodes = int((cores + (cores_per_node - 1)) / cores_per_node)
161 ppn = int((cores + nodes - 1) / nodes)
162 if cores != nodes * ppn:
163 print('Note:', nodes * ppn, 'cores reserved but only', cores,
164 'cores used.')
165 print(' Consider to use multiples of', cores_per_node, end=' ')
166 print('processors for best performance.')
167 print(c + d['nodes'] + str(nodes), file=f, end='')
168 if 'ppn' in d:
169 print(d['ppn'] + str(ppn), file=f)
170 else:
171 print(file=f)
172 print(c + d['tasks'] + str(ppn), file=f)
173 print(c + d['walltime'] + hms_string(set['time']), file=f)
174 if set['mail'] is not None:
175 print(c + '--mail-user=' + set['mail'], file=f)
176 print(c + d['mailtype'], file=f)
177 print('cd', set['wd'], file=f)
178 print('export MODULEPATH=' + env['MODULEPATH'], file=f)
179 for module in env['LOADEDMODULES'].split(':'):
180 print('module load', module, file=f)
181 print(d['mpirun'] + str(cores) + ' gpaw python',
182 set['script'], end=' ', file=f)
183 if 'parameters' in set:
184 print(set['parameters'], end=' ', file=f)
185 print('>', set['out'] + '_' + d['jobid'], end=' ', file=f)
186 print('2>', set['err'] + '_' + d['jobid'], file=f)
187 f.close()
189 return filename