Residual preconditioner solving [G² + k_TF²(r)] x = G² F by conjugate gradients. More...
#include <local_thomas_fermi.hpp>
Inheritance diagram for q3::LocalThomasFermiPreconditioner:Public Member Functions | |
| LocalThomasFermiPreconditioner (std::vector< Real > g_squared, Real q0_squared, int first_nonzero_g_index, std::function< void(const std::vector< Real > &, std::vector< Complex > &)> to_reciprocal_space, std::function< void(const std::vector< Complex > &, std::vector< Real > &)> to_real_space, std::function< void(std::vector< Real > &)> reduce_within_pool={}) | |
| void | apply (std::vector< Complex > &residual_g) const override |
| Real | mean_screening () const |
| Cell-averaged k_TF², in bohr⁻². Exposed for tests. | |
| std::string | name () const override |
| void | update (const std::vector< Real > &density_real) override |
Public Member Functions inherited from q3::Preconditioner | |
| virtual | ~Preconditioner ()=default |
Residual preconditioner solving [G² + k_TF²(r)] x = G² F by conjugate gradients.
Definition at line 47 of file local_thomas_fermi.hpp.
|
inline |
| g_squared | |G|² per G-vector, in bohr⁻². |
| q0_squared | floor for k_TF², in bohr⁻² (see below). |
| first_nonzero_g_index | index of the first G ≠ 0 component. |
| to_reciprocal_space | real-space → G-space density transform. |
| to_real_space | G-space → real-space density transform. |
| reduce_within_pool | sums values over the ranks sharing this pool's real-space grid; empty when one rank holds all of it. |
The floor is the existing mixing.q0 key rather than a second knob. It matters because ρ → 0 gives k_TF → 0, which is correct physics – there is nothing to screen in vacuum – but leaves the operator singular in exactly the cells (slabs, molecules in a box) this preconditioner exists to help. Flooring at q0² recovers Kerker's behaviour in the vacuum region and the local screening everywhere else.
Until the first update() the local screening is unknown, so the operator falls back to the uniform q0² – i.e. to Kerker. That keeps apply() well defined if it is ever called before a density exists.
Definition at line 66 of file local_thomas_fermi.hpp.
|
inlineoverridevirtual |
Apply the preconditioner to one G-space residual channel, in place.
Callers pass the TOTAL charge channel only: the magnetization channel has no Coulomb kernel to screen, and damping its long-wavelength components would freeze slow moment relaxation. The G = 0 component must be left untouched — it is fixed by charge conservation, and the mixing metric excludes it for the same reason.
| residual_g | residual in reciprocal space; overwritten with the preconditioned residual. |
Asked of a flag rather than of screening_real_: a rank that holds no real-space planes has an empty screening after update() like any other, and taking the shortcut there would skip the collective transforms every other rank runs below.
No density supplied yet: fall back to the uniform-screening answer G²F/(G²+q0²), which is what the operator reduces to anyway – Kerker exactly.
Initial guess: the exact solution of the uniform-screening problem, which is free and is already correct wherever k_TF² is close to its cell average – the error it leaves is only the variation of the screening. This is deliberately not warm-started from the previous SCF step: the residual shrinks by orders of magnitude as the SCF converges, so a stale iterate has the wrong scale and is a worse starting point than this one.
A truncated solve is not wrong, but it is no longer local-TF: it sits somewhere between Kerker and the identity, and the SCF would only show it as unexplained slow convergence. Say so.
Copy back the G != 0 components only. G = 0 is left exactly as the caller passed it: it is fixed by charge conservation, and the solve never represented it.
Implements q3::Preconditioner.
Definition at line 125 of file local_thomas_fermi.hpp.
References q3::log::info().
|
inline |
Cell-averaged k_TF², in bohr⁻². Exposed for tests.
Definition at line 236 of file local_thomas_fermi.hpp.
|
inlineoverridevirtual |
Implements q3::Preconditioner.
Definition at line 87 of file local_thomas_fermi.hpp.
|
inlineoverridevirtual |
Recompute k_TF²(r) and its cell average from the spin-summed input density.
The density is this rank's part of the real-space grid. Where ranks share a pool that is only some of the planes, and the average taken over them alone differed from rank to rank. The average sets the diagonal of the conjugate-gradient solve, which then took different steps on different ranks around the same collective transforms and hung. Sum and point count are therefore completed within the pool before they are divided.
FFT round-off leaves small negative values in low-density regions. A negative density would give a negative k_TF² and an indefinite operator, so clamp before the cube root rather than after.
Reimplemented from q3::Preconditioner.
Definition at line 101 of file local_thomas_fermi.hpp.