Coverage for gpaw/io/logger.py: 94%
125 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
1import os
2import sys
3import time
5import numpy as np
6import ase
7from ase import __version__ as ase_version
8from ase.utils import search_current_git_hash, IOContext
10import gpaw
11import gpaw.cgpaw as cgpaw
12from gpaw.utilities.memory import maxrss
15class GPAWLogger:
16 """Class for handling all text output."""
17 def __init__(self, world):
18 self.world = world
20 self.verbose = False
21 self._fd = None
22 self.oldfd = 42
23 self.iocontext = IOContext()
24 self.use_colors = False
25 self.green = ''
26 self.reset = ''
28 @property
29 def fd(self):
30 return self._fd
32 @fd.setter
33 def fd(self, fd):
34 """Set the stream for text output.
36 If `txt` is not a stream-object, then it must be one of:
38 * None: Throw output away.
39 * '-': Use stdout (``sys.stdout``) on master, elsewhere throw away.
40 * A filename: Open a new file on master, elsewhere throw away.
41 """
42 if fd == self.oldfd:
43 return
44 self.oldfd = fd
45 self._fd = self.iocontext.openfile(fd, self.world)
46 self.header()
48 def __call__(self, *args, **kwargs):
49 flush = kwargs.pop('flush', False)
50 print(*args, file=self._fd, **kwargs)
51 if flush:
52 self._fd.flush()
54 def flush(self):
55 self._fd.flush()
57 def header(self):
58 self()
59 self(' ___ ___ ___ _ _ _ ')
60 self(' | | |_ | | | | ')
61 self(' | | | | | . | | | | ')
62 self(' |__ | _|___|_____| ', gpaw.__version__)
63 self(' |___|_| ')
64 self()
66 write_header(self, self.world)
68 def print_dict(self, dct, sep=' '):
69 options = np.get_printoptions()
70 try:
71 np.set_printoptions(threshold=4, linewidth=50)
72 for key, value in sorted(dct.items()):
73 if hasattr(value, 'todict'):
74 value = value.todict()
75 if isinstance(value, dict):
76 sep = ',\n ' + ' ' * len(key)
77 keys = sorted(value, key=lambda k: (str(type(k)), k))
78 s = sep.join(f'{k}: {value[k]}' for k in keys)
79 self(f' {key}: {{{s}}}')
80 elif hasattr(value, '__len__'):
81 value = np.asarray(value)
82 sep = ',\n ' + ' ' * len(key)
83 s = sep.join(str(value).splitlines())
84 self(f' {key}: {s}')
85 else:
86 self(f' {key}: {value}')
87 finally:
88 np.set_printoptions(**options)
90 def __del__(self):
91 """Destructor: Write timing output before closing."""
92 self.close()
94 def close(self):
95 if gpaw.dry_run or self._fd.closed:
96 return
98 try:
99 mr = maxrss()
100 except (LookupError, TypeError, NameError, AttributeError):
101 # Thing can get weird during interpreter shutdown ...
102 mr = 0
104 if mr > 0:
105 if mr < 1024**3:
106 self('Memory usage: %.2f MiB' % (mr / 1024**2))
107 else:
108 self('Memory usage: %.2f GiB' % (mr / 1024**3))
110 self('Date: ' + time.asctime())
112 self.iocontext.close()
115def write_header(log, world):
116 # We use os.uname() here bacause platform.uname() starts a subprocess,
117 # which MPI may not like!
118 # This might not work on Windows. We will see ...
119 nodename, machine = os.uname()[1::3]
121 log('User: ', os.getenv('USER', '???') + '@' + nodename)
122 log('Date: ', time.asctime())
123 log('Arch: ', machine)
124 log('Pid: ', os.getpid())
125 log('CWD: ', os.getcwd())
126 log('Python: {}.{}.{}'.format(*sys.version_info[:3]))
127 # GPAW
128 line = os.path.dirname(gpaw.__file__)
129 githash = search_current_git_hash(gpaw, world)
130 if githash is not None:
131 line += f' ({githash:.10})'
132 log('gpaw: ', line)
134 # Find C-code:
135 c = getattr(cgpaw._gpaw, '__file__', '')
136 if not c:
137 c = sys.executable
138 line = os.path.normpath(c)
139 if hasattr(cgpaw, 'githash'):
140 line += f' ({cgpaw.githash():.10})'
141 log('_gpaw: ', cut(line))
143 # ASE
144 line = f'{os.path.dirname(ase.__file__)} (version {ase_version}'
145 githash = search_current_git_hash(ase, world)
146 if githash is not None:
147 line += f'-{githash:.10}'
148 line += ')'
149 log('ase: ', line)
151 log('numpy: %s (version %s)' %
152 (os.path.dirname(np.__file__), np.version.version))
153 import scipy as sp
154 log('scipy: %s (version %s)' %
155 (os.path.dirname(sp.__file__), sp.version.version))
156 # Explicitly deleting SciPy seems to remove garbage collection
157 # problem of unknown cause
158 del sp
159 log('libxc: ', getattr(cgpaw, 'libxc_version', '2.x.y'))
160 log('units: Angstrom and eV')
161 log('cores:', world.size)
162 log('OpenMP:', cgpaw.have_openmp)
163 log('OMP_NUM_THREADS:', os.environ['OMP_NUM_THREADS'])
165 if gpaw.debug:
166 log('DEBUG-MODE: true')
168 log()
171def cut(s, indent=' '):
172 if len(s) + len(indent) < 80:
173 return s
174 s1, s2 = s.rsplit('/', 1)
175 return s1 + '/\n' + indent + s2
178def indent(s, level=1, tab=' '):
179 return ''.join(tab * level + l for l in s.splitlines(True))