Skip to content

Commit 36fe56a

Browse files
committed
Add run_qha_2d.py example
1 parent 8782929 commit 36fe56a

File tree

9 files changed

+531
-37
lines changed

9 files changed

+531
-37
lines changed

abipy/dfpt/deformation_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _add(name, new_rprim, i, j, k, l, m, n) -> None:
5151
structures_new[name] = new_structure
5252
strain_inds.append([i, j, k, l, m, n])
5353

54-
i, j, k, l, m, n = 6 * [None]
54+
i, j, k, l, m, n = 6 * [0]
5555

5656
if 1 <= spgrp_number <= 2:
5757
disp=[[1,1,1,1,1,1], [0,1,1,1,1,1], [2,1,1,1,1,1], [1,0,1,1,1,1], [1,2,1,1,1,1], [1,1,0,1,1,1],
@@ -193,5 +193,5 @@ def _add(name, new_rprim, i, j, k, l, m, n) -> None:
193193
else:
194194
raise ValueError(f"Invalid {spgrp_number=}")
195195

196-
return structures_new, np.array(strain_inds, dtype=object), spgrp_number
196+
return structures_new, np.array(strain_inds, dtype=int), spgrp_number
197197

abipy/dfpt/qha_2D.py

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,96 @@
1212
import numpy as np
1313
import abipy.core.abinit_units as abu
1414

15+
from scipy.interpolate import RectBivariateSpline #, RegularGridInterpolator
1516
#from monty.collections import dict2namedtuple
1617
#from monty.functools import lazy_property
1718
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt
1819
from abipy.tools.typing import Figure
20+
from abipy.tools.serialization import HasPickleIO, mjson_load
1921
from abipy.electrons.gsr import GsrFile
2022
from abipy.dfpt.ddb import DdbFile
2123
from abipy.dfpt.phonons import PhdosFile # PhononBandsPlotter, PhononDos,
22-
from scipy.interpolate import RectBivariateSpline #, RegularGridInterpolator
24+
from abipy.dfpt.vzsisa import anaget_phdoses_with_gauss
25+
2326

2427

25-
class QHA_2D:
28+
class QHA_2D(HasPickleIO):
2629
"""
2730
Quasi-Harmonic Approximation (QHA) analysis in 2D.
2831
Provides methods for calculating and visualizing energy, free energy, and thermal expansion.
2932
"""
3033

31-
#@classmethod
32-
#def from_ddb_files(cls, ddb_files):
33-
# for row in ddb_files:
34-
# for gp in row:
35-
# if os.path.exists(gp):
34+
@classmethod
35+
def from_json_file(cls,
36+
filepath: PathLike,
37+
nqsmall_or_qppa: int,
38+
anaget_kwargs: dict | None = None,
39+
smearing_ev: float | None = None,
40+
verbose: int = 0) -> Vzsisa:
41+
"""
42+
Build an instance from a json file `filepath` typically produced by an Abipy flow.
43+
For the meaning of the other arguments see from_gsr_ddb_paths.
44+
"""
45+
data = mjson_load(filepath)
46+
47+
bo_strains_ac = [data["strains_a"], data["strains_c"]]
48+
phdos_strains_ac = [data["strains_a"], data["strains_c"]]
3649

37-
# return cls.from_files(ddb_files, phdos_paths_2D, gsr_file="DDB")
50+
return cls.from_gsr_ddb_paths(nqsmall_or_qppa,
51+
data["gsr_relax_paths"], data["ddb_relax_paths"],
52+
bo_strains_ac, phdos_strains_ac,
53+
anaget_kwargs=anaget_kwargs, smearing_ev=smearing_ev, verbose=verbose)
3854

3955
@classmethod
40-
def from_files(cls, gsr_paths_2D, phdos_paths_2D, gsr_file="GSR.nc") -> QHA_2D:
56+
def from_gsr_ddb_paths(cls,
57+
nqsmall_or_qppa: int,
58+
gsr_paths,
59+
ddb_paths,
60+
bo_strains_ac,
61+
phdos_strains_ac,
62+
anaget_kwargs: dict | None = None,
63+
smearing_ev: float | None = None,
64+
verbose: int = 0) -> QHA_2D:
65+
"""
66+
Creates an instance from a list of GSR files and a list of DDB files.
67+
This is a simplified interface that computes the PHDOS.nc files automatically
68+
from the DDB files by invoking anaddb
69+
70+
Args:
71+
nqsmall_or_qppa: Define the q-mesh for the computation of the PHDOS.
72+
if > 0, it is interpreted as nqsmall
73+
if < 0, it is interpreted as qppa.
74+
gsr_paths: list of paths to GSR files.
75+
ddb_paths: list of paths to DDB files.
76+
bo_strains_ac: List of strains for the a and the c lattice vector.
77+
phdos_strains_ac: List of strains for the a and the c lattice vector.
78+
anaget_kwargs: dict with extra arguments passed to anaget_phdoses_with_gauss.
79+
smearing_ev: Gaussian smearing in eV.
80+
verbose: Verbosity level.
81+
"""
82+
phdos_paths, phbands_paths = anaget_phdoses_with_gauss(nqsmall_or_qppa, smearing_ev, ddb_paths, anaget_kwargs, verbose)
83+
84+
new = cls.from_files(ddb_files, phdos_paths_2D, bo_strains_ac, phdos_strains_ac, gsr_file="GSR.nc")
85+
#new.pickle_dump(workdir, basename=None)
86+
return new
87+
88+
@classmethod
89+
def from_files(cls, gsr_paths_2D, phdos_paths_2D, bo_strains_ac, phdos_strains_ac, gsr_file="GSR.nc") -> QHA_2D:
4190
"""
4291
Creates an instance of QHA from 2D lists of GSR and PHDOS files.
4392
4493
Args:
4594
gsr_paths_2D: 2D list of paths to GSR files.
4695
phdos_paths_2D: 2D list of paths to PHDOS.nc files.
47-
48-
Returns:
49-
A new instance of QHA.
96+
bo_strains_ac: List of strains for the a and the c lattice vector.
97+
phdos_strains_ac: List of strains for the a and the c lattice vector.
5098
"""
5199
energies, structures, phdoses , structures_from_phdos = [], [], [],[]
52100

101+
#shape = (len(strains_a), len(strains_c))
102+
#gsr_paths_2d = np.reshape(gsr_paths_2D, shape)
103+
#phdos_paths_2d = np.reshape(phdos_paths_2D, shape)
104+
53105
if gsr_file == "GSR.nc":
54106
# Process GSR files
55107
for row in gsr_paths_2D:
@@ -98,22 +150,29 @@ def from_files(cls, gsr_paths_2D, phdos_paths_2D, gsr_file="GSR.nc") -> QHA_2D:
98150
phdoses.append(row_doses)
99151
structures_from_phdos.append(row_structures)
100152

101-
return cls(structures, phdoses, energies , structures_from_phdos)
153+
return cls(structures, phdoses, energies, structures_from_phdos, bo_strains_ac, phdos_strains_ac)
102154

103155
def __init__(self, structures, phdoses, energies, structures_from_phdos,
156+
bo_strains_ac, phdos_strains_ac,
104157
eos_name: str='vinet', pressure: float=0.0):
105158
"""
106159
Args:
107160
structures (list): List of structures at different volumes.
108161
phdoses: List of density of states (DOS) data for phonon calculations.
109162
energies (list): SCF energies for the structures in eV.
163+
bo_strains_ac: List of strains for the a and the c lattice vector.
164+
phdos_strains_ac: List of strains for the a and the c lattice vector.
110165
eos_name (str): Expression used to fit the energies (e.g., 'vinet').
111166
pressure (float): External pressure in GPa to include in p*V term.
112167
"""
113168
self.phdoses = phdoses
114169
self.structures = structures
115170
self.structures_from_phdos = structures_from_phdos
116171
self.energies = np.array(energies, dtype=np.float64)
172+
173+
self.bo_strains_ac = bo_strains_ac
174+
self.phdos_strains_ac = phdos_strains_ac
175+
117176
self.eos_name = eos_name
118177
self.pressure = pressure
119178
self.volumes = np.array([[s.volume if s else np.nan for s in row] for row in structures])
@@ -509,7 +568,7 @@ def get_vib_free_energies(self, tstart: float, tstop: float, num: int) -> np.nda
509568
tstop: Stop temperature.
510569
num: Number of temperature points.
511570
512-
Return: A 3D array of vibrational free energies.
571+
Return: A 3D array of vibrational free energies of shape (num_c, num_a, num_temp)
513572
"""
514573
f = np.zeros((len(self.lattice_c_from_phdos[0]), len(self.lattice_a_from_phdos[:, 0]), num))
515574
for i in range(len(self.lattice_a_from_phdos[:, 0])):

abipy/dfpt/qha_general_stress.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,21 @@ def from_json_file(cls,
4646
verbose: Verbosity level
4747
"""
4848
data = mjson_load(filepath)
49-
ddb_paths = data["ddb_relax_paths"]
50-
gsr_path = data["gsr_relax_paths"]
49+
ddb_relax_paths = data["ddb_relax_paths"]
50+
gsr_relax_paths = data["gsr_relax_paths"]
5151

5252
phdos_paths, phbands_paths = anaget_phdoses_with_gauss(nqsmall_or_qppa, smearing_ev,
53-
ddb_paths, anaget_kwargs, verbose)
53+
ddb_relax_paths, anaget_kwargs, verbose)
5454

