or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-tools.mdcore-phonopy.mdgruneisen.mdindex.mdloading.mdqha.mdstructure.md

qha.mddocs/

0

# Quasi-Harmonic Approximation (QHA)

1

2

Perform quasi-harmonic approximation calculations to study temperature and pressure effects on material properties through phonon calculations at multiple volumes. QHA enables prediction of thermal expansion, bulk modulus, heat capacity, and other thermodynamic properties by combining phonon calculations with equation of state fitting.

3

4

## Capabilities

5

6

### QHA Initialization

7

8

Initialize QHA calculations with volume-dependent phonon data and optional electronic contributions.

9

10

```python { .api }

11

class PhonopyQHA:

12

def __init__(

13

self,

14

volumes: ArrayLike = None,

15

electronic_energies: ArrayLike = None,

16

temperatures: ArrayLike = None,

17

free_energy: ArrayLike = None,

18

cv: ArrayLike = None,

19

entropy: ArrayLike = None,

20

t_max: float = None,

21

energy_plot_factor: float = None,

22

pressure: float = None,

23

eos: str = "vinet",

24

verbose: bool = False

25

):

26

"""

27

Initialize quasi-harmonic approximation calculation.

28

29

Parameters:

30

- volumes: Crystal volumes for each calculation (ų)

31

- electronic_energies: Electronic energies at each volume (eV)

32

- temperatures: Temperature range for calculations (K)

33

- free_energy: Phonon free energies [nvols, ntemps] (kJ/mol)

34

- cv: Phonon heat capacities [nvols, ntemps] (J/K/mol)

35

- entropy: Phonon entropies [nvols, ntemps] (J/K/mol)

36

- t_max: Maximum temperature for analysis (K)

37

- energy_plot_factor: Scaling factor for energy plots

38

- pressure: Applied pressure (GPa)

39

- eos: Equation of state ('vinet', 'murnaghan', 'birch_murnaghan', 'p3')

40

- verbose: Print detailed calculation information

41

"""

42

```

43

44

**Example:**

45

46

```python

47

from phonopy import Phonopy, PhonopyQHA

48

import numpy as np

49

50

# Volume range (typically ±10% around equilibrium)

51

volumes = np.array([95, 100, 105, 110, 115]) # ų

52

53

# Electronic energies from DFT at each volume

54

electronic_energies = np.array([-10.5, -10.8, -10.6, -10.2, -9.7]) # eV

55

56

# Temperature range

57

temperatures = np.arange(0, 1000, 10) # K

58

59

# Collect phonon data from Phonopy calculations at each volume

60

free_energies = []

61

heat_capacities = []

62

entropies = []

63

64

for i, vol in enumerate(volumes):

65

# Create Phonopy instance for this volume

66

ph = Phonopy(unitcell_scaled[i], supercell_matrix)

67

ph.force_constants = force_constants[i]

68

69

# Calculate thermal properties

70

ph.run_mesh([20, 20, 20])

71

ph.run_thermal_properties(temperatures)

72

thermal = ph.get_thermal_properties_dict()

73

74

free_energies.append(thermal['free_energy'])

75

heat_capacities.append(thermal['heat_capacity'])

76

entropies.append(thermal['entropy'])

77

78

# Create QHA instance

79

qha = PhonopyQHA(

80

volumes=volumes,

81

electronic_energies=electronic_energies,

82

temperatures=temperatures,

83

free_energy=np.array(free_energies).T, # [ntemps, nvols]

84

cv=np.array(heat_capacities).T,

85

entropy=np.array(entropies).T,

86

eos='vinet'

87

)

88

```

89

90

### Bulk Modulus Analysis

91

92

Calculate bulk modulus as a function of volume and temperature.

93

94

