High-precision calculations for one- and two-valence atomic systems
SpinorMatrix.hpp
1#pragma once
2#include "LinAlg/Matrix.hpp"
3#include "Maths/Grid.hpp"
4#include "Maths/Interpolator.hpp"
5#include "RadialMatrix.hpp"
6#include "Wavefunction/DiracSpinor.hpp"
7#include <algorithm>
8#include <cassert>
9#include <complex>
10#include <cstdlib>
11#include <iostream>
12#include <memory>
13#include <type_traits>
14
15namespace MBPT {
16
17/*!
18 @brief Radial Dirac spinor matrix.
19
20 @details
21 Designed to store Greens-function like operators: \f$ \ket{F_a}\bra{F_b} \f$
22 (where Fa, Fb are radial Dirac spinors), as a radial matrix. The matrix is
23 stored on a sub-grid (between r0 and rmax), with a specified stride.
24
25 SpinorMatrix is a 2*2 matrix in spinor space {ff, fg, gf, gg} - the g blocks
26 are small and are optional. Each block is an N*N radial matrix, where N is a
27 subset of the number of points along the full radial grid. May store doubles
28 or complex doubles.
29
30 \f[
31 \begin{align}
32 G &= \begin{pmatrix} G_{ff} & G_{fg} \\ G_{gf} & G_{gg} \end{pmatrix},\\
33 G F &= \begin{pmatrix} G_{ff} & G_{fg} \\ G_{gf} & G_{gg} \end{pmatrix}
34 \begin{pmatrix} f \\ g \end{pmatrix}
35 = \begin{pmatrix}
36 G_{ff}(r,r')\,f(r') + G_{fg}(r,r')\,g(r') \\
37 G_{gf}(r,r')\,f(r') + G_{gg}(r,r')\,g(r')
38 \end{pmatrix}.
39 \end{align}
40 \f]
41
42 Careful to distinguish SpinorMatrix multiplication/integration:
43 \f[
44 \begin{align}
45 G_1 * G_2 &= \int G_1(r_a,r_b)\,G_2(r_b,r_c)
46 = \sum_j G_1(i,j)\,G_2(j,k),\\
47 G_1.{\rm drj}() * G_2 &= \int G_1(r_a,r_b)\,G_2(r_b,r_c)\,dr_b
48 = \sum_j G_1(i,j)\,G_2(j,k)\,dr_j
49 = G_1 * G_2.{\rm dri}(),
50 \end{align}
51 \f]
52 where \f$ dr_j = (dr/du)_j \times du \times {\rm stride} \f$ (see dr()).
53
54 While almost always symmetric, this doesn't assume that.
55*/
56template <typename T>
58
59 std::size_t m_i0, m_stride;
60 std::size_t m_size;
61 std::size_t m_g_size;
62 LinAlg::Matrix<T> m_ff, m_fg, m_gf, m_gg;
63 bool m_incl_g;
64 std::shared_ptr<const Grid> m_rgrid; // "full" grid
65 std::vector<double> sub_r{}; // sub grid
66
67public:
68 //============================================================================
69
70 /*!
71 @brief Constructs a zero matrix on the given radial sub-grid.
72
73 @details
74 Sub-grid point i is full-grid point i0 + i*stride (see index_to_fullgrid).
75 The last sub-grid point must lie on the full grid (checked by assert).
76
77 @param i0 Full-grid index of the first sub-grid point
78 @param stride Sub-grid stride: every stride-th point of the full grid
79 @param size Number of sub-grid points (each block is size*size)
80 @param incl_g Include the g blocks (fg, gf, gg)
81 @param rgrid Full radial grid
82 */
83 SpinorMatrix(std::size_t i0, std::size_t stride, std::size_t size,
84 bool incl_g, std::shared_ptr<const Grid> rgrid)
85 : m_i0(i0),
86 m_stride(stride),
87 m_size(size),
88 m_g_size(incl_g ? size : 0),
89 m_ff(m_size),
90 m_fg(m_g_size),
91 m_gf(m_g_size),
92 m_gg(m_g_size),
93 m_incl_g(incl_g),
94 m_rgrid(rgrid) {
95 //------------------
96 // create vector of r on sub-grid, used to interpolate values onto full
97 const auto &r = m_rgrid->r();
98 sub_r.reserve(m_size);
99 // last sub-grid point, i0 + stride*(size-1), must lie on the full grid
100 assert(m_size > 0 && index_to_fullgrid(m_size - 1) < r.size());
101 for (std::size_t i = 0; i < m_size; ++i) {
102 sub_r.push_back(r[index_to_fullgrid(i)]);
103 }
104 assert(m_size < 2 || sub_r[1] == r[index_to_fullgrid(1)]);
105 assert(sub_r[m_size - 1] == r[index_to_fullgrid(m_size - 1)]);
106 //------------------
107 }
108
109 //============================================================================
110 //! direct access to matrix elements
111 T &ff(std::size_t i, std::size_t j) { return m_ff(i, j); }
112 T &fg(std::size_t i, std::size_t j) { return m_fg(i, j); }
113 T &gf(std::size_t i, std::size_t j) { return m_gf(i, j); }
114 T &gg(std::size_t i, std::size_t j) { return m_gg(i, j); }
115 const T ff(std::size_t i, std::size_t j) const { return m_ff(i, j); }
116 const T fg(std::size_t i, std::size_t j) const { return m_fg(i, j); }
117 const T gf(std::size_t i, std::size_t j) const { return m_gf(i, j); }
118 const T gg(std::size_t i, std::size_t j) const { return m_gg(i, j); }
119
120 //! direct access to matrices
121 const LinAlg::Matrix<T> &ff() const { return m_ff; }
122 const LinAlg::Matrix<T> &fg() const { return m_fg; }
123 const LinAlg::Matrix<T> &gf() const { return m_gf; }
124 const LinAlg::Matrix<T> &gg() const { return m_gg; }
125 LinAlg::Matrix<T> &ff() { return m_ff; }
126 LinAlg::Matrix<T> &fg() { return m_fg; }
127 LinAlg::Matrix<T> &gf() { return m_gf; }
128 LinAlg::Matrix<T> &gg() { return m_gg; }
129
130 /*!
131 @brief Spinor block by index: 0 = f, 1 = g.
132
133 @details
134 (mu, nu) = (0, 0): ff; (0, 1): fg; (1, 0): gf; (1, 1): gg.
135
136 @note The g blocks are empty (0*0) if g parts are not included.
137 @warning @p mu and @p nu must be 0 or 1; aborts otherwise.
138 */
139 LinAlg::Matrix<T> &sp(std::size_t mu, std::size_t nu) {
140 assert(mu < 2 && nu < 2);
141 if (mu == 0 && nu == 0)
142 return m_ff;
143 if (mu == 0 && nu == 1)
144 return m_fg;
145 if (mu == 1 && nu == 0)
146 return m_gf;
147 if (mu == 1 && nu == 1)
148 return m_gg;
149 assert(false && "SpinorMatrix::sp: mu, nu must be 0 or 1");
150 std::abort();
151 }
152
153 //! Spinor block by index: 0 = f, 1 = g (const)
154 const LinAlg::Matrix<T> &sp(std::size_t mu, std::size_t nu) const {
155 assert(mu < 2 && nu < 2);
156 if (mu == 0 && nu == 0)
157 return m_ff;
158 if (mu == 0 && nu == 1)
159 return m_fg;
160 if (mu == 1 && nu == 0)
161 return m_gf;
162 if (mu == 1 && nu == 1)
163 return m_gg;
164 assert(false && "SpinorMatrix::sp: mu, nu must be 0 or 1");
165 std::abort();
166 }
167
168 //! Number of sub-grid points (dimension of each block)
169 std::size_t size() const { return m_size; }
170 //! Dimension of the g blocks: size(), or 0 if g parts are not included
171 std::size_t g_size() const { return m_g_size; }
172 //! True if g parts (fg, gf, gg) are included
173 bool includes_g() const { return m_g_size == m_size; };
174 //! Full-grid index of the first sub-grid point
175 std::size_t i0() const { return m_i0; }
176 //! Sub-grid stride: every stride-th point of the full grid
177 std::size_t stride() const { return m_stride; }
178
179 //============================================================================
180 //! Sets all matrix elements to zero
181 void zero() {
182 m_ff.zero();
183 m_fg.zero();
184 m_gf.zero();
185 m_gg.zero();
186 }
187
188 //============================================================================
189 //! Kills g parts of spinor matrix, in place!
191 m_g_size = 0;
192 m_incl_g = false;
193 m_fg.resize(0, 0);
194 m_gf.resize(0, 0);
195 m_gg.resize(0, 0);
196 return *this;
197 }
198
199 /*!
200 @brief Creates g parts of spinor matrix - will have value 0.
201
202 @note If g parts already exist, they are reset to 0.
203 */
205 m_g_size = m_size;
206 m_incl_g = true;
207 m_fg.resize(m_size, m_size);
208 m_gf.resize(m_size, m_size);
209 m_gg.resize(m_size, m_size);
210 return *this;
211 }
212
213 //============================================================================
214 /*!
215 @brief Matrix addition +,-.
216
217 @details
218 A matrix without g parts is treated as having zero g parts: adding one to
219 a matrix with g parts leaves its g parts unchanged; adding a matrix with g
220 parts to one without creates them.
221 */
223 m_ff += rhs.m_ff;
224 if (rhs.m_incl_g) {
225 if (!m_incl_g) {
226 create_g();
227 }
228 m_fg += rhs.m_fg;
229 m_gf += rhs.m_gf;
230 m_gg += rhs.m_gg;
231 }
232 return *this;
233 }
234 //! Matrix addition +,- (see operator+= for matrices without g parts)
236 m_ff -= rhs.m_ff;
237 if (rhs.m_incl_g) {
238 if (!m_incl_g) {
239 create_g();
240 }
241 m_fg -= rhs.m_fg;
242 m_gf -= rhs.m_gf;
243 m_gg -= rhs.m_gg;
244 }
245 return *this;
246 }
247 //! Scalar multiplication
249 m_ff *= x;
250 m_fg *= x;
251 m_gf *= x;
252 m_gg *= x;
253 return *this;
254 }
255
256 //! Matrix addition +,-
257 [[nodiscard]] friend SpinorMatrix<T> operator+(SpinorMatrix<T> lhs,
258 const SpinorMatrix<T> &rhs) {
259 return (lhs += rhs);
260 }
261 //! Matrix addition +,-
262 [[nodiscard]] friend SpinorMatrix<T> operator-(SpinorMatrix<T> lhs,
263 const SpinorMatrix<T> &rhs) {
264 return (lhs -= rhs);
265 }
266 //! Scalar multiplication
267 [[nodiscard]] friend SpinorMatrix<T> operator*(const T x,
268 SpinorMatrix<T> rhs) {
269 return (rhs *= x);
270 }
271
272 //! Addition of identity: Matrix<T> += T : T assumed to be *Identity!
274 m_ff += aI;
275 m_gg += aI;
276 return *this;
277 }
278 //! Addition of identity: Matrix<T> -= T : T assumed to be *Identity!
280 m_ff -= aI;
281 m_gg -= aI;
282 return *this;
283 }
284
285 //! Addition of identity: Matrix<T> + T : T assumed to be *Identity!
286 [[nodiscard]] friend SpinorMatrix<T> operator+(SpinorMatrix<T> M, T aI) {
287 return (M += aI);
288 }
289 //! Addition of identity: Matrix<T> - T : T assumed to be *Identity!
290 [[nodiscard]] friend SpinorMatrix<T> operator-(SpinorMatrix<T> M, T aI) {
291 return (M -= aI);
292 }
293
294 //============================================================================
295
296 /*!
297 @brief Matrix multiplication.
298
299 @details
300 \f$ C=A\times B \equiv C_{ij} = \sum_k A_{ik}\,B_{kj}, \f$ including the
301 sum over spinor blocks (e.g., ff = a.ff*b.ff + a.fg*b.gf).
302
303 A matrix without g parts is treated as having zero g parts (as in
304 operator+=); the result includes g parts if either @p a or @p b does.
305
306 @note Integration measure not automatically included: call .drj() first
307 to include it!
308 */
309 [[nodiscard]] friend SpinorMatrix<T> operator*(const SpinorMatrix<T> &a,
310 const SpinorMatrix<T> &b) {
311
312 SpinorMatrix<T> out(a.m_i0, a.m_stride, a.m_size, a.m_incl_g || b.m_incl_g,
313 a.m_rgrid);
314
315 // FF = FF*FF + FG*GF
316 // FG = FF*FG + FG*GG
317 // GF = GF*FF + GG*GF
318 // GG = GF*FG + GG*GG
319 out.ff() = a.ff() * b.ff();
320 if (a.m_incl_g && b.m_incl_g) {
321 out.ff() += a.fg() * b.gf();
322 out.fg() = a.ff() * b.fg() + a.fg() * b.gg();
323 out.gf() = a.gf() * b.ff() + a.gg() * b.gf();
324 out.gg() = a.gf() * b.fg() + a.gg() * b.gg();
325 } else if (a.m_incl_g) {
326 // b has zero g parts: only GF = GF*FF survives
327 out.gf() = a.gf() * b.ff();
328 } else if (b.m_incl_g) {
329 // a has zero g parts: only FG = FF*FG survives
330 out.fg() = a.ff() * b.fg();
331 }
332 return out;
333 }
334
335 //============================================================================
336 //! Multiply elements (in place): Gij -> Gij*Bij
338 m_ff.mult_elements_by(rhs.ff());
339 if (this->m_incl_g) {
340 if (rhs.m_incl_g) {
341 m_fg.mult_elements_by(rhs.fg());
342 m_gf.mult_elements_by(rhs.gf());
343 m_gg.mult_elements_by(rhs.gg());
344 } else {
345 // rhs has zero g parts
346 m_fg.zero();
347 m_gf.zero();
348 m_gg.zero();
349 }
350 }
351 return *this;
352 }
353 //! Multiply elements (new matrix): Gij = Aij*Bij
354 [[deprecated]] [[nodiscard]] friend SpinorMatrix<T>
356 lhs.mult_elements_by(rhs);
357 return lhs;
358 }
359
360 //============================================================================
361 //! Multiply elements (in place): Gij -> Gij*Bij
363 m_ff.mult_elements_by(rhs.Rmatrix());
364 if (this->m_incl_g) {
365 m_fg.mult_elements_by(rhs.Rmatrix());
366 m_gf.mult_elements_by(rhs.Rmatrix());
367 m_gg.mult_elements_by(rhs.Rmatrix());
368 }
369 return *this;
370 }
371
372 //! Multiply elements (new matrix): Gij = Aij*Bij
373 [[nodiscard]] friend SpinorMatrix<T>
375 lhs.mult_elements_by(rhs);
376 return lhs;
377 }
378
379 //! Multiply elements (new matrix): Gij = Aij*Bij
380 [[nodiscard]] friend SpinorMatrix<T> mult_elements(const RadialMatrix<T> &rhs,
381 SpinorMatrix<T> lhs) {
382 lhs.mult_elements_by(rhs);
383 return lhs;
384 }
385
386 //============================================================================
387
388 //! Returns conjugate of matrix
389 [[nodiscard]] SpinorMatrix<T> conj() const {
390 auto out = *this;
391 out.ff().conj_in_place();
392 out.fg().conj_in_place();
393 out.gf().conj_in_place();
394 out.gg().conj_in_place();
395 return out;
396 }
397 //! Returns real part of complex matrix (changes type; returns a real matrix)
398 [[nodiscard]] SpinorMatrix<double> real() const {
399 SpinorMatrix<double> out(m_i0, m_stride, m_size, m_incl_g, m_rgrid);
400 out.ff() = m_ff.real();
401 out.fg() = m_fg.real();
402 out.gf() = m_gf.real();
403 out.gg() = m_gg.real();
404 return out;
405 }
406 //! Returns imag part of complex matrix (changes type; returns a real matrix)
407 [[nodiscard]] SpinorMatrix<double> imag() const {
408 SpinorMatrix<double> out(m_i0, m_stride, m_size, m_incl_g, m_rgrid);
409 out.ff() = m_ff.imag();
410 out.fg() = m_fg.imag();
411 out.gf() = m_gf.imag();
412 out.gg() = m_gg.imag();
413 return out;
414 }
415 //! Converts a real to complex matrix (changes type; returns a complex matrix)
417 SpinorMatrix<std::complex<double>> out(m_i0, m_stride, m_size, m_incl_g,
418 m_rgrid);
419 out.ff() = m_ff.complex();
420 out.fg() = m_fg.complex();
421 out.gf() = m_gf.complex();
422 out.gg() = m_gg.complex();
423 return out;
424 }
425
426 //============================================================================
427 //! Inversion (in place)
429 m_ff.invert_in_place();
430 if (m_incl_g) {
431 const auto &ai = m_ff; // already inverted
432 const auto &b = m_fg;
433 const auto &c = m_gf;
434 const auto &d = m_gg;
435 const auto cai = c * ai;
436 const auto dmcaib = (d - cai * b).invert_in_place();
437 const auto aib_dmcaib = ai * b * dmcaib;
438 m_ff += aib_dmcaib * cai;
439 m_fg = -1.0 * aib_dmcaib;
440 m_gf = -1.0 * dmcaib * cai;
441 m_gg = dmcaib;
442 }
443 return *this;
444 }
445 //! Returns inverse of matrix; original matrix unchanged
446 [[nodiscard]] SpinorMatrix<T> inverse() const {
447 auto out = *this; //
448 return out.invert_in_place();
449 }
450
451 //============================================================================
452 //! Multiplies by drj: Q_ij -> Q_ij*dr_j, in place
454 const auto dus = m_rgrid->du() * double(m_stride);
455 for (auto i = 0ul; i < m_size; ++i) {
456 for (auto j = 0ul; j < m_size; ++j) {
457 const auto sj = index_to_fullgrid(j);
458 const auto dr = m_rgrid->drdu(sj) * dus;
459 m_ff[i][j] *= dr;
460 }
461 }
462 if (m_incl_g) {
463 for (auto i = 0ul; i < m_size; ++i) {
464 for (auto j = 0ul; j < m_size; ++j) {
465 const auto sj = index_to_fullgrid(j);
466 const auto dr = m_rgrid->drdu(sj) * dus;
467 m_fg[i][j] *= dr;
468 m_gf[i][j] *= dr;
469 m_gg[i][j] *= dr;
470 }
471 }
472 }
473 return *this;
474 }
475 //! Multiplies by dri: Q_ij -> Q_ij*dr_i, in place
477 const auto dus = m_rgrid->du() * double(m_stride);
478 for (auto i = 0ul; i < m_size; ++i) {
479 const auto si = index_to_fullgrid(i);
480 const auto dr = m_rgrid->drdu(si) * dus;
481 for (auto j = 0ul; j < m_size; ++j) {
482 m_ff[i][j] *= dr;
483 }
484 }
485 if (m_incl_g) {
486 for (auto i = 0ul; i < m_size; ++i) {
487 const auto si = index_to_fullgrid(i);
488 const auto dr = m_rgrid->drdu(si) * dus;
489 for (auto j = 0ul; j < m_size; ++j) {
490 m_fg[i][j] *= dr;
491 m_gf[i][j] *= dr;
492 m_gg[i][j] *= dr;
493 }
494 }
495 }
496 return *this;
497 }
498 //! Multiplies by drj: Q_ij -> Q_ij*dr_j. Returns new matrix (orig unchanged)
500 auto out = *this;
501 return out.drj_in_place();
502 }
503 //! Multiplies by dri: Q_ij -> Q_ij*dr_i. Returns new matrix (orig unchanged)
505 auto out = *this;
506 return out.dri_in_place();
507 }
508
509 //! returns dr at position along sub grid
510 double dr(std::size_t sub_index) const {
511 const auto full_index = index_to_fullgrid(sub_index);
512 return m_rgrid->drdu(full_index) * m_rgrid->du() * double(m_stride);
513 }
514
515 //============================================================================
516 //! Converts an index on the sub-grid to the full grid.
517 std::size_t index_to_fullgrid(std::size_t i) const {
518 return m_i0 + i * m_stride;
519 }
520
521 //============================================================================
522 //! Adds k*|ket><bra| to matrix (used for building Green's functions)
523 void add(const DiracSpinor &ket, const DiracSpinor &bra, T k = T(1.0)) {
524 // Adds (k)*|ket><bra| to G matrix
525 // G_ij = f * Q_i * W_j
526 // Q = Q(1) = ket, W = W(2) = bra
527 // Takes sub-grid into account; ket,bra are on full grid, G on sub-grid
528 for (auto i = 0ul; i < m_size; ++i) {
529 const auto si = index_to_fullgrid(i);
530 for (auto j = 0ul; j < m_size; ++j) {
531 const auto sj = index_to_fullgrid(j);
532 m_ff[i][j] += k * ket.f(si) * bra.f(sj);
533 }
534 }
535
536 if (m_incl_g) {
537 for (auto i = 0ul; i < m_size; ++i) {
538 const auto si = index_to_fullgrid(i);
539 for (auto j = 0ul; j < m_size; ++j) {
540 const auto sj = index_to_fullgrid(j);
541 // XXX Double check fg/gf right way!
542 m_fg[i][j] += k * ket.f(si) * bra.g(sj);
543 m_gf[i][j] += k * ket.g(si) * bra.f(sj); // symmetric, transpose?
544 m_gg[i][j] += k * ket.g(si) * bra.g(sj);
545 }
546 }
547 }
548 }
549
550 //============================================================================
551 /*!
552 @brief Action of SpinorMatrix operator on DiracSpinor.
553
554 @details
555 Assumes matrix already includes integration measure. Formed on the
556 sub-grid, then interpolated onto the full grid of @p Fn. Result is zero
557 outside the sub-grid.
558
559 @note If g parts are not included, the g component of the result is zero
560 (and the g component of @p Fn is not used).
561 */
563
564 const auto &r = Fn.grid().r();
565
566 std::vector<double> f(m_size), g;
567 for (auto i = 0ul; i < m_size; ++i) {
568 for (auto j = 0ul; j < m_size; ++j) {
569 const auto j_f = index_to_fullgrid(j);
570 f[i] += m_ff(i, j) * Fn.f(j_f);
571 }
572 }
573 if (m_incl_g) {
574 g.resize(m_size);
575 for (auto i = 0ul; i < m_size; ++i) {
576 for (auto j = 0ul; j < m_size; ++j) {
577 const auto j_f = index_to_fullgrid(j);
578 f[i] += m_fg(i, j) * Fn.g(j_f);
579 g[i] += (m_gf(i, j) * Fn.f(j_f) + m_gg(i, j) * Fn.g(j_f));
580 }
581 }
582 }
583
584 DiracSpinor out = Fn * 0.0;
585 // Interpolate from sub-grid to full grid
586 out.f() = Interpolator::interpolate(sub_r, f, r);
587 if (m_incl_g) {
588 out.g() = Interpolator::interpolate(sub_r, g, r);
589 }
590 return out;
591 }
592
593 //============================================================================
594 // For testing only:
595 friend std::ostream &operator<<(std::ostream &os, const SpinorMatrix<T> &a) {
596 os << "FF:\n";
597 os << a.m_ff;
598 if (a.m_incl_g) {
599 os << "FG:\n";
600 os << a.m_fg;
601 os << "GF:\n";
602 os << a.m_gf;
603 os << "GG:\n";
604 os << a.m_gg;
605 }
606 return os;
607 }
608};
609
610//! Checks if two matrices are equal (to within parts in 10^12)
611template <typename T>
612bool equal(const SpinorMatrix<T> &lhs, const SpinorMatrix<T> &rhs) {
613 return equal(lhs.ff(), rhs.ff()) && equal(lhs.fg(), rhs.fg()) &&
614 equal(lhs.gf(), rhs.gf()) && equal(lhs.gg(), rhs.gg());
615}
616
617//! returns maximum element (by abs)
618template <typename T>
619double max_element(const SpinorMatrix<T> &a) {
620 double xff = 0.0, xfg = 0.0, xgf = 0.0, xgg = 0.0;
621 for (auto i = 0ul; i < a.size(); ++i) {
622 for (auto j = 0ul; j < a.size(); ++j) {
623 if (std::abs(a.ff(i, j)) > xff)
624 xff = std::abs(a.ff(i, j));
625 if (a.g_size() != 0) {
626 if (std::abs(a.fg(i, j)) > xfg)
627 xfg = std::abs(a.fg(i, j));
628 if (std::abs(a.gf(i, j)) > xgf)
629 xgf = std::abs(a.gf(i, j));
630 if (std::abs(a.gg(i, j)) > xgg)
631 xgg = std::abs(a.gg(i, j));
632 }
633 }
634 }
635 return std::max({xff, xfg, xgf, xgg});
636}
637
638/*!
639 @brief Returns maximum difference (abs) between two matrices.
640
641 @details
642 A matrix without g parts is treated as having zero g parts.
643*/
644template <typename T>
645double max_delta(const SpinorMatrix<T> &a, const SpinorMatrix<T> &b) {
646 if (a.includes_g() != b.includes_g()) {
647 return a.includes_g() ? max_delta(a, SpinorMatrix<T>(b).create_g()) :
648 max_delta(SpinorMatrix<T>(a).create_g(), b);
649 }
650 double xff = 0.0, xfg = 0.0, xgf = 0.0, xgg = 0.0;
651 for (auto i = 0ul; i < a.size(); ++i) {
652 for (auto j = 0ul; j < a.size(); ++j) {
653 if (std::abs(a.ff(i, j) - b.ff(i, j)) > xff)
654 xff = std::abs(a.ff(i, j) - b.ff(i, j));
655 if (a.g_size() != 0) {
656 if (std::abs(a.fg(i, j) - b.fg(i, j)) > xfg)
657 xfg = std::abs(a.fg(i, j) - b.fg(i, j));
658 if (std::abs(a.gf(i, j) - b.gf(i, j)) > xgf)
659 xgf = std::abs(a.gf(i, j) - b.gf(i, j));
660 if (std::abs(a.gg(i, j) - b.gg(i, j)) > xgg)
661 xgg = std::abs(a.gg(i, j) - b.gg(i, j));
662 }
663 }
664 }
665 return std::max({xff, xfg, xgf, xgg});
666}
667
668/*!
669 @brief Returns maximum relative difference (abs) between two matrices.
670
671 @details
672 Maximum over all elements of \f$ |(a_{ij}-b_{ij})/(a_{ij}+b_{ij})| \f$.
673 A matrix without g parts is treated as having zero g parts.
674*/
675template <typename T>
676double max_epsilon(const SpinorMatrix<T> &a, const SpinorMatrix<T> &b) {
677 if (a.includes_g() != b.includes_g()) {
678 return a.includes_g() ? max_epsilon(a, SpinorMatrix<T>(b).create_g()) :
679 max_epsilon(SpinorMatrix<T>(a).create_g(), b);
680 }
681 double xff = 0.0, xfg = 0.0, xgf = 0.0, xgg = 0.0;
682 for (auto i = 0ul; i < a.size(); ++i) {
683 for (auto j = 0ul; j < a.size(); ++j) {
684 const auto eps_ff =
685 std::abs((a.ff(i, j) - b.ff(i, j)) / (a.ff(i, j) + b.ff(i, j)));
686 if (eps_ff > xff)
687 xff = eps_ff;
688 if (a.g_size() != 0) {
689 const auto eps_fg =
690 std::abs((a.fg(i, j) - b.fg(i, j)) / (a.fg(i, j) + b.fg(i, j)));
691 const auto eps_gf =
692 std::abs((a.gf(i, j) - b.gf(i, j)) / (a.gf(i, j) + b.gf(i, j)));
693 const auto eps_gg =
694 std::abs((a.gg(i, j) - b.gg(i, j)) / (a.gg(i, j) + b.gg(i, j)));
695 if (eps_fg > xfg)
696 xfg = eps_fg;
697 if (eps_gf > xgf)
698 xgf = eps_gf;
699 if (eps_gg > xgg)
700 xgg = eps_gg;
701 }
702 }
703 }
704 return std::max({xff, xfg, xgf, xgg});
705}
706
707//==============================================================================
708using GMatrix = SpinorMatrix<double>;
709using ComplexGMatrix = SpinorMatrix<std::complex<double>>;
710using ComplexDouble = std::complex<double>;
711
712} // namespace MBPT
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:55
const std::vector< double > & f() const
Upper (large) radial component function, f.
Definition DiracSpinor.hpp:180
const Grid & grid() const
Returns a const reference to the radial grid.
Definition DiracSpinor.hpp:166
const std::vector< double > & g() const
Lower (small) radial component function, g.
Definition DiracSpinor.hpp:187
const std::vector< double > & r() const
Full grid vector r.
Definition Grid.hpp:131
Row-major dense matrix with arithmetic and linear algebra support.
Definition Matrix.hpp:208
Matrix< T > & invert_in_place()
Inverts the matrix in place.
Definition Matrix.ipp:54
auto complex() const
Converts a real to complex matrix (changes type; returns a complex matrix)
Definition Matrix.ipp:188
auto imag() const
Returns imag part of complex matrix (changes type; returns a real matrix)
Definition Matrix.ipp:177
Matrix< T > & zero()
Sets all elements to zero, in place.
Definition Matrix.ipp:137
Matrix< T > & mult_elements_by(const Matrix< T > &a)
Elementwise multiply in place: M_ij *= a_ij.
Definition Matrix.ipp:268
auto real() const
Returns real part of complex matrix (changes type; returns a real matrix)
Definition Matrix.ipp:166
void resize(std::size_t rows, std::size_t cols)
Resizes matrix to new dimension; all values reset to default.
Definition Matrix.hpp:265
Radial matrix: stored on a sub-grid (defined by i0, stride, size).
Definition RadialMatrix.hpp:26
const LinAlg::Matrix< T > & Rmatrix() const
direct access to radial matrix
Definition RadialMatrix.hpp:83
Radial Dirac spinor matrix.
Definition SpinorMatrix.hpp:57
void add(const DiracSpinor &ket, const DiracSpinor &bra, T k=T(1.0))
Adds k*|ket><bra| to matrix (used for building Green's functions)
Definition SpinorMatrix.hpp:523
SpinorMatrix< T > & operator-=(T aI)
Addition of identity: Matrix<T> -= T : T assumed to be *Identity!
Definition SpinorMatrix.hpp:279
friend SpinorMatrix< T > mult_elements(SpinorMatrix< T > lhs, const SpinorMatrix< T > &rhs)
Multiply elements (new matrix): Gij = Aij*Bij.
Definition SpinorMatrix.hpp:355
SpinorMatrix< std::complex< double > > complex() const
Converts a real to complex matrix (changes type; returns a complex matrix)
Definition SpinorMatrix.hpp:416
SpinorMatrix< T > & create_g()
Creates g parts of spinor matrix - will have value 0.
Definition SpinorMatrix.hpp:204
SpinorMatrix(std::size_t i0, std::size_t stride, std::size_t size, bool incl_g, std::shared_ptr< const Grid > rgrid)
Constructs a zero matrix on the given radial sub-grid.
Definition SpinorMatrix.hpp:83
T & ff(std::size_t i, std::size_t j)
direct access to matrix elements
Definition SpinorMatrix.hpp:111
friend SpinorMatrix< T > operator+(SpinorMatrix< T > M, T aI)
Addition of identity: Matrix<T> + T : T assumed to be *Identity!
Definition SpinorMatrix.hpp:286
SpinorMatrix< T > drj() const
Multiplies by drj: Q_ij -> Q_ij*dr_j. Returns new matrix (orig unchanged)
Definition SpinorMatrix.hpp:499
SpinorMatrix< T > & drj_in_place()
Multiplies by drj: Q_ij -> Q_ij*dr_j, in place.
Definition SpinorMatrix.hpp:453
std::size_t g_size() const
Dimension of the g blocks: size(), or 0 if g parts are not included.
Definition SpinorMatrix.hpp:171
SpinorMatrix< T > & operator*=(const T x)
Scalar multiplication.
Definition SpinorMatrix.hpp:248
SpinorMatrix< T > & dri_in_place()
Multiplies by dri: Q_ij -> Q_ij*dr_i, in place.
Definition SpinorMatrix.hpp:476
std::size_t stride() const
Sub-grid stride: every stride-th point of the full grid.
Definition SpinorMatrix.hpp:177
friend SpinorMatrix< T > mult_elements(SpinorMatrix< T > lhs, const RadialMatrix< T > &rhs)
Multiply elements (new matrix): Gij = Aij*Bij.
Definition SpinorMatrix.hpp:374
const LinAlg::Matrix< T > & ff() const
direct access to matrices
Definition SpinorMatrix.hpp:121
LinAlg::Matrix< T > & sp(std::size_t mu, std::size_t nu)
Spinor block by index: 0 = f, 1 = g.
Definition SpinorMatrix.hpp:139
friend SpinorMatrix< T > operator*(const T x, SpinorMatrix< T > rhs)
Scalar multiplication.
Definition SpinorMatrix.hpp:267
SpinorMatrix< T > & operator-=(const SpinorMatrix< T > &rhs)
Matrix addition +,- (see operator+= for matrices without g parts)
Definition SpinorMatrix.hpp:235
const LinAlg::Matrix< T > & sp(std::size_t mu, std::size_t nu) const
Spinor block by index: 0 = f, 1 = g (const)
Definition SpinorMatrix.hpp:154
bool includes_g() const
True if g parts (fg, gf, gg) are included.
Definition SpinorMatrix.hpp:173
SpinorMatrix< T > & operator+=(T aI)
Addition of identity: Matrix<T> += T : T assumed to be *Identity!
Definition SpinorMatrix.hpp:273
std::size_t size() const
Number of sub-grid points (dimension of each block)
Definition SpinorMatrix.hpp:169
SpinorMatrix< double > imag() const
Returns imag part of complex matrix (changes type; returns a real matrix)
Definition SpinorMatrix.hpp:407
void zero()
Sets all matrix elements to zero.
Definition SpinorMatrix.hpp:181
SpinorMatrix< T > dri() const
Multiplies by dri: Q_ij -> Q_ij*dr_i. Returns new matrix (orig unchanged)
Definition SpinorMatrix.hpp:504
SpinorMatrix< T > & mult_elements_by(const SpinorMatrix< T > &rhs)
Multiply elements (in place): Gij -> Gij*Bij.
Definition SpinorMatrix.hpp:337
DiracSpinor operator*(const DiracSpinor &Fn) const
Action of SpinorMatrix operator on DiracSpinor.
Definition SpinorMatrix.hpp:562
SpinorMatrix< T > & drop_g()
Kills g parts of spinor matrix, in place!
Definition SpinorMatrix.hpp:190
SpinorMatrix< T > & operator+=(const SpinorMatrix< T > &rhs)
Matrix addition +,-.
Definition SpinorMatrix.hpp:222
SpinorMatrix< T > & invert_in_place()
Inversion (in place)
Definition SpinorMatrix.hpp:428
friend SpinorMatrix< T > mult_elements(const RadialMatrix< T > &rhs, SpinorMatrix< T > lhs)
Multiply elements (new matrix): Gij = Aij*Bij.
Definition SpinorMatrix.hpp:380
std::size_t index_to_fullgrid(std::size_t i) const
Converts an index on the sub-grid to the full grid.
Definition SpinorMatrix.hpp:517
friend SpinorMatrix< T > operator-(SpinorMatrix< T > M, T aI)
Addition of identity: Matrix<T> - T : T assumed to be *Identity!
Definition SpinorMatrix.hpp:290
double dr(std::size_t sub_index) const
returns dr at position along sub grid
Definition SpinorMatrix.hpp:510
friend SpinorMatrix< T > operator*(const SpinorMatrix< T > &a, const SpinorMatrix< T > &b)
Matrix multiplication.
Definition SpinorMatrix.hpp:309
SpinorMatrix< double > real() const
Returns real part of complex matrix (changes type; returns a real matrix)
Definition SpinorMatrix.hpp:398
friend SpinorMatrix< T > operator+(SpinorMatrix< T > lhs, const SpinorMatrix< T > &rhs)
Matrix addition +,-.
Definition SpinorMatrix.hpp:257
std::size_t i0() const
Full-grid index of the first sub-grid point.
Definition SpinorMatrix.hpp:175
SpinorMatrix< T > inverse() const
Returns inverse of matrix; original matrix unchanged.
Definition SpinorMatrix.hpp:446
SpinorMatrix< T > & mult_elements_by(const RadialMatrix< T > &rhs)
Multiply elements (in place): Gij -> Gij*Bij.
Definition SpinorMatrix.hpp:362
SpinorMatrix< T > conj() const
Returns conjugate of matrix.
Definition SpinorMatrix.hpp:389
friend SpinorMatrix< T > operator-(SpinorMatrix< T > lhs, const SpinorMatrix< T > &rhs)
Matrix addition +,-.
Definition SpinorMatrix.hpp:262
std::vector< double > interpolate(const std::vector< double > &x_in, const std::vector< double > &y_in, const std::vector< double > &x_out, Method method=Method::cspline)
Convenience wrapper: interpolates y_in(x_in) and evaluates at x_out.
Definition Interpolator.hpp:154
Many-body perturbation theory.
Definition MatrixElements.hpp:12
double max_element(const RadialMatrix< T > &a)
returns maximum element (by abs)
Definition RadialMatrix.hpp:307
bool equal(const RadialMatrix< T > &lhs, const RadialMatrix< T > &rhs)
Checks if two matrices are equal (to within parts in 10^12)
Definition RadialMatrix.hpp:301
double max_delta(const RadialMatrix< T > &a, const RadialMatrix< T > &b)
returns maximum difference (abs) between two matrices
Definition RadialMatrix.hpp:321
double max_epsilon(const RadialMatrix< T > &a, const RadialMatrix< T > &b)
returns maximum relative difference, |(aij-bij)/(aij+bij)|, of two matrices
Definition RadialMatrix.hpp:335