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

1import os 

2import re 

3 

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} 

24 

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} 

56 

57 

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 

69 

70 

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 

80 

81 

82def hms_string(secs): 

83 """return hours,minutes and seconds string, e.g. 02:00:45""" 

84 l = hms(secs) 

85 

86 def extend10(n): 

87 if n < 10: 

88 return '0' + str(n) 

89 else: 

90 return str(n) 

91 

92 return extend10(l[0]) + ':' + extend10(l[1]) + ':' + extend10(l[2]) 

93 

94 

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()) 

106 

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() 

111 

112 if 'HOSTNAME' in list(os.environ.keys()): 

113 return os.environ['HOSTNAME'] 

114 

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() 

121 

122 def has_key_regexp(dictionary, expression): 

123 for key in dictionary: 

124 if re.match(key, expression): 

125 return True 

126 return False 

127 

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()) 

137 

138 def list_architectures(self): 

139 string = '' 

140 for arch in _hardware_info: 

141 string += f' {arch}\n' 

142 return string 

143 

144 def write(self, filename=None, **set): 

145 if filename is None: 

146 filename = 'run.' + self.arch 

147 f = open(filename, 'w') 

148 

149 env = os.environ 

150 d = self.data['scheduler'] 

151 c = d['cmdstr'] 

152 

153 print('#!/bin/bash -x', file=f) 

154 

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() 

188 

189 return filename