```python { .api }

95

def get_bulk_modulus(self) -> tuple:

96

"""

97

Get bulk modulus data from equation of state fitting.

98

99

Returns:

100

Tuple containing:

101

- bulk_modulus: Bulk modulus values (GPa)

102

- volumes_for_fit: Volumes used in EOS fitting (ų)

103

- parameters: EOS fitting parameters

104

"""

105

106

def get_bulk_modulus_temperature(self) -> tuple:

107

"""

108

Get bulk modulus as function of temperature.

109

110

Returns:

111

Tuple of (temperatures, bulk_modulus_values)

112

"""

113

114

def write_bulk_modulus_temperature(

115

self,

116

filename: str = "bulk_modulus-temperature.dat"

117

):

118

"""

119

Write bulk modulus vs temperature to file.

120

121

Parameters:

122

- filename: Output file name

123

"""

124

125

def plot_bulk_modulus(self, thin_number: int = 10):

126

"""

127

Plot bulk modulus vs volume with EOS fitting.

128

129

Parameters:

130

- thin_number: Thinning factor for temperature curves

131

"""

132

```

133

134

**Example:**

135

136

```python

137

# Get bulk modulus analysis

138

bulk_modulus, volumes_fit, eos_params = qha.get_bulk_modulus()

139

print(f"Bulk modulus at 0K: {bulk_modulus[0]:.1f} GPa")

140

141

# Temperature-dependent bulk modulus

142

temps, bulk_mod_T = qha.get_bulk_modulus_temperature()

143

144

# Plot bulk modulus

145

qha.plot_bulk_modulus(thin_number=5)

146

plt.title("Bulk Modulus vs Volume")

147

plt.show()

148

149

# Save data

150

qha.write_bulk_modulus_temperature("bulk_modulus_T.dat")

151

```

152

153

### Helmholtz Free Energy

154

155

Calculate and analyze Helmholtz free energy as function of volume and temperature.

156

157

```python { .api }

158

def get_helmholtz_volume(self) -> tuple:

159

"""

160

Get Helmholtz free energy vs volume data.

161

162

Returns:

163

Tuple containing:

164

- temperatures: Temperature array (K)

165

- volumes: Volume array (ų)

166

- helmholtz_energies: Free energies [ntemps, nvols] (eV)

167

"""

168

169

def write_helmholtz_volume(

170

self,

171

filename: str = "helmholtz-volume.dat"

172

):

173

"""

174

Write Helmholtz free energy vs volume to file.

175

176

Parameters:

177

- filename: Output file name

178

"""

179

```

180

181

### Volume-Temperature Relations

182

183

Calculate equilibrium volume as function of temperature and thermal expansion.

184

185

```python { .api }

186

def get_volume_temperature(self) -> tuple:

187

"""

188

Get equilibrium volume vs temperature.

189

190

Returns:

191

Tuple of (temperatures, equilibrium_volumes)

192

"""

193

194

def get_thermal_expansion(self) -> tuple:

195

"""

196

Get thermal expansion coefficient vs temperature.

197

198

Returns:

199

Tuple of (temperatures, thermal_expansion_coefficients)

200

"""

201

202

def write_volume_temperature(

203

self,

204

filename: str = "volume-temperature.dat"

205

):

206

"""

207

Write volume vs temperature to file.

208

209

Parameters:

210

- filename: Output file name

211

"""

212

213

def write_thermal_expansion(

214

self,

215

filename: str = "thermal_expansion.dat"

216

):

217

"""

218

Write thermal expansion vs temperature to file.

219

220

Parameters:

221

- filename: Output file name

222

"""

223

224

def plot_volume_temperature(self, exp_data: ArrayLike = None):

225

"""

226

Plot volume vs temperature with optional experimental comparison.

227

228

Parameters:

229

- exp_data: Experimental data as [temperatures, volumes] for comparison

230

"""

231

```

232

233

**Example:**

234

235

```python

236

# Volume-temperature relationship

237

temps, volumes = qha.get_volume_temperature()

238

print(f"Volume at 300K: {volumes[30]:.2f} ų") # T[30] = 300K

239

240

# Thermal expansion coefficient

241

temps, alpha = qha.get_thermal_expansion()

242

print(f"Thermal expansion at 300K: {alpha[30]:.2e} K⁻¹")

243

244

# Plot with experimental data (if available)

245

exp_temps = [300, 400, 500, 600]

246

exp_volumes = [100.2, 100.8, 101.5, 102.3]

247

qha.plot_volume_temperature(exp_data=[exp_temps, exp_volumes])

248

plt.show()

249

250

# Save results

251

qha.write_volume_temperature("volume_T.dat")

252

qha.write_thermal_expansion("alpha_T.dat")

253

```

