Coverage for gpaw/lcaotddft/quadrupolemomentwriter.py: 19%
80 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
1import re
2import numpy as np
4from ase.utils import IOContext
6from gpaw.lcaotddft.observer import TDDFTObserver
9def calculate_quadrupole_moments(gd, rho_g, center_v):
10 # Center relative to the cell center
11 # center_v += 0.5 * gd.cell_cv.sum(0)
12 r_vg = gd.get_grid_point_coordinates()
13 qm_i = np.zeros(6, dtype=float)
14 i = 0
15 for v1 in range(3):
16 x1_g = r_vg[v1] - center_v[v1]
17 for v2 in range(v1, 3):
18 x2_g = r_vg[v2] - center_v[v2]
19 qm_i[i] = -gd.integrate(x1_g * x2_g * rho_g)
20 i += 1
21 return qm_i
24class QuadrupoleMomentWriter(TDDFTObserver):
25 version = 1
27 def __init__(self, paw, filename, center=[0, 0, 0], density='comp',
28 interval=1):
29 TDDFTObserver.__init__(self, paw, interval)
30 self.ioctx = IOContext()
31 if paw.niter == 0:
32 # Initialize
33 if isinstance(center, str) and center == 'center':
34 self.center_v = 0.5 * paw.density.finegd.cell_cv.sum(0)
35 else:
36 assert len(center) == 3
37 self.center_v = center
38 self.density_type = density
39 self.fd = self.ioctx.openfile(filename, comm=paw.world, mode='w')
40 else:
41 # Read and continue
42 self.read_header(filename)
43 self.fd = self.ioctx.openfile(filename, comm=paw.world, mode='a')
45 def _write(self, line):
46 self.fd.write(line)
47 self.fd.flush()
49 def _write_header(self, paw):
50 if paw.niter != 0:
51 return
52 line = f'# {self.__class__.__name__}[version={self.version}]'
53 line += '(center=[%.6f, %.6f, %.6f]' % tuple(self.center_v)
54 line += ', density=%s)\n' % repr(self.density_type)
55 line += ('# %15s %15s %22s %22s %22s %22s %22s %22s\n' %
56 ('time', 'norm', 'x^2', 'xy', 'xz', 'y^2', 'yz', 'z^2'))
57 self._write(line)
59 def read_header(self, filename):
60 with open(filename) as f:
61 line = f.readline()
62 regexp = (r"^(?P<name>\w+)\[version=(?P<version>\d+)\]\(center=\["
63 r"(?P<center0>[-+0-9\.]+), "
64 r"(?P<center1>[-+0-9\.]+), "
65 r"(?P<center2>[-+0-9\.]+)\], "
66 r"density='(?P<density>\w+)'\)$")
67 m = re.match(regexp, line[2:])
68 assert m is not None, 'Unknown fileformat'
69 assert m.group('name') == self.__class__.__name__
70 assert int(m.group('version')) == self.version
71 self.center_v = [float(m.group('center%d' % v)) for v in range(3)]
72 self.density_type = m.group('density')
74 def _write_kick(self, paw):
75 line = '# Kick: %s' % paw.kick_ext
76 line += '; Time = %.8lf' % paw.time
77 line += '\n'
78 self._write(line)
80 def _write_dm(self, paw):
81 time = paw.time
82 density = paw.density
83 if self.density_type == 'comp':
84 rho_g = density.rhot_g
85 gd = density.finegd
86 elif self.density_type == 'pseudo':
87 rho_g = density.nt_sg.sum(axis=0)
88 gd = density.finegd
89 elif self.density_type == 'pseudocoarse':
90 rho_g = density.nt_sG.sum(axis=0)
91 gd = density.gd
92 else:
93 raise RuntimeError('Unknown density type: %s' % self.density_type)
95 norm = gd.integrate(rho_g)
96 qm = calculate_quadrupole_moments(gd, rho_g, center_v=self.center_v)
97 line = (('%20.8lf %20.8le' + ' %22.12le' * len(qm) + '\n') %
98 ((time, norm) + tuple(qm)))
99 self._write(line)
101 def _update(self, paw):
102 if paw.action == 'init':
103 self._write_header(paw)
104 elif paw.action == 'kick':
105 self._write_kick(paw)
106 self._write_dm(paw)
108 def __del__(self):
109 self.ioctx.close()