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
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-19 00:19 +0000
1from __future__ import annotations
3import os
4import sys
5from typing import Literal
6from textwrap import fill
8from ase.utils import import_module, search_current_git_hash
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
20Color = Literal['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w', 'none']
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}
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)
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
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
80 libs = gpaw.get_libraries()
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')
92 if hasattr(cgpaw, 'githash'):
93 githash = f'-{cgpaw.githash():.10}'
94 else:
95 githash = ''
97 results.append(('_gpaw' + githash,
98 os.path.normpath(getattr(cgpaw._gpaw, '__file__',
99 'built-in'))))
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)'
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')
136 results.append(('scalapack', have_sl))
137 results.append(('Elpa', have_elpa))
139 have_fftw = fftw.have_fftw()
140 results.append(('FFTW', have_fftw))
141 results.append(('libvdwxc', compiled_with_libvdwxc()))
143 for i, path in enumerate(gpaw.setup_paths):
144 results.append((f'PAW-datasets ({i + 1})', str(path)))
146 if rank != 0:
147 return
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)
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='')
183class CLICommand:
184 """Show versions of GPAW and its dependencies"""
186 @staticmethod
187 def add_arguments(parser):
188 pass
190 @staticmethod
191 def run(args):
192 info()