254

255

### Gibbs Free Energy and Heat Capacity

256

257

Calculate temperature-dependent thermodynamic properties at equilibrium volume.

258

259

```python { .api }

260

def get_gibbs_temperature(self) -> tuple:

261

"""

262

Get Gibbs free energy vs temperature at equilibrium volume.

263

264

Returns:

265

Tuple of (temperatures, gibbs_free_energies)

266

"""

267

268

def get_heat_capacity_P_numerical(self) -> tuple:

269

"""

270

Get heat capacity at constant pressure (numerical derivative).

271

272

Returns:

273

Tuple of (temperatures, heat_capacities_P)

274

"""

275

276

def get_heat_capacity_P_polyfit(self) -> tuple:

277

"""

278

Get heat capacity at constant pressure (polynomial fit derivative).

279

280

Returns:

281

Tuple of (temperatures, heat_capacities_P)

282

"""

283

284

def write_gibbs_temperature(

285

self,

286

filename: str = "gibbs-temperature.dat"

287

):

288

"""

289

Write Gibbs free energy vs temperature to file.

290

291

Parameters:

292

- filename: Output file name

293

"""

294

295

def write_heat_capacity_P_numerical(

296

self,

297

filename: str = "Cp-temperature.dat"

298

):

299

"""

300

Write heat capacity at constant pressure to file.

301

302

Parameters:

303

- filename: Output file name

304

"""

305

```

306

307

### Grüneisen Temperature

308

309

Calculate average Grüneisen parameter as function of temperature.

310

311

```python { .api }

312

def get_gruneisen_temperature(self) -> tuple:

313

"""

314

Get average Grüneisen parameter vs temperature.

315

316

Returns:

317

Tuple of (temperatures, gruneisen_parameters)

318

"""

319

320

def write_gruneisen_temperature(

321

self,

322

filename: str = "gruneisen-temperature.dat"

323

):

324

"""

325

Write Grüneisen parameter vs temperature to file.

326

327

Parameters:

328

- filename: Output file name

329

"""

330

```

331

332

### Comprehensive Plotting

333

334

Generate comprehensive QHA analysis plots.

335

336

```python { .api }

337

def plot_qha(

338

self,

339

thin_number: int = 10,

340

volume_temp_experimental: ArrayLike = None,

341

thermal_expansion_experimental: ArrayLike = None,

342

cp_experimental: ArrayLike = None,

343

cv_experimental: ArrayLike = None

344

):

345

"""

346

Generate comprehensive QHA plots including volume-temperature,

347

thermal expansion, heat capacity, and Grüneisen parameter.

348

349

Parameters:

350

- thin_number: Thinning factor for curves

351

- volume_temp_experimental: Experimental volume-temperature data

352

- thermal_expansion_experimental: Experimental thermal expansion data

353

- cp_experimental: Experimental Cp data

354

- cv_experimental: Experimental Cv data

355

"""

356

357

def plot_pdf_qha(

358

self,

359

filename: str = "qha.pdf",

360

thin_number: int = 10

361

):

362

"""

363

Generate PDF file with all QHA plots.

364

365

Parameters:

366

- filename: Output PDF file name

367

- thin_number: Thinning factor for curves

368

"""

369

```

370

371

### Properties Access

372

373

Access calculated QHA properties directly.

374

375

