Coverage for gpaw/cli/info.py: 23%

107 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-19 00:19 +0000

1from __future__ import annotations 

2 

3import os 

4import sys 

5from typing import Literal 

6from textwrap import fill 

7 

8from ase.utils import import_module, search_current_git_hash 

9 

10import gpaw.cgpaw as cgpaw 

11import gpaw 

12import gpaw.fftw as fftw 

13from gpaw.mpi import have_mpi, rank 

14from gpaw.new.c import GPU_AWARE_MPI, GPU_ENABLED 

15from gpaw.utilities import compiled_with_libvdwxc, compiled_with_sl 

16from gpaw.utilities.elpa import LibElpa 

17from gpaw.gpu import cupy, cupy_is_fake, __file__ as gpaw_gpu_filename 

18 

19 

20Color = Literal['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w', 'none'] 

21 

22# Background: +10 

23COLORS: dict[Color, int] = {'r': 31, 'g': 32, 'b': 34, 

24 'c': 36, 'm': 35, 'y': 33, 

25 'k': 30, 'w': 37, 

26 'none': 39} 

27 

28 

29def highlight(text: str, 

30 foreground: Color = 'none', 

31 background: Color = 'none', 

32 *, 

33 bright: bool = True) -> str: 

34 sgr = '\x1b[{}m'.format 

35 colors = [COLORS[background] + 10, 

36 COLORS[foreground], 

37 COLORS['none'], 

38 COLORS['none'] + 10] 

39 if bright: 

40 colors[0] += 60 

41 colors[1] += 60 

42 color_codes = [sgr(c) for c in colors] 

43 return '{1[0]}{1[1]}{0}{1[2]}{1[3]}'.format(text, color_codes) 

44 

45 

46def warn(text: str, 

47 foreground: Color = 'w', 

48 background: Color = 'r', 

49 *, 

50 width: int | None = None, 

51 **kwargs) -> str: 

52 pad = ' ' * ((width or 0) - len(text)) 

53 return highlight(text, 

54 foreground=foreground, 

55 background=background, 

56 **kwargs) + pad 

57 

58 

59def info() -> None: 

60 """Show versions of GPAW and its dependencies.""" 

61 results: list[tuple[str, str | bool]] = [ 

62 ('python-' + sys.version.split()[0], sys.executable)] 

63 warnings = {} 

64 for name in ['gpaw', 'ase', 'numpy', 'scipy', 'gpaw_data']: 

65 try: 

66 module = import_module(name) 

67 except ImportError: 

68 results.append((name, False)) 

69 else: 

70 # Search for git hash 

71 githash = search_current_git_hash(module) 

72 if githash is None: 

73 githash = '' 

74 else: 

75 githash = f'-{githash:.10}' 

76 results.append( 

77 (name + '-' + module.__version__ + githash, 

78 module.__file__.rsplit('/', 1)[0] + '/')) # type: ignore 

79 

80 libs = gpaw.get_libraries() 

81 

82 libxc = libs['libxc'] 

83 if libxc: 

84 results.append((f'libxc-{libxc}', True)) 

85 else: 

86 results.append(('libxc', False)) 

87 warnings['libxc'] = ('GPAW not compiled with LibXC support; ' 

88 'though not a requirement, ' 

89 'it is recommended that LibXC be installed and ' 

90 'GPAW be recompiled with support therefor') 

91 

92 if hasattr(cgpaw, 'githash'): 

93 githash = f'-{cgpaw.githash():.10}' 

94 else: 

95 githash = '' 

96 

97 results.append(('_gpaw' + githash, 

98 os.path.normpath(getattr(cgpaw._gpaw, '__file__', 

99 'built-in')))) 

100 

101 results.append(('MPI enabled', have_mpi)) 

102 results.append(('OpenMP enabled', cgpaw.have_openmp)) 

103 results.append(('GPU enabled', GPU_ENABLED)) 

104 results.append(('GPU-aware MPI', GPU_AWARE_MPI)) 

105 cupy_version = 'cupy-' + cupy.__version__ 

106 results.append((cupy_version, cupy.__file__)) 

107 if cupy_is_fake and (GPU_ENABLED or GPU_AWARE_MPI): 

108 warnings[cupy_version] = ('GPAW compiled with GPU support, ' 

109 'but the requisite CuPy is not found or ' 

110 'cannot be set up (see gpaw.gpu at ' 

111 f'{gpaw_gpu_filename!r}); ' 

112 'GPU calculations will fail, ' 

113 'unless the user explicitly set the ' 

114 'environment variable GPAW_CPUPY=1, ' 

115 'which uses GPAW\'s fake CuPy ' 

116 '(gpaw.gpu.cpupy) for testing purposes') 

117 results.append(('MAGMA', cgpaw.have_magma)) 

118 if have_mpi: 

119 have_sl = compiled_with_sl() 

120 have_elpa = LibElpa.have_elpa() 

121 if have_elpa: 

122 version = LibElpa.api_version() 

123 if version is None: 

124 version = 'unknown, at most 2018.xx' 

125 have_elpa = f'yes; version: {version}' 

126 else: 

127 have_sl = have_elpa = 'no (MPI unavailable)' 

128 

129 if not hasattr(cgpaw, 'mmm'): 

130 results.append(('BLAS', 'using scipy.linalg.blas and numpy.dot()')) 

131 warnings['BLAS'] = ('GPAW not compiled with native BLAS support; ' 

132 'though not a requirement, ' 

133 'it is recommended that BLAS be installed and ' 

134 'GPAW be recompiled with support therefor') 

135 

136 results.append(('scalapack', have_sl)) 

137 results.append(('Elpa', have_elpa)) 

138 

139 have_fftw = fftw.have_fftw() 

140 results.append(('FFTW', have_fftw)) 

141 results.append(('libvdwxc', compiled_with_libvdwxc())) 

142 

143 for i, path in enumerate(gpaw.setup_paths): 

144 results.append((f'PAW-datasets ({i + 1})', str(path))) 

145 

146 if rank != 0: 

147 return 

148 

149 lines = [(a, b if isinstance(b, str) else ['no', 'yes'][b]) 

150 for a, b in results] 

151 n1 = max(len(a) for a, _ in lines) 

152 n2 = max(len(b) for _, b in lines) 

153 output_width = n1 + 6 + n2 

154 box_edge = ' ' + '-' * (output_width - 2) 

155 print(box_edge) 

156 for a, b in lines: 

157 if a in warnings: 

158 a, b = warn(a, width=n1), warn(b, width=n2) 

159 else: 

160 a, b = f'{a:{n1}}', f'{b:{n2}}' 

161 print(f'| {a} {b} |') 

162 print(box_edge) 

163 

164 if not warnings: 

165 return 

166 warning_header = 'WARNING ({}):'.format 

167 header_width = max(len(warning_header(item)) for item in warnings) + 1 

168 for item, message in warnings.items(): 

169 topic = 'WARNING ({}):'.format(item) 

170 message = fill(message, 

171 initial_indent=' ' * header_width, 

172 subsequent_indent=' ' * header_width, 

173 width=output_width) 

174 print(warn(topic, 

175 foreground='y', 

176 background='k', 

177 bright=False, 

178 width=header_width), 

179 message[header_width:], 

180 sep='') 

181 

182 

183class CLICommand: 

184 """Show versions of GPAW and its dependencies""" 

185 

186 @staticmethod 

187 def add_arguments(parser): 

188 pass 

189 

190 @staticmethod 

191 def run(args): 

192 info()