Loading...
Searching...
No Matches
methfessel_paxton.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * MethfesselPaxtonSmearing — Methfessel-Paxton occupation smearing of order N >= 1.
5 *
6 * Methfessel & Paxton, Phys. Rev. B 40, 3616 (1989). The smearing delta function is
7 * expanded in even Hermite polynomials,
8 *
9 * δ_N(x) = Σ_{n=0..N} A_n H_{2n}(x) exp(-x²), A_n = (-1)^n / (n! 4^n √π),
10 *
11 * which integrates to the occupation (x = (ε - E_F)/σ, occupied for x < 0)
12 *
13 * f_N(x) = ½ erfc(x) + Σ_{n=1..N} A_n H_{2n-1}(x) exp(-x²),
14 *
15 * and the generalized-entropy term of the free energy involves only the highest order,
16 * S_N(x) = ½ A_N H_{2N}(x) exp(-x²). Order N makes the free energy converge to the
17 * σ → 0 total energy as O(σ^(2N+2)). Q3's sign convention for entropy_weight is −S_N,
18 * matching GaussianSmearing (the N = 0 case).
19 *
20 * Order 1 keeps its original closed form so existing order-1 results stay bit-identical;
21 * higher orders use the Hermite three-term recurrence H_{k+1} = 2x H_k − 2k H_{k−1}.
22 */
23
24#include <cmath>
25#include <stdexcept>
26#include <string>
27
29
30namespace q3 {
31
32/// Methfessel-Paxton smearing of order N >= 1 (default 1).
34 public:
37 if (order_ < 1) {
38 throw std::invalid_argument("Methfessel-Paxton order must be >= 1, got " +
39 std::to_string(order_));
40 }
41 }
42
43 [[nodiscard]] Real occupation(Real x) const override {
44 const Real sqrtpi_inv = 1.0 / std::sqrt(constants::pi);
45 if (order_ == 1) {
46 // f_1(x) = (1/2)erfc(x) - x*exp(-x^2) / (2*sqrt(pi))
47 return 0.5 * std::erfc(x) - 0.5 * sqrtpi_inv * x * std::exp(-x * x);
48 }
49 const Real gaussian = std::exp(-x * x);
50 Real occupation_value = 0.5 * std::erfc(x);
51 Real hermite_previous = 1.0; // H_0
52 Real hermite_current = 2.0 * x; // H_1
53 int hermite_degree = 1;
54 Real coefficient = sqrtpi_inv; // A_0
55 for (int n = 1; n <= order_; ++n) {
56 coefficient *= -1.0 / (4.0 * n);
57 occupation_value += coefficient * hermite_current * gaussian; // A_n H_{2n-1}
58 advance_hermite(x, hermite_previous, hermite_current, hermite_degree);
59 advance_hermite(x, hermite_previous, hermite_current, hermite_degree);
60 }
61 return occupation_value;
62 }
63
64 /**
65 * −S_N(x) = −½ A_N H_{2N}(x) exp(-x²). For N = 1 this is
66 * (2x² − 1) exp(-x²) / (4√π), the original closed form.
67 */
68 [[nodiscard]] Real entropy_weight(Real x) const override {
69 const Real sqrtpi_inv = 1.0 / std::sqrt(constants::pi);
70 if (order_ == 1) {
71 return 0.25 * sqrtpi_inv * (2.0 * x * x - 1.0) * std::exp(-x * x);
72 }
73 Real hermite_previous = 1.0;
74 Real hermite_current = 2.0 * x;
75 int hermite_degree = 1;
76 while (hermite_degree < 2 * order_) {
77 advance_hermite(x, hermite_previous, hermite_current, hermite_degree);
78 }
79 Real coefficient = sqrtpi_inv;
80 for (int n = 1; n <= order_; ++n) {
81 coefficient *= -1.0 / (4.0 * n);
82 }
83 return -0.5 * coefficient * hermite_current * std::exp(-x * x);
84 }
85
86 [[nodiscard]] std::string name() const override {
87 return "methfessel-paxton";
88 }
89
90 /// Expansion order N.
91 [[nodiscard]] int order() const {
92 return order_;
93 }
94
95 private:
96 /// Advance (H_{k-1}, H_k) to (H_k, H_{k+1}) via H_{k+1} = 2x H_k − 2k H_{k−1}.
97 static void advance_hermite(Real x, Real& previous, Real& current, int& degree) {
98 const Real next = 2.0 * x * current - 2.0 * degree * previous;
99 previous = current;
100 current = next;
101 ++degree;
102 }
103
104 int order_;
105};
106
107} // namespace q3
Methfessel-Paxton smearing of order N >= 1 (default 1).
Real occupation(Real x) const override
Compute the occupation f(x) where x = (E - E_F) / σ.
MethfesselPaxtonSmearing(Real smearing_width=0.0, int order=1)
Real entropy_weight(Real x) const override
std::string name() const override
int order() const
Expansion order N.
Abstract interface for electronic smearing schemes.
Definition init.hpp:19
Real smearing_width() const
Smearing width σ (degauss) in Ry.
Definition init.hpp:33
constexpr Real pi
Definition constants.hpp:24
double Real