```python { .api }

376

@property

377

def bulk_modulus(self) -> ndarray:

378

"""Bulk modulus values (GPa)."""

379

380

@property

381

def thermal_expansion(self) -> ndarray:

382

"""Thermal expansion coefficients (K⁻¹)."""

383

384

@property

385

def helmholtz_volume(self) -> ndarray:

386

"""Helmholtz free energies at each volume and temperature (eV)."""

387

388

@property

389

def volume_temperature(self) -> ndarray:

390

"""Equilibrium volumes at each temperature (ų)."""

391

392

@property

393

def gibbs_temperature(self) -> ndarray:

394

"""Gibbs free energies at equilibrium volume vs temperature (eV)."""

395

396

@property

397

def bulk_modulus_temperature(self) -> ndarray:

398

"""Bulk modulus vs temperature (GPa)."""

399

400

@property

401

def heat_capacity_P_numerical(self) -> ndarray:

402

"""Heat capacity at constant pressure, numerical (J/K/mol)."""

403

404

@property

405

def heat_capacity_P_polyfit(self) -> ndarray:

406

"""Heat capacity at constant pressure, polynomial fit (J/K/mol)."""

407

408

@property

409

def gruneisen_temperature(self) -> ndarray:

410

"""Average Grüneisen parameter vs temperature."""

411

```

412

413

## Complete QHA Workflow

414

415

