Loading...
Searching...
No Matches
fermi_dirac.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * FermiDiracSmearing — physical finite-temperature Fermi-Dirac occupation.
5 *
6 * f(x) = 1 / (exp(x) + 1), the actual electronic occupation at temperature
7 * T = smearing_width (in Ry). Unlike Gaussian/Methfessel-Paxton, the entropy
8 * term here is the true electronic entropy -[f ln f + (1-f) ln(1-f)], not a
9 * generalized/ad hoc correction.
10 */
11
12#include <cmath>
13
15
16namespace q3 {
17
18/// Fermi-Dirac (physical finite-T) smearing.
20 public:
22
23 [[nodiscard]] Real occupation(Real x) const override {
24 /**
25 * No saturation branch, and none is needed: IEEE-754 arithmetic
26 * already makes this expression total and exact at both limits.
27 * Large positive x overflows exp() to +inf (clause 7.4, default
28 * non-trapping), and 1/(inf + 1) is exactly 0.0; large negative x
29 * underflows exp() to +0 (clause 7.5), and 1/(0 + 1) is exactly
30 * 1.0. Both are the values a threshold would have returned, so a
31 * threshold would only decide *where* to stop computing an answer
32 * the format hands back for free.
33 *
34 * Do not add one back. Any bound below ~709 makes the function
35 * discontinuous at the bound in entropy_weight(), which reaches
36 * f through this method -- see EntropyWeightIsContinuousAcrossX36.
37 */
38 return 1.0 / (std::exp(x) + 1.0);
39 }
40
41 /**
42 * Returns f ln f + (1-f) ln(1-f), which is minus the electronic
43 * entropy S = -[f ln f + (1-f) ln(1-f)] >= 0. The negative sign is
44 * deliberate and load-bearing: Σ w_k σ entropy_weight(x) is then
45 * directly the -T*S total-energy term, matching the sign convention
46 * of the other schemes. Do not "correct" this to return a positive
47 * entropy -- the sign is consumed by the caller.
48 */
49 [[nodiscard]] Real entropy_weight(Real x) const override {
50 Real f = occupation(x);
51 Real one_minus_f = 1.0 - f;
52 /**
53 * Load-bearing, not defensive: f reaches exactly 0.0 and exactly
54 * 1.0 in the saturated tails, where f*log(f) would be 0*(-inf) =
55 * NaN. The floor is what keeps the tails at 0 instead. Removing it
56 * makes entropy_weight NaN at every saturated point.
57 */
58 constexpr Real probability_floor = 1e-16;
59 Real term_occupied = (f > probability_floor) ? f * std::log(f) : 0.0;
60 Real term_empty =
61 (one_minus_f > probability_floor) ? one_minus_f * std::log(one_minus_f) : 0.0;
62 return term_occupied + term_empty;
63 }
64
65 [[nodiscard]] std::string name() const override {
66 return "fermi-dirac";
67 }
68};
69
70} // namespace q3
Fermi-Dirac (physical finite-T) smearing.
std::string name() const override
Real occupation(Real x) const override
Compute the occupation f(x) where x = (E - E_F) / σ.
Real entropy_weight(Real x) const override
FermiDiracSmearing(Real smearing_width=0.0)
Abstract interface for electronic smearing schemes.
Definition init.hpp:19
Real smearing_width() const
Smearing width σ (degauss) in Ry.
Definition init.hpp:33
double Real