5555
# Create a 6D array with shape (3, 3, 3, 3, 3, 3) initialized with None.
5656
gsr_paths_6d = np.full((3, 3, 3, 3, 3, 3), None, dtype=object)
5757
phdos_paths_6d = np.full((3, 3, 3, 3, 3, 3), None, dtype=object)
5858

59-
cell6_inds = np.array(data["cell6_inds"], dtype=np.int)
60-
for gsr_path, phdos_path, cell_ind in zip(gsr_paths, phdos_paths, cell6_inds, strict=True):
61-
gsr_paths_6d[cell_ind] = gsr_path
62-
phdos_paths_6d[cell_ind] = phdos_path
59+
strain_inds = data["strain_inds"]
60+
#print(strain_inds)
61+
for gsr_path, phdos_path, inds in zip(gsr_relax_paths, phdos_paths, strain_inds, strict=True):
62+
gsr_paths_6d[inds] = gsr_path
63+
phdos_paths_6d[inds] = phdos_path
6364

6465
new = cls.from_files(gsr_paths_6d, phdos_paths_6d, gsr_guess, model='ZSISA')
6566
#new.pickle_dump(workdir, basename=None)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Tests for deformation_utils module"""
2+
import numpy as np
3+
import abipy.data as abidata
4+
5+
from abipy.core.testing import AbipyTest
6+
from abipy.dfpt.deformation_utils import generate_deformations
7+
8+
9+
class DeformationUtilsTest(AbipyTest):
10+
11+
def test_generate_deformations(self):
12+
"""Testing generate_deformations"""
13+
eps = 0.005
14+
si = self.get_structure("Si")
15+
structures_dict, strain_inds, spgrp_number = generate_deformations(si, eps)
16+
assert len(structures_dict) == 3
17+
assert spgrp_number == 227
18+
assert strain_inds.shape == (3, 6)
19+
self.assert_equal(strain_inds[:, 0], [0, 1, 2])
20+
assert np.all(strain_inds[1:, 1] == 0)