```python

416

from phonopy import Phonopy, PhonopyQHA

417

from phonopy.structure.atoms import PhonopyAtoms

418

import numpy as np

419

import matplotlib.pyplot as plt

420

421

# 1. Define volume range (typically ±10% around equilibrium)

422

V0 = 100.0 # Equilibrium volume (ų)

423

volume_strains = np.array([-0.08, -0.04, 0.0, 0.04, 0.08])

424

volumes = V0 * (1 + volume_strains)

425

426

# Electronic energies from DFT calculations at each volume

427

electronic_energies = np.array([-10.85, -10.92, -10.89, -10.81, -10.68])

428

429

# 2. Temperature range for thermal properties

430

temperatures = np.arange(0, 1000, 10) # 0 to 1000 K in 10 K steps

431

432

# 3. Calculate phonon thermal properties at each volume

433

phonon_free_energies = []

434

phonon_heat_capacities = []

435

phonon_entropies = []

436

437

for i, vol in enumerate(volumes):

438

# Create scaled unit cell for this volume

439

scale_factor = (vol / V0)**(1/3)

440

unitcell_scaled = unitcell.copy()

441

unitcell_scaled.cell *= scale_factor

442

443

# Phonopy calculation

444

ph = Phonopy(unitcell_scaled, [[2,0,0],[0,2,0],[0,0,2]])

445

ph.force_constants = force_constants_at_volume[i] # From DFT

446

447

# Calculate thermal properties

448

ph.run_mesh([20, 20, 20])

449

ph.run_thermal_properties(temperatures)

450

thermal = ph.get_thermal_properties_dict()

451

452

phonon_free_energies.append(thermal['free_energy'])

453

phonon_heat_capacities.append(thermal['heat_capacity'])

454

phonon_entropies.append(thermal['entropy'])

455

456

# Convert to proper array shape [ntemps, nvols]

457

free_energy = np.array(phonon_free_energies).T

458

cv = np.array(phonon_heat_capacities).T

459

entropy = np.array(phonon_entropies).T

460

461

# 4. Initialize QHA calculation

462

qha = PhonopyQHA(

463

volumes=volumes,

464

electronic_energies=electronic_energies,

465

temperatures=temperatures,

466

free_energy=free_energy,

467

cv=cv,

468

entropy=entropy,

469

eos='vinet', # Vinet equation of state

470

verbose=True

471

)

472

473

# 5. Extract thermodynamic properties

474

475

# Bulk modulus analysis

476

bulk_modulus_0K = qha.bulk_modulus[0]

477

print(f"Bulk modulus at 0 K: {bulk_modulus_0K:.1f} GPa")

478

479

# Volume-temperature relationship

480

temps, eq_volumes = qha.get_volume_temperature()

481

V_300K = eq_volumes[30] # Volume at 300 K

482

print(f"Volume at 300 K: {V_300K:.2f} ų")

483

484

# Thermal expansion

485

temps, alpha = qha.get_thermal_expansion()

486

alpha_300K = alpha[30] # Thermal expansion at 300 K

487

print(f"Thermal expansion at 300 K: {alpha_300K:.2e} K⁻¹")

488

489

# Heat capacity at constant pressure

490

temps, Cp = qha.get_heat_capacity_P_numerical()

491

Cp_300K = Cp[30] # Heat capacity at 300 K

492

print(f"Heat capacity (Cp) at 300 K: {Cp_300K:.1f} J/K/mol")

493

494

# Average Grüneisen parameter

495

temps, gamma_avg = qha.get_gruneisen_temperature()

496

gamma_300K = gamma_avg[30]

497

print(f"Average Grüneisen parameter at 300 K: {gamma_300K:.2f}")

498

499

# 6. Generate comprehensive plots

500

qha.plot_qha(thin_number=5)

501

plt.suptitle("Quasi-Harmonic Approximation Results")

502

plt.tight_layout()

503

plt.show()

504

505

# Plot specific properties

506

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

507

508

# Volume vs temperature

509

axes[0,0].plot(temps, eq_volumes)

510

axes[0,0].set_xlabel("Temperature (K)")

511

axes[0,0].set_ylabel("Volume (ų)")

512

axes[0,0].set_title("Equilibrium Volume vs Temperature")

513

axes[0,0].grid(True)

514

515

# Thermal expansion

516

axes[0,1].plot(temps[1:], alpha[1:]) # Skip T=0

517

axes[0,1].set_xlabel("Temperature (K)")

518

axes[0,1].set_ylabel("Thermal Expansion (K⁻¹)")

519

axes[0,1].set_title("Thermal Expansion Coefficient")

520

axes[0,1].grid(True)

521

522

# Heat capacity

523

axes[1,0].plot(temps, Cp, label='Cp (constant P)')

524

axes[1,0].plot(temps, cv.mean(axis=1), label='Cv (constant V)')

525

axes[1,0].set_xlabel("Temperature (K)")

526

axes[1,0].set_ylabel("Heat Capacity (J/K/mol)")

527

axes[1,0].set_title("Heat Capacity")

528

axes[1,0].legend()

529

axes[1,0].grid(True)

530

531

# Bulk modulus vs temperature

532

temps_BM, BM = qha.get_bulk_modulus_temperature()

533

axes[1,1].plot(temps_BM, BM)

534

axes[1,1].set_xlabel("Temperature (K)")

535

axes[1,1].set_ylabel("Bulk Modulus (GPa)")

536

axes[1,1].set_title("Bulk Modulus vs Temperature")

537

axes[1,1].grid(True)

538

539

plt.tight_layout()

540

plt.show()

541

542

# 7. Save results to files

543

qha.write_volume_temperature("volume_temperature.dat")

544

qha.write_thermal_expansion("thermal_expansion.dat")

545

qha.write_heat_capacity_P_numerical("Cp_temperature.dat")

546

qha.write_bulk_modulus_temperature("bulk_modulus_temperature.dat")

547

qha.write_gruneisen_temperature("gruneisen_temperature.dat")

548

549

# Save comprehensive plot to PDF

550

qha.plot_pdf_qha("qha_analysis.pdf")

551

print("QHA analysis complete. Results saved to files.")

552

```

553

554

## Physical Interpretation

555

556

The quasi-harmonic approximation assumes:

557

1. **Harmonic phonons** at each volume

558

2. **Volume-dependent frequencies** captured through multiple calculations

559

3. **No phonon-phonon interactions** (pure harmonic limit)

560

561

**Key outputs and their significance:**

562

563

- **Thermal expansion α**: How much material expands per unit temperature increase

564

- **Bulk modulus B**: Material's resistance to uniform compression

565

- **Heat capacity Cp**: Heat required to raise temperature at constant pressure

566

- **Grüneisen parameter γ**: Measure of anharmonicity and mode coupling

567

568

**Limitations:**

569

- Fails at high temperatures where anharmonic effects dominate

570

- Cannot capture temperature-dependent phonon linewidths

571

- May be inaccurate near phase transitions

572

- Electronic contributions must be included separately

573

574

**Applications:**

575

- Predicting thermal expansion of materials

576

- Understanding pressure effects on lattice dynamics

577

- Calculating thermodynamic properties for materials design

578

- Comparing with experimental thermal expansion measurements