Skip to content

Commit 80b0907

Browse files
committed
print out the quasiparticle entropy for numerical evaluation of Cv
1 parent ef34793 commit 80b0907

File tree

5 files changed

+94
-37
lines changed

5 files changed

+94
-37
lines changed

anphon/scph.cpp

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ void Scph::postprocess(std::complex<double> ****delta_dymat, std::complex<double
295295
double *FE_QHA = nullptr;
296296
double *dFE_scph = nullptr;
297297
double *FE_total = nullptr;
298+
double *entropy = nullptr;
298299
double **msd_update = nullptr;
299300
double ***ucorr_update = nullptr;
300301
double ****dielec_update = nullptr;
@@ -318,6 +319,7 @@ void Scph::postprocess(std::complex<double> ****delta_dymat, std::complex<double
318319
allocate(FE_QHA, NT);
319320
allocate(dFE_scph, NT);
320321
allocate(FE_total, NT);
322+
allocate(entropy, NT);
321323

322324
if (writes->getPrintMSD()) {
323325
allocate(msd_update, NT, ns);
@@ -434,6 +436,14 @@ void Scph::postprocess(std::complex<double> ****delta_dymat, std::complex<double
434436

435437
FE_total[iT] = thermodynamics->compute_FE_total(iT, FE_QHA[iT], dFE_scph[iT]);
436438

439+
entropy[iT] = thermodynamics->vibrational_entropy(temperature,
440+
dos->kmesh_dos->nk_irred,
441+
ns,
442+
dos->kmesh_dos->kpoint_irred_all,
443+
dos->kmesh_dos->weight_k.data(),
444+
eval_update[iT]) /
445+
k_Boltzmann;
446+
437447
if (writes->getPrintMSD()) {
438448
double shift[3]{0.0, 0.0, 0.0};
439449

@@ -501,7 +511,13 @@ void Scph::postprocess(std::complex<double> ****delta_dymat, std::complex<double
501511
if (dos->compute_dos) {
502512
writes->writePhononDos(dos_update, is_qha, 0);
503513
}
504-
writes->writeThermodynamicFunc(heat_capacity, heat_capacity_correction, FE_QHA, dFE_scph, FE_total, is_qha);
514+
writes->writeThermodynamicFunc(heat_capacity,
515+
heat_capacity_correction,
516+
FE_QHA,
517+
dFE_scph,
518+
FE_total,
519+
entropy,
520+
is_qha);
505521
if (writes->getPrintMSD()) {
506522
writes->writeMSD(msd_update, is_qha, 0);
507523
}
@@ -777,6 +793,7 @@ void Scph::postprocess(std::complex<double> ****delta_dymat, std::complex<double
777793
if (FE_QHA) deallocate(FE_QHA);
778794
if (dFE_scph) deallocate(dFE_scph);
779795
if (FE_total) deallocate(FE_total);
796+
if (entropy) deallocate(entropy);
780797
if (dielec_update) deallocate(dielec_update);
781798

782799
if (eval_gam) deallocate(eval_gam);
@@ -2131,8 +2148,6 @@ void Scph::interpolate_to_dense_mesh(std::complex<double> ***dymat_q,
21312148
}
21322149

21332150

2134-
2135-
21362151
bool Scph::check_convergence(const Eigen::MatrixXd &omega_now, const Eigen::MatrixXd &omega_old, const double conv_tol,
21372152
const unsigned int verbosity, const int iloop, double &diff) const
21382153
{
@@ -2386,10 +2401,10 @@ void Scph::compute_anharmonic_frequency(std::complex<double> ***v4_array_all, do
23862401

23872402

23882403
void Scph::compute_anharmonic_frequency_diis_perkpoint(std::complex<double> ***v4_array_all, double **omega2_out,
2389-
std::complex<double> ***evec_anharm_scph, const double temp,
2390-
bool &flag_converged, std::complex<double> ***cmat_convert,
2391-
const bool offdiag, std::complex<double> **delta_v2_renorm,
2392-
const unsigned int verbosity)
2404+
std::complex<double> ***evec_anharm_scph, const double temp,
2405+
bool &flag_converged, std::complex<double> ***cmat_convert,
2406+
const bool offdiag, std::complex<double> **delta_v2_renorm,
2407+
const unsigned int verbosity)
23932408
{
23942409
// SCPH with per-k-point DIIS using GDIIS_PerKpoint class
23952410
// Each k-point has independent DIIS history and coefficients
@@ -2448,13 +2463,23 @@ void Scph::compute_anharmonic_frequency_diis_perkpoint(std::complex<double> ***v
24482463

24492464
const auto T_in = temp;
24502465

2451-
initialize_scph_iteration(T_in, flag_converged, omega2_out, verbosity,
2452-
omega_now, omega2_HA, evec_initial, evec_initial_adjoint, cmat_convert);
2466+
initialize_scph_iteration(T_in,
2467+
flag_converged,
2468+
omega2_out,
2469+
verbosity,
2470+
omega_now,
2471+
omega2_HA,
2472+
evec_initial,
2473+
evec_initial_adjoint,
2474+
cmat_convert);
24532475

24542476
setup_harmonic_dynamical_matrices(omega2_HA, evec_initial, delta_v2_renorm, Fmat0, dymat_q_HA);
24552477

2456-
dynamical->precompute_dymat_harm(kmesh_dense->nk, kmesh_dense->xk, kmesh_dense->kvec_na,
2457-
dymat_harm_short, dymat_harm_long);
2478+
dynamical->precompute_dymat_harm(kmesh_dense->nk,
2479+
kmesh_dense->xk,
2480+
kmesh_dense->kvec_na,
2481+
dymat_harm_short,
2482+
dymat_harm_long);
24582483

24592484
// Initialize per-k-point DIIS mixer
24602485
const int diis_history = std::min(3, static_cast<int>(maxiter / 3));
@@ -2473,13 +2498,28 @@ void Scph::compute_anharmonic_frequency_diis_perkpoint(std::complex<double> ***v
24732498
knum = kmap_interpolate_to_scph[knum_interpolate];
24742499

24752500
update_fmat_with_v4(Fmat0, v4_array_all, dmat_convert, offdiag, ik, Fmat);
2476-
diagonalize_and_symmetrize(Fmat, evec_initial, v4_array_all, ik, knum, knum_interpolate,
2477-
flag_converged, omega2_out, verbosity, icount, eval_tmp, dymat_q);
2501+
diagonalize_and_symmetrize(Fmat,
2502+
evec_initial,
2503+
v4_array_all,
2504+
ik,
2505+
knum,
2506+
knum_interpolate,
2507+
flag_converged,
2508+
omega2_out,
2509+
verbosity,
2510+
icount,
2511+
eval_tmp,
2512+
dymat_q);
24782513
}
24792514

24802515
// Step 2: Interpolate to get new eigenvalues (output from using dmat_convert)
2481-
interpolate_to_dense_mesh(dymat_q, dymat_q_HA, evec_initial, eval_interpolate,
2482-
evec_new, cmat_convert, omega_now);
2516+
interpolate_to_dense_mesh(dymat_q,
2517+
dymat_q_HA,
2518+
evec_initial,
2519+
eval_interpolate,
2520+
evec_new,
2521+
cmat_convert,
2522+
omega_now);
24832523

24842524
// Step 3: Check convergence before mixing
24852525
if (check_convergence(omega_now, omega_old, conv_tol, verbosity, iloop, diff)) {
@@ -2492,7 +2532,7 @@ void Scph::compute_anharmonic_frequency_diis_perkpoint(std::complex<double> ***v
24922532
}
24932533

24942534
if (use_diis) {
2495-
gdiis_mixer_perkpoint.push(dmat_convert, omega_now); // Correct: D_input → ω_output
2535+
gdiis_mixer_perkpoint.push(dmat_convert, omega_now); // Correct: D_input → ω_output
24962536
}
24972537

24982538
// Step 5: Compute what D should be based on new eigenvalues
@@ -2549,7 +2589,8 @@ void Scph::compute_anharmonic_frequency_diis_perkpoint(std::complex<double> ***v
25492589
std::cout << " (with per-k-point DIIS)";
25502590
auto stats = gdiis_mixer_perkpoint.get_success_stats();
25512591
int total_success = 0;
2552-
for (int s : stats) total_success += s;
2592+
for (int s: stats)
2593+
total_success += s;
25532594
if (verbosity > 1) {
25542595
std::cout << "\n Total DIIS successes: " << total_success;
25552596
}

anphon/thermodynamics.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ void Thermodynamics::setup()
4343

4444
auto Thermodynamics::Cv(const double omega, const double temp_in) const -> double
4545
{
46+
// Mode specific heat at constant volume.
47+
// Exactly heat capacity only within the QHA.
48+
// In other cases, Σ_q C_v is not equal to the total heat capacity.
4649
if (std::abs(temp_in) < eps) return 0.0;
4750

4851
const auto x = omega / (T_to_Ryd * temp_in);
@@ -51,13 +54,15 @@ auto Thermodynamics::Cv(const double omega, const double temp_in) const -> doubl
5154

5255
auto Thermodynamics::Cv_classical(const double omega, const double temp_in) -> double
5356
{
57+
// Just return k_B
5458
if (std::abs(temp_in) < eps) return 0.0;
5559

5660
return k_Boltzmann;
5761
}
5862

5963
auto Thermodynamics::fB(const double omega, const double temp_in) const -> double
6064
{
65+
// Bose-Einstein distribution function
6166
if (std::abs(temp_in) < eps || omega < eps8) return 0.0;
6267

6368
const auto x = omega / (T_to_Ryd * temp_in);
@@ -66,6 +71,7 @@ auto Thermodynamics::fB(const double omega, const double temp_in) const -> doubl
6671

6772
auto Thermodynamics::fC(const double omega, const double temp_in) const -> double
6873
{
74+
// Classical limit of Bose-Einstein distribution function
6975
if (std::abs(temp_in) < eps || omega < eps8) return 0.0;
7076

7177
const auto x = omega / (T_to_Ryd * temp_in);
@@ -76,6 +82,7 @@ auto Thermodynamics::Cv_tot(const double temp_in, const unsigned int nk_irred, c
7682
const std::vector<std::vector<KpointList>> &kp_irred, const double *weight_k_irred,
7783
const double *const *eval_in) const -> double
7884
{
85+
// Total constant-volume heat capacity. Only the quasiharmonic term is included here.
7986
int i;
8087
unsigned int ik, is;
8188
double omega;
@@ -120,6 +127,10 @@ auto Thermodynamics::Cv_anharm_correction(const double temp_in, const unsigned i
120127
const double *weight_k_irred, const double *const *eval_in,
121128
const double *const *del_eval_in) const -> double
122129
{
130+
// Anharmonic correction to constant-volume heat capacity
131+
// We only use the adjacent temperature point for the numerical derivative, so the numerical accuracy
132+
// may not be very high. For more reliable estimate, it is recommended to fit the entropy curve with
133+
// respect to temperature by polynomial function and take the derivative of the fitted curve.
123134
int i;
124135
unsigned int ik, is;
125136
double omega, domega_dt;
@@ -165,6 +176,8 @@ auto Thermodynamics::internal_energy(const double temp_in, const unsigned int nk
165176
const std::vector<std::vector<KpointList>> &kp_irred, const double *weight_k_irred,
166177
const double *const *eval_in) const -> double
167178
{
179+
// Vibrational internal energy within QHA.
180+
// U = F + TS = Σ_q 0.5 * hw_q * coth(hw_q/2k_BT)] = Σ_q [hw_q (n_q + 0.5)]
168181
int i;
169182
unsigned int ik, is;
170183
double omega;
@@ -207,6 +220,8 @@ auto Thermodynamics::vibrational_entropy(const double temp_in, const unsigned in
207220
const std::vector<std::vector<KpointList>> &kp_irred,
208221
const double *weight_k_irred, const double *const *eval_in) const -> double
209222
{
223+
// Vibrational entropy correct for QHA/SCP and other quasiparticle approaches
224+
// S = -(∂F/∂T) = k_B * Σ_q [(n_q + 1)ln(n_q + 1) - n_q ln n_q]
210225
int i;
211226
unsigned int ik, is;
212227
double omega, x;
@@ -250,6 +265,8 @@ auto Thermodynamics::free_energy_QHA(const double temp_in, const unsigned int nk
250265
const std::vector<std::vector<KpointList>> &kp_irred, const double *weight_k_irred,
251266
const double *const *eval_in) const -> double
252267
{
268+
// Vibrational free energy within QHA and QHA-like term within SCP.
269+
// F = Σ_q [0.5 hw_q + k_B T ln(1 - exp(-hw_q/k_BT))] (quantum)
253270
int i;
254271
unsigned int ik, is;
255272
double omega, x;
@@ -682,6 +699,8 @@ auto Thermodynamics::FE_scph_correction(unsigned int iT, double **eval, std::com
682699
double **eval_harm_renormalized,
683700
std::complex<double> ***evec_harm_renormalized) const -> double
684701
{
702+
// The correction term to the free energy within SCPH theory.
703+
// This term is necessary to result in S =
685704
using namespace Eigen;
686705
const auto nk = dos->kmesh_dos->nk;
687706
const auto ns = dynamical->neval;

anphon/write_phonons.cpp

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3131,7 +3131,7 @@ void Writes::writePhononDos(double **dos_in, const bool is_qha, const int bubble
31313131
}
31323132

31333133
void Writes::writeThermodynamicFunc(double *heat_capacity, double *heat_capacity_correction, double *FE_QHA,
3134-
double *dFE_scph, double *FE_total, const bool is_qha) const
3134+
double *dFE_scph, double *FE_total, double *entropy, const bool is_qha) const
31353135
{
31363136
const auto Tmin = system->Tmin;
31373137
const auto Tmax = system->Tmax;
@@ -3161,6 +3161,12 @@ void Writes::writeThermodynamicFunc(double *heat_capacity, double *heat_capacity
31613161
}
31623162
if (thermodynamics->calc_FE_bubble) {
31633163
ofs_thermo << "# The bubble free-energy calculated on top of the SCPH wavefunction is also shown.\n";
3164+
ofs_thermo << "# However, the bubble contributions to the heat capacity and entropy are not included.\n";
3165+
ofs_thermo << "# If these are needed, please fit the free energy data including the bubble term \n "
3166+
"# by polynomial function and then estimate S and Cv by numerical derivatives.\n";
3167+
}
3168+
if (!is_qha) {
3169+
ofs_thermo << "# The Cv data accounts for the QHA-like term only.\n";
31643170
}
31653171

31663172
ofs_thermo << "# Temperature [K], Cv [in kB unit]";
@@ -3175,21 +3181,11 @@ void Writes::writeThermodynamicFunc(double *heat_capacity, double *heat_capacity
31753181
if (thermodynamics->calc_FE_bubble) {
31763182
ofs_thermo << ", F_{vib} (Bubble correction) [Ry]";
31773183
}
3178-
// write renormalized zero-th order IFC
3184+
// write renormalized zero-th order IFC
31793185
if (relaxation->relax_str != 0) {
31803186
ofs_thermo << ", Phi0 [Ry]";
31813187
}
3182-
ofs_thermo << ", F_{total} [Ry]\n";
3183-
3184-
// if (thermodynamics->calc_FE_bubble) {
3185-
// ofs_thermo << "# The bubble free-energy calculated on top of the SCPH wavefunction is also shown." << '\n';
3186-
// ofs_thermo <<
3187-
// "# Temperature [K], Cv [in kB unit], F_{vib} (QHA term) [Ry], F_{vib} (SCPH correction) [Ry], F_{vib} (Bubble correction) [Ry]"
3188-
// << '\n';
3189-
// } else {
3190-
// ofs_thermo << "# Temperature [K], Cv [in kB unit], F_{vib} (QHA term) [Ry], F_{vib} (SCPH correction) [Ry]"
3191-
// << '\n';
3192-
// }
3188+
ofs_thermo << ", F_{total} [Ry], S_{vib} [in kB unit]\n";
31933189

31943190
if (thermodynamics->classical) {
31953191
ofs_thermo << "# CLASSICAL = 1: Use classical limit.\n";
@@ -3216,15 +3212,16 @@ void Writes::writeThermodynamicFunc(double *heat_capacity, double *heat_capacity
32163212
if (relaxation->relax_str != 0) {
32173213
ofs_thermo << std::setw(18) << relaxation->V0[iT];
32183214
}
3219-
ofs_thermo << std::setw(18) << FE_total[iT] << '\n';
3215+
ofs_thermo << std::setw(18) << FE_total[iT];
3216+
ofs_thermo << std::setw(18) << entropy[iT] << '\n';
32203217
}
32213218

32223219
ofs_thermo.close();
32233220
std::cout << " " << std::setw(input->job_title.length() + 12) << std::left << file_thermo;
32243221
if (is_qha) {
3225-
std::cout << " : QHA heat capcaity and free energy\n";
3222+
std::cout << " : QHA heat capcaity, free energy, entropy\n";
32263223
} else {
3227-
std::cout << " : SCPH heat capcaity and free energy\n";
3224+
std::cout << " : SCPH heat capcaity, free energy, entropy\n";
32283225
}
32293226
}
32303227

anphon/write_phonons.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class Writes: protected Pointers
5858
void writePhononDos(double **dos_in, const bool is_qha = false, const int bubble = 0) const;
5959

6060
void writeThermodynamicFunc(double *heat_capacity, double *heat_capacity_correction, double *FE_QHA,
61-
double *dFE_scph, double *FE_total, const bool is_qha = false) const;
61+
double *dFE_scph, double *FE_total, double *entropy, const bool is_qha = false) const;
6262

6363
void writeMSD(double **msd_in, const bool is_qha = false, const int bubble = 0) const;
6464

tools/plotband.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ def get_kpath_and_kval(file_in):
4343
kval_float = [float(val) for val in kval[1:]]
4444
kpath_list = []
4545
for i in range(len(kpath[1:])):
46-
if kpath[i + 1] == "G":
47-
kpath_list.append("$\Gamma$")
46+
if kpath[i + 1] == "G" or kpath[i + 1] == "GAMMA":
47+
kpath_list.append(r"$\Gamma$")
4848
else:
49-
kpath_list.append("$\mathrm{%s}$" % kpath[i + 1])
49+
kpath_list.append(r"$\mathrm{%s}$" % kpath[i + 1])
5050

5151
return kpath_list, kval_float
5252
else:

0 commit comments

Comments
 (0)