abipy/dfpt/tests/test_qha_2d.py

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for QHA_2D"""
22
import os
3+
import numpy as np
34
import abipy.data as abidata
45

56
from abipy.dfpt.qha_2D import QHA_2D
@@ -8,20 +9,25 @@
89

910
class Qha2dTest(AbipyTest):
1011

11-
def test_qha_2d(self):
12+
def test_zsisa_approximation(self):
1213

13-
strains_a = [ 995,1000, 1005, 1010, 1015 ]
14-
strains_c = [ 995,1000, 1005, 1010, 1015 ]
15-
strains_a1 = [ 1000, 1005, 1010 ]
16-
strains_c1 = [ 1000, 1005, 1010 ]
14+
bo_strains_a = [995, 1000, 1005, 1010, 1015]
15+
bo_strains_c = [995, 1000, 1005, 1010, 1015]
16+
phdos_strains_a = [1000, 1005, 1010]
17+
phdos_strains_c = [1000, 1005, 1010]
1718

1819
# Root points to the directory in the git submodule with the output results.
1920
root = os.path.join(abidata.dirpath, "data_v-ZSISA-QHA.git", "ZnO_ZSISA_approximation")
2021

21-
gsr_paths = [[os.path.join(root, f"scale_{s1}_{s3}/out_GSR_DDB") for s3 in strains_c] for s1 in strains_a]
22-
dos_paths = [[os.path.join(root, f"scale_{s1}_{s3}/out_PHDOS.nc") for s3 in strains_c1] for s1 in strains_a1]
22+
gsr_paths = [[os.path.join(root, f"scale_{s1}_{s3}/out_GSR_DDB") for s3 in bo_strains_c] for s1 in bo_strains_a]
23+
dos_paths = [[os.path.join(root, f"scale_{s1}_{s3}/out_PHDOS.nc") for s3 in phdos_strains_c] for s1 in phdos_strains_a]
24+
25+
bo_strains_ac = [bo_strains_a, bo_strains_c]
26+
bo_strains_ac = (np.array(bo_strains_ac) - 1000) / 100
27+
phdos_strains_ac = [phdos_strains_a, phdos_strains_c]
28+
phdos_strains_ac = (np.array(phdos_strains_ac) - 1000) / 100
2329

24-
qha = QHA_2D.from_files(gsr_paths, dos_paths, gsr_file="DDB")
30+
qha = QHA_2D.from_files(gsr_paths, dos_paths, bo_strains_ac, phdos_strains_ac, gsr_file="DDB")
2531

2632
# Test properties and methods.
2733
assert qha.pressure == 0
@@ -48,8 +54,78 @@ def test_qha_2d(self):
4854
self.assert_almost_equal(f_mat, ref_mat)
4955

5056
if self.has_matplotlib():
51-
#if False:
5257
qha.plot_energies(show=False)
5358
qha.plot_free_energies(tstop=500, tstart=0, num=6, show=False)
5459
qha.plot_thermal_expansion(tstop=1000, tstart=0, num=101, show=False)
5560
qha.plot_lattice(tstop=1000, tstart=0, num=101, show=False)
61+
62+
def test_qha_2d(self):
63+
64+
bo_strains_a = [995, 1000, 1005, 1010, 1015]
65+
bo_strains_c = [995, 1000, 1005, 1010, 1015]
66+
#bo_strains_a = [1000, 1005, 1010, 1015 , 1020]
67+
#bo_strains_c = [1000, 1005, 1010, 1015 , 1020]
68+
69+
# Root points to the directory in the git submodule with the output results.
70+
root = os.path.join(abidata.dirpath, "data_v-ZSISA-QHA.git", "ZnO_ZSISA_QHA")
71+
72+
#gsr_paths = [[f"scale_{s1}_{s3}/out_GSR.nc" for s3 in bo_strains_c] for s1 in bo_strains_a]
73+
gsr_paths = [[os.path.join(root, f"scale_{s1}_{s3}/out_GSR_DDB") for s3 in bo_strains_c] for s1 in bo_strains_a]
74+
phdos_paths = [[os.path.join(root, f"scale_{s1}_{s3}/out_PHDOS.nc") for s3 in bo_strains_c] for s1 in bo_strains_a]
75+
76+
bo_strains_ac = [bo_strains_a, bo_strains_c]
77+
bo_strains_ac = (np.array(bo_strains_ac) - 1000) / 100
78+
79+
#qha = QHA_2D.from_files(gsr_paths, phdos_paths, bo_strains_ac, phdos_strains_ac, gsr_file="GSR.nc")
80+
qha = QHA_2D.from_files(gsr_paths, phdos_paths, bo_strains_ac, bo_strains_ac, gsr_file="DDB")
81+
82+
# Test properties and methods.
83+
assert qha.pressure == 0
84+
85+
f_mat = qha.get_vib_free_energies(0, 1000, 2)
86+
#print(f_mat)
87+
ref_mat = [
88+
[
89+
[ 0.2364734 , -1.0138777 ],
90+
[ 0.23334375, -1.02191455],
91+
[ 0.23021055, -1.03048734],
92+
[ 0.22707712, -1.03958781],
93+
[ 0.22394442, -1.04917831]
94+
],
95+
[
96+
[ 0.23500674, -1.0175096 ],
97+
[ 0.23190046, -1.0257329 ],
98+
[ 0.2287909 , -1.03448119],
99+
[ 0.22568001, -1.0437223 ],
100+
[ 0.22257111, -1.05344296]
101+
],
102+
[
103+
[ 0.23352563, -1.02131569],
104+
[ 0.23044276, -1.02971896],
105+
[ 0.22735644, -1.03862873],
106+
[ 0.22427218, -1.04801921],
107+
[ 0.2211813 , -1.05787544]
108+
],
109+
[
110+
[ 0.23203085, -1.02529729],
111+
[ 0.22897121, -1.0338748 ],
112+
[ 0.22590762, -1.04294394],
113+
[ 0.22284219, -1.05248871],
114+
[ 0.21977746, -1.06247964]
115+
],
116+
[
117+
[ 0.2305236 , -1.02944732],
118+
[ 0.22748688, -1.03820052],
119+
[ 0.22444611, -1.04742487],
120+
[ 0.22140302, -1.05711238],
121+
[ 0.21836046, -1.06723406]
122+
]
123+
]
124+
125+
self.assert_almost_equal(f_mat, ref_mat)
126+
127+
if self.has_matplotlib():
128+
assert qha.plot_energies(show=False)
129+
assert qha.plot_free_energies(tstop=500, tstart=0, num=6, show=False)
130+
assert qha.plot_thermal_expansion(tstop=1000, tstart=0, num=101, show=False)
131+
assert qha.plot_lattice(tstop=1000, tstart=00, num=101, show=False)

abipy/dfpt/vzsisa.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ def from_gsr_ddb_paths(cls,
119119
if < 0, it is interpreted as qppa.
120120
gsr_paths: list of paths to GSR files.
121121
ddb_paths: list of paths to DDB files.
122-
anaget_kwargs: dict with extra arguments passed to anaget_phdoses_with_gauss
123-
smearing_ev: Gaussian smearing in eV
124-
verbose:
122+
anaget_kwargs: dict with extra arguments passed to anaget_phdoses_with_gauss.
123+
smearing_ev: Gaussian smearing in eV.
124+
verbose: Verbosity level.
125125
"""
126126
phdos_paths, phbands_paths = anaget_phdoses_with_gauss(nqsmall_or_qppa, smearing_ev, ddb_paths, anaget_kwargs, verbose)
127127
new = cls.from_gsr_phdos_files(gsr_paths, phdos_paths)

0 commit comments

Comments
 (0)