QAssemble 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
QAssemble/BLatDyn.py ADDED
@@ -0,0 +1,725 @@
1
+ """Dynamic bosonic lattice quantities, polarization, and screened interactions."""
2
+ import numpy as np
3
+ import sys, os
4
+ import itertools
5
+ import copy, gc, time, datetime
6
+ import h5py
7
+ from .Crystal import Crystal
8
+ from .BLatStc import V
9
+ from .utility.DLR import DLR
10
+ from .utility.Fourier import Fourier
11
+ from .utility.Dyson import Dyson
12
+ from .utility.Mixing import Mixing
13
+
14
+
15
+ class BLatDyn(object):
16
+ """Base operations for dynamic bosonic lattice tensors."""
17
+ def __init__(self, crystal: Crystal, dlr: DLR, mixing_method: str = "pulay", npulay: int = 5):
18
+ """Initialize the object and prepare derived state."""
19
+ self.crystal = crystal
20
+ self.dlr = dlr
21
+ self.mixer = Mixing(method=mixing_method, npulay=npulay)
22
+ # self.flatdyn = flatdyn
23
+ self._boson_phase_cache_k2r = self._get_boson_phaseK2R()
24
+ self._boson_phase_cache_r2k = self._get_boson_phaseR2K()
25
+
26
+ def _get_boson_phaseK2R(self) -> np.ndarray:
27
+ """Build the phase matrix for bosonic k-to-real-space transforms."""
28
+
29
+
30
+ nrk = self.crystal.rkgrid[0]*self.crystal.rkgrid[1]*self.crystal.rkgrid[2]
31
+
32
+ basis_orb = self.crystal.basisf[self.crystal.borb2atom]
33
+
34
+ kv = self.crystal.kpoint[:nrk] @ basis_orb.T
35
+
36
+ kv_delta = kv[:, :, None] - kv[:, None, :]
37
+ phases = np.exp(2.0j * np.pi * kv_delta)
38
+ phases_T = np.transpose(phases, (1, 2, 0))
39
+
40
+ return phases_T
41
+
42
+ def _get_boson_phaseR2K(self) -> np.ndarray:
43
+ """Build the phase matrix for bosonic real-to-k-space transforms."""
44
+
45
+ nrk = self.crystal.rkgrid[0]*self.crystal.rkgrid[1]*self.crystal.rkgrid[2]
46
+
47
+ basis_orb = self.crystal.basisf[self.crystal.borb2atom]
48
+
49
+ kv = self.crystal.kpoint[:nrk] @ basis_orb.T
50
+
51
+ kv_delta = kv[:, :, None] - kv[:, None, :]
52
+ phases = np.exp(-2.0j * np.pi * kv_delta)
53
+ phases_T = np.transpose(phases, (1, 2, 0))
54
+
55
+ return phases_T
56
+
57
+ def Inverse(self, matin: np.ndarray) -> np.ndarray:
58
+ """Return block-wise matrix inverses for the input tensor."""
59
+ norb = matin.shape[0]
60
+ ns = matin.shape[2]
61
+ nrk = matin.shape[4]
62
+ nft = matin.shape[5]
63
+
64
+ matout = np.zeros((norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F")
65
+ tempmat = np.zeros((norb * ns, norb * ns), dtype=np.complex128)
66
+ tempmat2 = np.zeros((norb * ns, norb * ns), dtype=np.complex128)
67
+
68
+ # Make composite matrix #
69
+ for ift in range(nft):
70
+ for irk in range(nrk):
71
+ tempmat = self.crystal.OrbSpin2Composite(matin[:, :, :, :, irk, ift])
72
+ tempmat2 = np.linalg.inv(tempmat)
73
+ matout[:, :, :, :, irk, ift] = self.crystal.Composite2OrbSpin(tempmat2)
74
+
75
+ return matout
76
+
77
+ def Moment(self, bf: np.ndarray, oddzero: bool, highzero: bool) -> tuple:
78
+ """Compute moment corrections for imaginary-frequency transforms."""
79
+ norb = bf.shape[0]
80
+ ns = bf.shape[2]
81
+ nrk = bf.shape[4]
82
+
83
+ moment = np.zeros((norb, norb, ns, ns, nrk, 3), dtype=np.complex128, order="F")
84
+ high = np.zeros((norb, norb, ns, nrk), dtype=np.complex128, order="F")
85
+
86
+ # moment, high = QAFort.fourier.blatdyn_m(self.dlr.nu, bf, oddzero, highzero)
87
+ moment, high = Fourier.BLatDynM(self.dlr.nu, bf, oddzero, highzero)
88
+
89
+ return moment, high
90
+
91
+ def F2T(self, bf: np.ndarray) -> np.ndarray:
92
+ """Transform data from Matsubara frequency to imaginary time."""
93
+ norb = bf.shape[0]
94
+ ns = bf.shape[2]
95
+ nrk = bf.shape[4]
96
+ nfreq = bf.shape[5]
97
+
98
+ bf_t = np.moveaxis(bf, -1, 0) # (nfreq, norb, norb, ns, ns, nrk)
99
+ batch = norb * norb * ns * ns * nrk
100
+ bf_2d = np.ascontiguousarray(bf_t).reshape(nfreq, batch)
101
+
102
+ # Boson: dlr_from_matsubara uses bosonic_corr_x[:, None, None]
103
+ # which requires 3D input. Solve lu_solve in 2D, then apply correction.
104
+ from scipy.linalg import lu_solve
105
+ G_xaa = lu_solve((self.dlr.dB.dlrmf2cf, self.dlr.dB.mf2cfpiv), bf_2d / self.dlr.beta)
106
+ G_xaa /= self.dlr.dB.bosonic_corr_x[:, None]
107
+
108
+ btau_2d = np.tensordot(self.dlr.dB.T_lx, G_xaa, axes=(1, 0))
109
+ ntau = btau_2d.shape[0]
110
+ btau = btau_2d.reshape(ntau, norb, norb, ns, ns, nrk)
111
+ btau = np.moveaxis(btau, 0, -1) # (norb, norb, ns, ns, nrk, ntau)
112
+ btau = np.asfortranarray(btau)
113
+
114
+ return btau
115
+
116
+ def T2F(self, btau: np.ndarray) -> np.ndarray:
117
+ """Transform data from imaginary time to Matsubara frequency."""
118
+ norb = btau.shape[0]
119
+ ns = btau.shape[2]
120
+ nrk = btau.shape[4]
121
+ ntau = btau.shape[5]
122
+
123
+ btau_t = np.moveaxis(btau, -1, 0) # (ntau, norb, norb, ns, ns, nrk)
124
+ batch = norb * norb * ns * ns * nrk
125
+ btau_2d = np.ascontiguousarray(btau_t).reshape(ntau, batch)
126
+
127
+ # Boson: lu_solve in 2D, then matsubara_from_dlr manually
128
+ from scipy.linalg import lu_solve
129
+ fxx = lu_solve((self.dlr.dB.dlrit2cf, self.dlr.dB.it2cfpiv), btau_2d)
130
+ bf_2d = self.dlr.beta * np.tensordot(
131
+ self.dlr.dB.T_qx * self.dlr.dB.bosonic_corr_x[None, :], fxx, axes=(1, 0))
132
+ nfreq = bf_2d.shape[0]
133
+ bf = bf_2d.reshape(nfreq, norb, norb, ns, ns, nrk)
134
+ bf = np.moveaxis(bf, 0, -1) # (norb, norb, ns, ns, nrk, nfreq)
135
+ bf = np.asfortranarray(bf)
136
+
137
+ return bf
138
+
139
+ def K2R(self, matk: np.ndarray) -> np.ndarray:
140
+ """Transform lattice data from reciprocal space to real space."""
141
+
142
+ norb = matk.shape[0]
143
+ ns = matk.shape[2]
144
+ nrk = matk.shape[4]
145
+ nft = matk.shape[5]
146
+
147
+ matr = np.zeros((norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F")
148
+ tempmat = matk.copy()
149
+ tempmat *= self._boson_phase_cache_k2r[:, :, None, None, :, None]
150
+
151
+ matr = Fourier.BLatDynK2R(tempmat, self.crystal.rkgrid)
152
+
153
+
154
+ return matr
155
+
156
+ def R2K(self, matr: np.ndarray) -> np.ndarray:
157
+ """Transform lattice data from real space to reciprocal space."""
158
+ norb = matr.shape[0]
159
+ ns = matr.shape[2]
160
+ nrk = matr.shape[4]
161
+ nft = matr.shape[5]
162
+ rkgrid = self.crystal.rkgrid
163
+
164
+ matk = np.zeros((norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F")
165
+ tempmat = np.empty((norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F")
166
+
167
+ tempmat = Fourier.BLatDynR2K(matr, rkgrid)
168
+
169
+ matk = tempmat * self._boson_phase_cache_r2k[:, :, None, None, :, None]
170
+
171
+
172
+ return matk
173
+
174
+ def GaussianLinearBroad(self, x, y, w1, temperature, cutoff):
175
+ """Apply Gaussian broadening with temperature-dependent widths."""
176
+ norb = y.shape[0]
177
+ ns = y.shape[2]
178
+ nrk = y.shape[3]
179
+ nft = y.shape[4]
180
+
181
+ ynew = np.zeros((norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F")
182
+
183
+ w0 = (1.0 - 3.0 * w1) * np.pi * temperature
184
+ widtharray = w0 + w1 * x
185
+ cnt = 0
186
+ for irk in range(nrk):
187
+ for x0 in x:
188
+ if x0 > cutoff + (w0 + w1 * cutoff) * 3.0:
189
+ ynew[..., irk, cnt] = y[..., irk, cnt]
190
+ else:
191
+ if (x0 > 3 * widtharray[cnt]) and (
192
+ (x[-1] - x0) > 3 * widtharray[cnt]
193
+ ):
194
+ dist = (
195
+ 1.0
196
+ / np.sqrt(2 * np.pi)
197
+ / widtharray[cnt]
198
+ * np.exp(-((x - x0) ** 2) / 2.0 / widtharray[cnt] ** 2)
199
+ )
200
+ for js in range(ns):
201
+ for ks in range(ns):
202
+ for iorb in range(norb):
203
+ for jorb in range(norb):
204
+ ynew[iorb, jorb, js, ks, irk, cnt] = sum(
205
+ dist * y[iorb, jorb, js, ks, irk]
206
+ ) / sum(dist)
207
+ else:
208
+ ynew[..., irk, cnt] = y[..., irk, cnt]
209
+ cnt += 1
210
+
211
+ return ynew
212
+
213
+ def Mixing(self, iter: int, mix: float, Bb: np.ndarray, Bold: np.ndarray) -> np.ndarray:
214
+ """Mix a new iterate with history from previous iterations."""
215
+ if iter == 1:
216
+ Bold = np.zeros_like(Bb)
217
+ return self.mixer(iter=iter, mix=mix, Fnew=Bb, Fold=Bold)
218
+
219
+ def Dyson(self, mat1: np.ndarray, mat2: np.ndarray) -> np.ndarray:
220
+ """Solve the Dyson equation for the supplied objects."""
221
+ # matout = QAFort.dyson.blatdyn(mat1, mat2)
222
+ return Dyson.BLatDyn(mat1, mat2)
223
+
224
+ # def Projection(self, matin: np.ndarray):
225
+ # norbc = self.crystal.bprojector.shape[1]
226
+ # ns = self.crystal.ns
227
+ # nft = len(self.dlr.nu) # self.ft.size
228
+ # nspace = self.crystal.bprojector.shape[3]
229
+
230
+ # matout = np.zeros(
231
+ # (norbc, norbc, ns, ns, nft, nspace), dtype=np.complex128, order="F"
232
+ # )
233
+
234
+ # for ispace in range(nspace):
235
+ # matout[..., ispace] = QAFort.projection.blatdyn(
236
+ # matin, self.crystal.bprojector[..., ispace]
237
+ # )
238
+
239
+ # return matout
240
+
241
+ def Quad2Double(self, matin: np.ndarray) -> np.ndarray:
242
+ """Convert a four-index tensor to paired two-index layout."""
243
+ # norb = len(self.crystal.bind)
244
+ # ns = self.crystal.ns
245
+ # nrk = len(self.crystal.kpoint)
246
+ # nft = len(self.dlr.nu)#self.ft.size
247
+ _, _, _, _, ns, _, nrk, nft = matin.shape
248
+ norb = len(self.crystal.bind)
249
+
250
+ matout = np.zeros(
251
+ (norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F"
252
+ )
253
+
254
+ # for ift in range(nft):
255
+ # for irk in range(nrk):
256
+ for irk, ift in itertools.product(list(range(nrk)), list(range(nft))):
257
+ for ks, js in itertools.product(range(ns), repeat=2):
258
+ matout[:, :, js, ks, irk, ift] = self.crystal.Quad2Double(
259
+ matin[:, :, :, :, js, ks, irk, ift]
260
+ )
261
+
262
+ return matout
263
+
264
+ def Double2Quad(self, matin: np.ndarray) -> np.ndarray:
265
+ """Convert a paired two-index tensor to four-index layout."""
266
+ norb = len(self.crystal.find)
267
+ # ns = self.crystal.ns
268
+ # nrk = len(self.crystal.kpoint)
269
+ # nft = len(self.dlr.nu)#self.ft.size
270
+ _, _, ns, _, nrk, nft = matin.shape
271
+
272
+ matout = np.zeros(
273
+ (norb, norb, norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F"
274
+ )
275
+
276
+ for ift in range(nft):
277
+ for irk in range(nrk):
278
+ for ks, js in itertools.product(range(ns), repeat=2):
279
+ matout[:, :, :, :, js, ks, irk, ift] = self.crystal.Double2Quad(
280
+ matin[:, :, js, ks, irk, ift]
281
+ )
282
+
283
+ return matout
284
+
285
+ def Double2Full(self, matin: np.ndarray) -> np.ndarray:
286
+ """Embed a paired two-index tensor into the full basis layout."""
287
+ norb = len(self.crystal.find)
288
+ _, _, ns, _, nrk, nft = matin.shape
289
+ nind = norb * norb
290
+ c2b = np.asarray(self.crystal.c2b, dtype=np.int64)
291
+
292
+ matout = np.zeros(
293
+ (nind, nind, ns, ns, nrk, nft), dtype=np.complex128, order="F"
294
+ )
295
+ matout[np.ix_(c2b, c2b)] = matin
296
+
297
+ del matin
298
+ gc.collect()
299
+ return matout
300
+
301
+ def Full2Double(self, matin: np.ndarray) -> np.ndarray:
302
+ """Project a full-basis tensor into paired two-index layout."""
303
+ c2b = np.asarray(self.crystal.c2b, dtype=np.int64)
304
+
305
+ matout = np.asarray(matin[np.ix_(c2b, c2b)], dtype=np.complex128, order="F")
306
+
307
+ return matout
308
+
309
+ def Quad2Full(self, matin: np.ndarray) -> np.ndarray:
310
+ """Embed a four-index tensor into the full basis layout."""
311
+ norb = len(self.crystal.find)
312
+ ns = self.crystal.ns
313
+ nrk = len(self.crystal.kpoint)
314
+ nft = len(self.dlr.nu) # self.ft.size
315
+
316
+ matout = np.zeros(
317
+ (norb * norb, norb * norb, ns, ns, nrk, nft), dtype=np.complex128, order="F"
318
+ )
319
+
320
+ for ift in range(nft):
321
+ for irk in range(nrk):
322
+ for ks, js in itertools.product(range(ns), repeat=2):
323
+ matout[:, :, js, ks, irk, ift] = self.crystal.Quad2Full(
324
+ matin[:, :, :, :, js, ks, irk, ift]
325
+ )
326
+
327
+ return matout
328
+
329
+ def Full2Quad(self, matin: np.ndarray) -> np.ndarray:
330
+ """Project a full-basis tensor into four-index layout."""
331
+ norb = len(self.crystal.find)
332
+ ns = self.crystal.ns
333
+ nrk = len(self.crystal.kpoint)
334
+ nft = len(self.dlr.nu) # self.ft.size
335
+
336
+ matout = np.zeros(
337
+ (norb, norb, norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F"
338
+ )
339
+
340
+ for ift in range(nft):
341
+ for irk in range(nrk):
342
+ for ks, js in itertools.product(range(ns), repeat=2):
343
+ matout[:, :, :, :, js, ks, irk, ift] = self.crystal.Full2Quad(
344
+ matin[:, :, js, ks, irk, ift]
345
+ )
346
+
347
+ return matout
348
+
349
+ def StcEmbedding(self, matin: np.ndarray) -> np.ndarray:
350
+ """Embed a static tensor into a dynamic tensor layout."""
351
+ norb = matin.shape[0]
352
+ ns = matin.shape[2]
353
+ nrk = matin.shape[4]
354
+ nft = len(self.dlr.nu) # self.ft.size
355
+
356
+ matout = np.zeros(
357
+ (norb, norb, ns, ns, nrk, nft), dtype=np.complex128, order="F"
358
+ )
359
+
360
+ for ift in range(nft):
361
+ matout[..., ift] += matin
362
+ del matin
363
+ gc.collect()
364
+ return matout
365
+
366
+ def Save(self, matin: np.ndarray, fn: str):
367
+ """Persist calculated arrays to the configured HDF5 output group."""
368
+ norb = matin.shape[0]
369
+ ns = matin.shape[2]
370
+ nrk = matin.shape[4]
371
+ nft = matin.shape[5]
372
+
373
+ if os.path.exists("blatdyn"):
374
+ pass
375
+ else:
376
+ os.mkdir("blatdyn")
377
+ os.chdir("blatdyn")
378
+
379
+ with open(fn + ".txt", "w") as f:
380
+ f.write("iorb, jorb, is, js, irk, ift, Re(B(k,w)), Im(B(k,w))\n")
381
+ for ift in range(nft):
382
+ for irk in range(nrk):
383
+ for ks, js in itertools.product(range(ns), repeat=2):
384
+ for jorb, iorb in itertools.product(range(norb), repeat=2):
385
+ f.write(
386
+ f"{iorb} {jorb} {js} {ks} {irk} {ift} {matin[iorb, jorb, js, ks, irk, ift].real} {matin[iorb, jorb, js, ks, irk, ift].imag}\n"
387
+ )
388
+
389
+ os.chdir("..")
390
+
391
+ return None
392
+
393
+ def R2KArb(self, matr: np.ndarray = None, kpoint: np.ndarray = None): # R2KAny
394
+ """Transform real-space data to arbitrary k-points."""
395
+ # if self.crystal.kpath == None:
396
+ # print("Error, kpath doesn't generate")
397
+ # sys.exit()
398
+ # kpoint = self.crystal.kpath
399
+ norb = len(self.crystal.find)
400
+ ns = self.crystal.ns
401
+ nr = self.crystal.rkgrid[0] * self.crystal.rkgrid[1] * self.crystal.rkgrid[2]
402
+ nk = len(kpoint)
403
+ nft = matr.shape[4]
404
+
405
+ self.crystal.RVec()
406
+ tempmat = copy.deepcopy(matr)
407
+ matk = np.zeros((norb, norb, ns, ns, nk, nft), dtype=complex, order="F")
408
+
409
+ for ift in range(nft):
410
+ for ik in range(nk):
411
+ for ks in range(ns):
412
+ for js in range(ns):
413
+ for jorb in range(norb):
414
+ for iorb in range(norb):
415
+ temp = 0
416
+ for ir in range(nr):
417
+ temp += tempmat[
418
+ iorb, jorb, js, ks, ir, ift
419
+ ] * np.exp(
420
+ -2.0j
421
+ * np.pi
422
+ * (kpoint[ik] @ self.crystal.rvec[ir])
423
+ )
424
+ [a, m1] = self.crystal.FAtomOrb(iorb)
425
+ [b, m2] = self.crystal.FAtomOrb(jorb)
426
+ delta = (
427
+ self.crystal.basisf[a, :]
428
+ - self.crystal.basisf[b, :]
429
+ )
430
+ phase = np.exp(-2.0j * np.pi * (kpoint[ik] @ delta))
431
+ matk[iorb, jorb, js, ks, ik, ift] = temp * phase
432
+
433
+ return matk
434
+
435
+ def CheckGroup(self, filepath: str, group: str):
436
+ """Ensure that the requested HDF5 group exists before writing."""
437
+ with h5py.File(filepath, "r") as file:
438
+ return group in file
439
+
440
+ def RT2mRmT(self, ftau: np.ndarray):
441
+ """Reorder real-space and time axes into mirrored conventions."""
442
+ ftau_mr = self.crystal.R2mR(ftau)
443
+ norb, _, ns, nr, ntau = ftau_mr.shape
444
+ fmtau_mr = np.zeros((norb, norb, ns, nr, ntau), dtype=np.complex128, order="F")
445
+
446
+ for ir in range(nr):
447
+ for js in range(ns):
448
+ for jorb in range(norb):
449
+ for iorb in range(norb):
450
+ fmtau_mr[iorb, jorb, js, ir] = self.dlr.T2mT(
451
+ ftau_mr[iorb, jorb, js, ir]
452
+ )
453
+ # fmtau_mr = self.dlr.T2mT(ftau_mr)
454
+
455
+ return fmtau_mr
456
+
457
+ def TauF2TauB(self, ftau : np.ndarray) -> np.ndarray:
458
+ """Convert fermionic imaginary-time samples to bosonic time ordering."""
459
+
460
+ norb, _, ns, nk, _ = ftau.shape
461
+ ntau = len(self.dlr.tauB)
462
+ fout = np.zeros((norb, norb, ns, nk, ntau), dtype=np.complex128, order='F')
463
+
464
+ for ik in range(nk):
465
+ for js in range(ns):
466
+ for jorb, iorb in itertools.product(range(norb), repeat=2):
467
+ tempmat = ftau[iorb, jorb, js, ik]
468
+ fout[iorb, jorb, js, ik] = self.dlr.TauF2TauB(tempmat)
469
+
470
+ return fout
471
+
472
+
473
+ class P(BLatDyn):
474
+ """Bosonic polarization calculator built from fermionic Green functions."""
475
+ def __init__(self,crystal: Crystal,dlr: DLR,g: np.ndarray = None,hdf5file: str = "glob.h5",group: str = None,):
476
+ """Initialize the polarization calculator from Green-function data."""
477
+ super().__init__(crystal, dlr)
478
+ norb = len(self.crystal.find)
479
+ ns = self.crystal.ns
480
+ nrk = self.crystal.nk
481
+ nfreq = len(self.dlr.nu)
482
+ ntau = len(self.dlr.tauB)
483
+ self.rt = np.zeros(
484
+ (norb*norb, norb*norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
485
+ )
486
+ self.kt = np.zeros(
487
+ (norb*norb, norb*norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
488
+ )
489
+ self.rf = np.zeros(
490
+ (norb*norb, norb*norb, ns, ns, nrk, nfreq), dtype=np.complex128, order="F"
491
+ )
492
+ self.kf = np.zeros(
493
+ (norb*norb, norb*norb, ns, ns, nrk, nfreq), dtype=np.complex128, order="F"
494
+ )
495
+ self.hdf5file = hdf5file
496
+ self.group = group
497
+ self.subgroup = self.__class__.__name__
498
+ if g is None:
499
+ print("Error, There is no Green's function.")
500
+ sys.exit()
501
+ self.g = g
502
+
503
+ print("Polarizability Calculation Start")
504
+ start = time.time()
505
+ self.Cal()
506
+ self.kt = self.R2K(self.rt)
507
+
508
+ self.rf = self.T2F(self.rt)
509
+ self.kf = self.T2F(self.kt)
510
+ end = time.time()
511
+ print("Polarizability Calculation Done")
512
+ print(f"Calculation Time : {str(datetime.timedelta(seconds=end-start))}")
513
+
514
+ def Cal(self):
515
+ """Compute the primary array represented by this object."""
516
+
517
+ ns = self.crystal.ns
518
+ nrk = len(self.crystal.kpoint)
519
+
520
+ ntau = len(self.dlr.tauB)
521
+
522
+ grt = self.TauF2TauB(self.g)
523
+
524
+ norb = len(self.crystal.bind)
525
+
526
+ polrt = np.zeros(
527
+ (norb, norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
528
+ )
529
+
530
+ # gmrt = self.crystal.RT2mRmT(grt)
531
+ gmrt = self.RT2mRmT(grt)
532
+
533
+ if ns == 2:
534
+ map0 = np.array([self.crystal.MappingBosonFermion(i)[0] for i in range(norb)])
535
+ map1 = np.array([self.crystal.MappingBosonFermion(i)[1] for i in range(norb)])
536
+
537
+ term1_tensor = gmrt[map1[np.newaxis, :], map0[:, np.newaxis], :, :, :]
538
+ term2_tensor = grt[map1[:, np.newaxis], map0[np.newaxis, :], :, :, :]
539
+ diagonal_product = term1_tensor * term2_tensor
540
+ s_indices = np.arange(ns)
541
+
542
+ polrt[:, :, s_indices, s_indices, :, :] = diagonal_product
543
+
544
+ else:
545
+ if self.crystal.soc == True:
546
+ C = 1
547
+ map0 = np.array([self.crystal.MappingBosonFermion(i)[0] for i in range(norb)])
548
+ map1 = np.array([self.crystal.MappingBosonFermion(i)[1] for i in range(norb)])
549
+
550
+ term1_slice = gmrt[map1[np.newaxis, :], map0[:, np.newaxis], 0, :, :]
551
+ term2_slice = grt[map1[:, np.newaxis], map0[np.newaxis, :], 0, :, :]
552
+ result_slice = term1_slice * term2_slice * C
553
+ polrt[:, :, 0, 0, :, :] = result_slice
554
+
555
+ else:
556
+ C = 2
557
+ map0 = np.array([self.crystal.MappingBosonFermion(i)[0] for i in range(norb)])
558
+ map1 = np.array([self.crystal.MappingBosonFermion(i)[1] for i in range(norb)])
559
+
560
+ term1_slice = gmrt[map1[np.newaxis, :], map0[:, np.newaxis], 0, :, :]
561
+ term2_slice = grt[map1[:, np.newaxis], map0[np.newaxis, :], 0, :, :]
562
+ result_slice = term1_slice * term2_slice * C
563
+ polrt[:, :, 0, 0, :, :] = result_slice
564
+
565
+ self.rt = polrt
566
+
567
+
568
+ return None
569
+
570
+ def Save(self, fn: str):
571
+ """Persist calculated arrays to the configured HDF5 output group."""
572
+ with h5py.File(self.hdf5file, "a") as file:
573
+ if self.CheckGroup(self.hdf5file, self.group):
574
+ group = file[self.group]
575
+ if self.subgroup in group:
576
+ p = group[self.subgroup]
577
+ else:
578
+ p = group.create_group(self.subgroup)
579
+ else:
580
+ group = file.create_group(self.group)
581
+ p = group.create_group(self.subgroup)
582
+ p.create_dataset(fn, dtype=complex, data=self.kf)
583
+
584
+ return None
585
+
586
+
587
+ class W(BLatDyn):
588
+ """Screened interaction calculator for GW workflows."""
589
+ def __init__(self,crystal: Crystal,dlr: DLR,p: np.ndarray = None,v: V = None,c: float = 1.0,hdf5file: str = "glob.h5", group: str = None,):
590
+ """Initialize the screened-interaction calculator."""
591
+ super().__init__(crystal, dlr)
592
+ norb = len(self.crystal.bind)
593
+ ns = self.crystal.ns
594
+ nrk = self.crystal.nk
595
+ nfreq = len(self.dlr.nu)
596
+ ntau = len(self.dlr.tauB)
597
+
598
+ # W quantity
599
+ self.rt = np.zeros(
600
+ (norb, norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
601
+ )
602
+ self.kt = np.zeros(
603
+ (norb, norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
604
+ )
605
+ self.rf = np.zeros(
606
+ (norb, norb, ns, ns, nrk, nfreq), dtype=np.complex128, order="F"
607
+ )
608
+ self.kf = np.zeros(
609
+ (norb, norb, ns, ns, nrk, nfreq), dtype=np.complex128, order="F"
610
+ )
611
+
612
+ # Wc quantity
613
+ self.crt = np.zeros(
614
+ (norb, norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
615
+ ) # rt to kf
616
+ self.ckt = np.zeros(
617
+ (norb, norb, ns, ns, nrk, ntau), dtype=np.complex128, order="F"
618
+ )
619
+ self.crf = np.zeros(
620
+ (norb, norb, ns, ns, nrk, nfreq), dtype=np.complex128, order="F"
621
+ )
622
+ self.ckf = np.zeros(
623
+ (norb, norb, ns, ns, nrk, nfreq), dtype=np.complex128, order="F"
624
+ )
625
+
626
+ self.c = c
627
+ self.hdf5file = hdf5file
628
+ self.group = group
629
+ self.subgroup = self.__class__.__name__
630
+ if p is None:
631
+ print("Error, polarizability doesn't exist")
632
+ sys.exit()
633
+ if v is None:
634
+ print("Error, bare coulomb interaction doesn't exist")
635
+ sys.exit()
636
+ self.p = p
637
+ self.v = v
638
+
639
+ print("Screened Coulomb Interaction Calculation Start")
640
+ start = time.time()
641
+ self.Cal()
642
+
643
+ # self.wkt = self.F2T(self.wkf,1,1)
644
+ # self.wrf = self.K2R(self.wkf)
645
+ # self.wrt = self.K2R(self.wkt)
646
+
647
+ print(f"Fourier transform in {self.__class__.__name__} start")
648
+ self.ckt = self.F2T(self.ckf)
649
+ self.crf = self.K2R(self.ckf)
650
+ self.crt = self.K2R(self.ckt)
651
+ end= time.time()
652
+ print(f"Fourier transform in {self.__class__.__name__} finish")
653
+ print("Screened Coulomb Interaction Calculation Finish")
654
+ print(f"Screened Coulomb interaction use time : {datetime.timedelta(seconds=end - start)} s")
655
+
656
+ def Cal(self): # calculate W and Wc
657
+ """Compute the primary array represented by this object."""
658
+ norb = len(self.crystal.bind)
659
+ norbc = len(self.crystal.find)
660
+ ns = self.crystal.ns
661
+ nk = len(self.crystal.kpoint)
662
+ nfreq = len(self.dlr.nu)
663
+ ####### Initialization #######
664
+ tempmat = np.zeros(
665
+ (norbc * norbc, norbc * norbc, ns, ns, nk, nfreq),
666
+ dtype=np.complex128,
667
+ order="F",
668
+ )
669
+ wkf = np.zeros((norb, norb, ns, ns, nk, nfreq), dtype=np.complex128, order="F")
670
+ wckf = np.zeros((norb, norb, ns, ns, nk, nfreq), dtype=np.complex128, order="F")
671
+ vdyn = np.zeros((norb, norb, ns, ns, nk, nfreq), dtype=np.complex128, order="F")
672
+
673
+ # for ifreq in range(nfreq):
674
+ # vdyn[...,ifreq] = self.v.k
675
+ print("Make dynamic bare Coulomb interaction start")
676
+ vdyn = self.StcEmbedding(self.v.k)
677
+ print("Make dynamic bare Coulomb interaction finish")
678
+ polcomp = np.zeros(
679
+ (norbc * norbc, norbc * norbc, ns, ns, nk, nfreq),
680
+ dtype=np.complex128,
681
+ order="F",
682
+ )
683
+ vcomp = np.zeros(
684
+ (norbc * norbc, norbc * norbc, ns, ns, nk, nfreq),
685
+ dtype=np.complex128,
686
+ order="F",
687
+ )
688
+ ####### Initialization #######
689
+ polcomp = self.Double2Full(self.p) * self.c
690
+ # del self.p
691
+ vcomp = self.Double2Full(vdyn)
692
+
693
+ print("Dyson equation solving start")
694
+ start = time.time()
695
+ tempmat = self.Dyson(vcomp, polcomp)
696
+ wkf = self.Full2Double(tempmat)
697
+ end = time.time()
698
+ # print(f"Dyson equation solving use time: {end - start} s")
699
+ print("Dyson equation solving finish")
700
+ print(f"Dyson equation solving use time : {datetime.timedelta(seconds=end - start)} s")
701
+
702
+ self.kf = wkf
703
+
704
+ wckf = wkf - vdyn
705
+
706
+ self.ckf = wckf
707
+
708
+ return None
709
+
710
+ def Save(self, fn: str):
711
+ """Persist calculated arrays to the configured HDF5 output group."""
712
+ with h5py.File(self.hdf5file, "a") as file:
713
+ if self.CheckGroup(self.hdf5file, self.group):
714
+ group = file[self.group]
715
+ if self.subgroup in group:
716
+ w = group[self.subgroup]
717
+ else:
718
+ w = group.create_group(self.subgroup)
719
+ else:
720
+ group = file.create_group(self.group)
721
+ w = group.create_group(self.subgroup)
722
+
723
+ w.create_dataset(fn, dtype=complex, data=self.kf)
724
+
725
+ return None