valyte 0.1.9__tar.gz → 0.1.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {valyte-0.1.9/valyte.egg-info → valyte-0.1.11}/PKG-INFO +60 -1
- {valyte-0.1.9 → valyte-0.1.11}/README.md +59 -0
- valyte-0.1.11/pyproject.toml +3 -0
- {valyte-0.1.9 → valyte-0.1.11}/setup.py +1 -1
- valyte-0.1.11/valyte/band.py +247 -0
- valyte-0.1.11/valyte/band_plot.py +86 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/cli.py +66 -81
- valyte-0.1.11/valyte/dos_plot.py +380 -0
- valyte-0.1.11/valyte/ipr.py +186 -0
- valyte-0.1.11/valyte/kpoints.py +80 -0
- valyte-0.1.11/valyte/potcar.py +36 -0
- valyte-0.1.11/valyte/supercell.py +18 -0
- {valyte-0.1.9 → valyte-0.1.11/valyte.egg-info}/PKG-INFO +60 -1
- {valyte-0.1.9 → valyte-0.1.11}/valyte.egg-info/SOURCES.txt +2 -0
- valyte-0.1.9/valyte/band.py +0 -289
- valyte-0.1.9/valyte/band_plot.py +0 -127
- valyte-0.1.9/valyte/dos_plot.py +0 -549
- valyte-0.1.9/valyte/kpoints.py +0 -108
- valyte-0.1.9/valyte/potcar.py +0 -61
- valyte-0.1.9/valyte/supercell.py +0 -35
- {valyte-0.1.9 → valyte-0.1.11}/MANIFEST.in +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/setup.cfg +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/Logo.png +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/__init__.py +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/data/__init__.py +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/data/bradcrack.json +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/valyte_band.png +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte/valyte_dos.png +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte.egg-info/dependency_links.txt +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte.egg-info/entry_points.txt +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte.egg-info/requires.txt +0 -0
- {valyte-0.1.9 → valyte-0.1.11}/valyte.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: valyte
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: A comprehensive CLI tool for VASP pre-processing (Supercells, K-points) and post-processing (DOS, Band Structure plotting)
|
|
5
5
|
Home-page: https://github.com/nikyadav002/Valyte-Project
|
|
6
6
|
Author: Nikhil
|
|
@@ -42,6 +42,7 @@ Requires-Dist: seekpath
|
|
|
42
42
|
- VBM alignment to 0 eV.
|
|
43
43
|
- Color-coded bands (Purple for VB, Teal for CB).
|
|
44
44
|
- High-symmetry path labels from KPOINTS.
|
|
45
|
+
- **IPR Analysis**: Compute Inverse Participation Ratio from PROCAR to analyze wavefunction localization.
|
|
45
46
|
- **Publication Quality**: Clean aesthetics, custom fonts (Arial, Helvetica, Times New Roman), high DPI output.
|
|
46
47
|
|
|
47
48
|
## Installation
|
|
@@ -152,6 +153,38 @@ This command will prompt you for:
|
|
|
152
153
|
|
|
153
154
|
It automatically calculates the optimal grid based on your `POSCAR` structure.
|
|
154
155
|
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 🧪 Generate POTCAR
|
|
159
|
+
|
|
160
|
+
Generate a `POTCAR` file based on the species in your `POSCAR`.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
valyte potcar [options]
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Options:**
|
|
167
|
+
- `-i`, `--input`: Input POSCAR file (default: `POSCAR`).
|
|
168
|
+
- `-o`, `--output`: Output filename (default: `POTCAR`).
|
|
169
|
+
- `--functional`: Functional to use (default: `PBE`). Options include `PBE`, `PBE_52`, `PBE_54`, `LDA`, etc.
|
|
170
|
+
|
|
171
|
+
**Example:**
|
|
172
|
+
```bash
|
|
173
|
+
# Generate POTCAR using default PBE functional
|
|
174
|
+
valyte potcar
|
|
175
|
+
|
|
176
|
+
# Use a specific functional
|
|
177
|
+
valyte potcar --functional PBE_54
|
|
178
|
+
|
|
179
|
+
# Specify input and output files
|
|
180
|
+
valyte potcar -i POSCAR_relaxed -o POTCAR_new
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
> [!IMPORTANT]
|
|
184
|
+
> **Pymatgen Configuration Required**: This command requires Pymatgen to be configured with your VASP pseudopotential directory. Set `PMG_VASP_PSP_DIR` in your `~/.pmgrc.yaml` file. See the [Pymatgen documentation](https://pymatgen.org/installation.html#potcar-setup) for setup instructions.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
155
188
|
#### 2. Plot Band Structure
|
|
156
189
|
|
|
157
190
|
Plot the electronic band structure from `vasprun.xml`.
|
|
@@ -216,4 +249,30 @@ valyte dos -e Fe "Fe(d)"
|
|
|
216
249
|
valyte dos ./vasp_data --xlim -5 5 -o my_dos.png
|
|
217
250
|
```
|
|
218
251
|
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### 📐 Compute IPR (Inverse Participation Ratio)
|
|
255
|
+
|
|
256
|
+
Compute the Inverse Participation Ratio (IPR) from VASP `PROCAR` to analyze wavefunction localization.
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
valyte ipr
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
This interactive command will:
|
|
263
|
+
1. Read the `PROCAR` file from the current directory.
|
|
264
|
+
2. Display the number of k-points, bands, and atoms.
|
|
265
|
+
3. Prompt you for **band indices** to analyze (e.g., `5 6 7` or `5-7`).
|
|
266
|
+
4. Optionally show per-k-point IPR values.
|
|
267
|
+
5. Save results to `ipr_procar.dat`.
|
|
268
|
+
|
|
269
|
+
**Output Columns:**
|
|
270
|
+
- **Band**: Band index.
|
|
271
|
+
- **Energy**: Average energy (eV) across k-points.
|
|
272
|
+
- **IPR**: Inverse Participation Ratio (higher = more localized).
|
|
273
|
+
- **N_eff**: Effective number of atoms (1/IPR).
|
|
274
|
+
|
|
275
|
+
> [!TIP]
|
|
276
|
+
> Use IPR to identify localized defect states. A state localized on a single atom has IPR ≈ 1.0 and N_eff ≈ 1. Delocalized band states have small IPR and large N_eff.
|
|
277
|
+
|
|
219
278
|
</details>
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- VBM alignment to 0 eV.
|
|
24
24
|
- Color-coded bands (Purple for VB, Teal for CB).
|
|
25
25
|
- High-symmetry path labels from KPOINTS.
|
|
26
|
+
- **IPR Analysis**: Compute Inverse Participation Ratio from PROCAR to analyze wavefunction localization.
|
|
26
27
|
- **Publication Quality**: Clean aesthetics, custom fonts (Arial, Helvetica, Times New Roman), high DPI output.
|
|
27
28
|
|
|
28
29
|
## Installation
|
|
@@ -133,6 +134,38 @@ This command will prompt you for:
|
|
|
133
134
|
|
|
134
135
|
It automatically calculates the optimal grid based on your `POSCAR` structure.
|
|
135
136
|
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### 🧪 Generate POTCAR
|
|
140
|
+
|
|
141
|
+
Generate a `POTCAR` file based on the species in your `POSCAR`.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
valyte potcar [options]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Options:**
|
|
148
|
+
- `-i`, `--input`: Input POSCAR file (default: `POSCAR`).
|
|
149
|
+
- `-o`, `--output`: Output filename (default: `POTCAR`).
|
|
150
|
+
- `--functional`: Functional to use (default: `PBE`). Options include `PBE`, `PBE_52`, `PBE_54`, `LDA`, etc.
|
|
151
|
+
|
|
152
|
+
**Example:**
|
|
153
|
+
```bash
|
|
154
|
+
# Generate POTCAR using default PBE functional
|
|
155
|
+
valyte potcar
|
|
156
|
+
|
|
157
|
+
# Use a specific functional
|
|
158
|
+
valyte potcar --functional PBE_54
|
|
159
|
+
|
|
160
|
+
# Specify input and output files
|
|
161
|
+
valyte potcar -i POSCAR_relaxed -o POTCAR_new
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
> [!IMPORTANT]
|
|
165
|
+
> **Pymatgen Configuration Required**: This command requires Pymatgen to be configured with your VASP pseudopotential directory. Set `PMG_VASP_PSP_DIR` in your `~/.pmgrc.yaml` file. See the [Pymatgen documentation](https://pymatgen.org/installation.html#potcar-setup) for setup instructions.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
136
169
|
#### 2. Plot Band Structure
|
|
137
170
|
|
|
138
171
|
Plot the electronic band structure from `vasprun.xml`.
|
|
@@ -197,4 +230,30 @@ valyte dos -e Fe "Fe(d)"
|
|
|
197
230
|
valyte dos ./vasp_data --xlim -5 5 -o my_dos.png
|
|
198
231
|
```
|
|
199
232
|
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### 📐 Compute IPR (Inverse Participation Ratio)
|
|
236
|
+
|
|
237
|
+
Compute the Inverse Participation Ratio (IPR) from VASP `PROCAR` to analyze wavefunction localization.
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
valyte ipr
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
This interactive command will:
|
|
244
|
+
1. Read the `PROCAR` file from the current directory.
|
|
245
|
+
2. Display the number of k-points, bands, and atoms.
|
|
246
|
+
3. Prompt you for **band indices** to analyze (e.g., `5 6 7` or `5-7`).
|
|
247
|
+
4. Optionally show per-k-point IPR values.
|
|
248
|
+
5. Save results to `ipr_procar.dat`.
|
|
249
|
+
|
|
250
|
+
**Output Columns:**
|
|
251
|
+
- **Band**: Band index.
|
|
252
|
+
- **Energy**: Average energy (eV) across k-points.
|
|
253
|
+
- **IPR**: Inverse Participation Ratio (higher = more localized).
|
|
254
|
+
- **N_eff**: Effective number of atoms (1/IPR).
|
|
255
|
+
|
|
256
|
+
> [!TIP]
|
|
257
|
+
> Use IPR to identify localized defect states. A state localized on a single atom has IPR ≈ 1.0 and N_eff ≈ 1. Delocalized band states have small IPR and large N_eff.
|
|
258
|
+
|
|
200
259
|
</details>
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Band structure KPOINTS generation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import numpy as np
|
|
6
|
+
import seekpath
|
|
7
|
+
from pymatgen.core import Structure
|
|
8
|
+
from pymatgen.symmetry.bandstructure import HighSymmKpath
|
|
9
|
+
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
|
10
|
+
try:
|
|
11
|
+
from importlib.resources import files as ilr_files
|
|
12
|
+
except ImportError:
|
|
13
|
+
from importlib_resources import files as ilr_files
|
|
14
|
+
|
|
15
|
+
from valyte.potcar import generate_potcar
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_band_kpoints(poscar_path="POSCAR", npoints=40, output="KPOINTS", symprec=0.01, mode="bradcrack"):
|
|
19
|
+
"""Generate a line-mode KPOINTS file for band structure calculations."""
|
|
20
|
+
|
|
21
|
+
if not os.path.exists(poscar_path):
|
|
22
|
+
raise FileNotFoundError(f"{poscar_path} not found")
|
|
23
|
+
|
|
24
|
+
mode = (mode or "bradcrack").lower()
|
|
25
|
+
|
|
26
|
+
structure = Structure.from_file(poscar_path)
|
|
27
|
+
|
|
28
|
+
if mode == "bradcrack":
|
|
29
|
+
try:
|
|
30
|
+
kpath = BradCrackKpath(structure, symprec=symprec)
|
|
31
|
+
prim_std = kpath.prim
|
|
32
|
+
path = kpath.path
|
|
33
|
+
kpoints = kpath.kpoints
|
|
34
|
+
|
|
35
|
+
standard_filename = "POSCAR_standard"
|
|
36
|
+
prim_std.to(filename=standard_filename)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
raise RuntimeError(f"Error generating Bradley-Cracknell path: {e}")
|
|
39
|
+
else:
|
|
40
|
+
try:
|
|
41
|
+
if mode == "seekpath":
|
|
42
|
+
mode = "hinuma"
|
|
43
|
+
|
|
44
|
+
sga = SpacegroupAnalyzer(structure, symprec=symprec)
|
|
45
|
+
prim_std = sga.get_primitive_standard_structure()
|
|
46
|
+
except Exception as e:
|
|
47
|
+
raise RuntimeError(f"Error during standardization: {e}")
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
kpath = HighSymmKpath(prim_std, path_type=mode, symprec=symprec)
|
|
51
|
+
|
|
52
|
+
standard_filename = "POSCAR_standard"
|
|
53
|
+
prim_std.to(filename=standard_filename)
|
|
54
|
+
|
|
55
|
+
path = kpath.kpath["path"]
|
|
56
|
+
kpoints = kpath.kpath["kpoints"]
|
|
57
|
+
except Exception as e:
|
|
58
|
+
raise RuntimeError(f"Error generating K-path: {e}")
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
with open(output, "w") as f:
|
|
62
|
+
f.write("KPOINTS for Band Structure\n")
|
|
63
|
+
f.write(f"{npoints}\n")
|
|
64
|
+
f.write("Line-mode\n")
|
|
65
|
+
f.write("Reciprocal\n")
|
|
66
|
+
|
|
67
|
+
for subpath in path:
|
|
68
|
+
for i in range(len(subpath) - 1):
|
|
69
|
+
start_label = subpath[i]
|
|
70
|
+
end_label = subpath[i + 1]
|
|
71
|
+
|
|
72
|
+
start_coords = kpoints[start_label]
|
|
73
|
+
end_coords = kpoints[end_label]
|
|
74
|
+
|
|
75
|
+
f.write(
|
|
76
|
+
f"{start_coords[0]:10.6f} {start_coords[1]:10.6f} {start_coords[2]:10.6f} ! {start_label}\n"
|
|
77
|
+
)
|
|
78
|
+
f.write(
|
|
79
|
+
f"{end_coords[0]:10.6f} {end_coords[1]:10.6f} {end_coords[2]:10.6f} ! {end_label}\n"
|
|
80
|
+
)
|
|
81
|
+
f.write("\n")
|
|
82
|
+
|
|
83
|
+
print(f"Generated {output} ({' - '.join([' - '.join(seg) for seg in path])})")
|
|
84
|
+
print(f"Generated {standard_filename} (Standardized Primitive Cell)")
|
|
85
|
+
print("IMPORTANT: Use POSCAR_standard for the band calculation.")
|
|
86
|
+
except Exception as e:
|
|
87
|
+
raise RuntimeError(f"Error writing KPOINTS file: {e}")
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
print("Generating default POTCAR (PBE)...")
|
|
91
|
+
generate_potcar(poscar_path=poscar_path, functional="PBE", output="POTCAR")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"Warning: could not generate POTCAR: {e}")
|
|
94
|
+
print("Proceeding without POTCAR generation.")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class BradCrackKpath:
|
|
98
|
+
"""Bradley-Cracknell K-path generation using SeeK-path output."""
|
|
99
|
+
|
|
100
|
+
def __init__(self, structure, symprec=0.01):
|
|
101
|
+
self.structure = structure
|
|
102
|
+
self.symprec = symprec
|
|
103
|
+
|
|
104
|
+
sga = SpacegroupAnalyzer(structure, symprec=symprec)
|
|
105
|
+
self._spg_data = sga.get_symmetry_dataset()
|
|
106
|
+
|
|
107
|
+
cell = (
|
|
108
|
+
structure.lattice.matrix,
|
|
109
|
+
structure.frac_coords,
|
|
110
|
+
[s.specie.number for s in structure],
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
self._seek_data = seekpath.get_path(cell, symprec=symprec)
|
|
114
|
+
|
|
115
|
+
prim_lattice = self._seek_data["primitive_lattice"]
|
|
116
|
+
prim_pos = self._seek_data["primitive_positions"]
|
|
117
|
+
prim_types = self._seek_data["primitive_types"]
|
|
118
|
+
|
|
119
|
+
z_to_specie = {s.specie.number: s.specie for s in structure}
|
|
120
|
+
prim_species = [z_to_specie[z] for z in prim_types]
|
|
121
|
+
|
|
122
|
+
self.prim = Structure(prim_lattice, prim_species, prim_pos)
|
|
123
|
+
|
|
124
|
+
conv_lattice = self._seek_data["conv_lattice"]
|
|
125
|
+
conv_pos = self._seek_data["conv_positions"]
|
|
126
|
+
conv_types = self._seek_data["conv_types"]
|
|
127
|
+
conv_species = [z_to_specie[z] for z in conv_types]
|
|
128
|
+
self.conv = Structure(conv_lattice, conv_species, conv_pos)
|
|
129
|
+
|
|
130
|
+
self._get_bradcrack_path()
|
|
131
|
+
|
|
132
|
+
def _get_bradcrack_path(self):
|
|
133
|
+
a, b, c = self.conv.lattice.abc
|
|
134
|
+
angles = self.conv.lattice.angles
|
|
135
|
+
|
|
136
|
+
angles_r = [round(x, 3) for x in angles]
|
|
137
|
+
unique_val = min(angles_r, key=angles_r.count)
|
|
138
|
+
unique = angles_r.index(unique_val)
|
|
139
|
+
|
|
140
|
+
spg_symbol = self._spg_data["international"]
|
|
141
|
+
spg_number = self._spg_data["number"]
|
|
142
|
+
|
|
143
|
+
lattice_type = self.get_lattice_type(spg_number)
|
|
144
|
+
bravais = self._get_bravais_lattice(spg_symbol, lattice_type, a, b, c, unique)
|
|
145
|
+
|
|
146
|
+
json_file = ilr_files("valyte.data").joinpath("bradcrack.json")
|
|
147
|
+
with json_file.open("r") as f:
|
|
148
|
+
data = json.load(f)
|
|
149
|
+
|
|
150
|
+
if bravais not in data:
|
|
151
|
+
raise ValueError(f"Bravais lattice code '{bravais}' not found in BradCrack data.")
|
|
152
|
+
|
|
153
|
+
self.bradcrack_data = data[bravais]
|
|
154
|
+
self.kpoints = self.bradcrack_data["kpoints"]
|
|
155
|
+
self.path = self.bradcrack_data["path"]
|
|
156
|
+
|
|
157
|
+
def get_lattice_type(self, number):
|
|
158
|
+
if 1 <= number <= 2:
|
|
159
|
+
return "triclinic"
|
|
160
|
+
if 3 <= number <= 15:
|
|
161
|
+
return "monoclinic"
|
|
162
|
+
if 16 <= number <= 74:
|
|
163
|
+
return "orthorhombic"
|
|
164
|
+
if 75 <= number <= 142:
|
|
165
|
+
return "tetragonal"
|
|
166
|
+
if 143 <= number <= 167:
|
|
167
|
+
if number in [146, 148, 155, 160, 161, 166, 167]:
|
|
168
|
+
return "rhombohedral"
|
|
169
|
+
return "trigonal"
|
|
170
|
+
if 168 <= number <= 194:
|
|
171
|
+
return "hexagonal"
|
|
172
|
+
if 195 <= number <= 230:
|
|
173
|
+
return "cubic"
|
|
174
|
+
return "unknown"
|
|
175
|
+
|
|
176
|
+
def _get_bravais_lattice(self, spg_symbol, lattice_type, a, b, c, unique):
|
|
177
|
+
if lattice_type == "triclinic":
|
|
178
|
+
return "triclinic"
|
|
179
|
+
|
|
180
|
+
if lattice_type == "monoclinic":
|
|
181
|
+
if "P" in spg_symbol:
|
|
182
|
+
if unique == 0:
|
|
183
|
+
return "mon_p_a"
|
|
184
|
+
if unique == 1:
|
|
185
|
+
return "mon_p_b"
|
|
186
|
+
if unique == 2:
|
|
187
|
+
return "mon_p_c"
|
|
188
|
+
if "C" in spg_symbol:
|
|
189
|
+
if unique == 0:
|
|
190
|
+
return "mon_c_a"
|
|
191
|
+
if unique == 1:
|
|
192
|
+
return "mon_c_b"
|
|
193
|
+
if unique == 2:
|
|
194
|
+
return "mon_c_c"
|
|
195
|
+
|
|
196
|
+
if lattice_type == "orthorhombic":
|
|
197
|
+
if "P" in spg_symbol:
|
|
198
|
+
return "orth_p"
|
|
199
|
+
if "A" in spg_symbol or "C" in spg_symbol:
|
|
200
|
+
if a > b:
|
|
201
|
+
return "orth_c_a"
|
|
202
|
+
if b > a:
|
|
203
|
+
return "orth_c_b"
|
|
204
|
+
if "F" in spg_symbol:
|
|
205
|
+
inv_a2 = 1 / a**2
|
|
206
|
+
inv_b2 = 1 / b**2
|
|
207
|
+
inv_c2 = 1 / c**2
|
|
208
|
+
if (inv_a2 < inv_b2 + inv_c2) and (inv_b2 < inv_c2 + inv_a2) and (inv_c2 < inv_a2 + inv_b2):
|
|
209
|
+
return "orth_f_1"
|
|
210
|
+
if inv_c2 > inv_a2 + inv_b2:
|
|
211
|
+
return "orth_f_2"
|
|
212
|
+
if inv_b2 > inv_a2 + inv_c2:
|
|
213
|
+
return "orth_f_3"
|
|
214
|
+
if inv_a2 > inv_c2 + inv_b2:
|
|
215
|
+
return "orth_f_4"
|
|
216
|
+
if "I" in spg_symbol:
|
|
217
|
+
if a > b and a > c:
|
|
218
|
+
return "orth_i_a"
|
|
219
|
+
if b > a and b > c:
|
|
220
|
+
return "orth_i_b"
|
|
221
|
+
if c > a and c > b:
|
|
222
|
+
return "orth_i_c"
|
|
223
|
+
|
|
224
|
+
if lattice_type == "tetragonal":
|
|
225
|
+
if "P" in spg_symbol:
|
|
226
|
+
return "tet_p"
|
|
227
|
+
if "I" in spg_symbol:
|
|
228
|
+
return "tet_i_a" if a > c else "tet_i_c"
|
|
229
|
+
|
|
230
|
+
if lattice_type in ["trigonal", "hexagonal", "rhombohedral"]:
|
|
231
|
+
if "R" in spg_symbol:
|
|
232
|
+
return "trig_r_a" if a > np.sqrt(2) * c else "trig_r_c"
|
|
233
|
+
if "P" in spg_symbol:
|
|
234
|
+
if unique == 0:
|
|
235
|
+
return "trig_p_a"
|
|
236
|
+
if unique == 2:
|
|
237
|
+
return "trig_p_c"
|
|
238
|
+
|
|
239
|
+
if lattice_type == "cubic":
|
|
240
|
+
if "P" in spg_symbol:
|
|
241
|
+
return "cubic_p"
|
|
242
|
+
if "I" in spg_symbol:
|
|
243
|
+
return "cubic_i"
|
|
244
|
+
if "F" in spg_symbol:
|
|
245
|
+
return "cubic_f"
|
|
246
|
+
|
|
247
|
+
return "unknown"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib as mpl
|
|
4
|
+
mpl.use("agg")
|
|
5
|
+
mpl.rcParams["axes.unicode_minus"] = False
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
from pymatgen.io.vasp import BSVasprun
|
|
8
|
+
from pymatgen.electronic_structure.plotter import BSPlotter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def plot_band_structure(vasprun_path, kpoints_path=None, output="valyte_band.png",
|
|
12
|
+
ylim=None, figsize=(4, 4), dpi=400, font="Arial"):
|
|
13
|
+
"""Plot the electronic band structure from a VASP vasprun.xml."""
|
|
14
|
+
|
|
15
|
+
if os.path.isdir(vasprun_path):
|
|
16
|
+
vasprun_path = os.path.join(vasprun_path, "vasprun.xml")
|
|
17
|
+
|
|
18
|
+
font_map = {
|
|
19
|
+
"arial": "Arial",
|
|
20
|
+
"helvetica": "Helvetica",
|
|
21
|
+
"times": "Times New Roman",
|
|
22
|
+
"times new roman": "Times New Roman",
|
|
23
|
+
}
|
|
24
|
+
font = font_map.get(font.lower(), "Arial")
|
|
25
|
+
mpl.rcParams["font.family"] = font
|
|
26
|
+
mpl.rcParams["axes.linewidth"] = 1.4
|
|
27
|
+
mpl.rcParams["font.weight"] = "bold"
|
|
28
|
+
mpl.rcParams["font.size"] = 14
|
|
29
|
+
mpl.rcParams["xtick.major.width"] = 1.2
|
|
30
|
+
mpl.rcParams["ytick.major.width"] = 1.2
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
vr = BSVasprun(vasprun_path, parse_projected_eigen=False)
|
|
34
|
+
bs = vr.get_band_structure(kpoints_filename=kpoints_path, line_mode=True)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
raise ValueError(f"Failed to load band structure: {e}")
|
|
37
|
+
|
|
38
|
+
bs_plotter = BSPlotter(bs)
|
|
39
|
+
data = bs_plotter.bs_plot_data(zero_to_efermi=True)
|
|
40
|
+
|
|
41
|
+
distances = data["distances"]
|
|
42
|
+
energies = data["energy"]
|
|
43
|
+
ticks = data["ticks"]
|
|
44
|
+
|
|
45
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
46
|
+
|
|
47
|
+
color_vb = "#8e44ad"
|
|
48
|
+
color_cb = "#2a9d8f"
|
|
49
|
+
|
|
50
|
+
for i in range(len(distances)):
|
|
51
|
+
d = distances[i]
|
|
52
|
+
|
|
53
|
+
if isinstance(energies, dict):
|
|
54
|
+
for spin in energies:
|
|
55
|
+
for band in energies[spin][i]:
|
|
56
|
+
c = color_vb if np.mean(band) <= 0 else color_cb
|
|
57
|
+
ax.plot(d, band, color=c, lw=1.5, alpha=1.0)
|
|
58
|
+
else:
|
|
59
|
+
for spin in energies[i]:
|
|
60
|
+
for band in energies[i][spin]:
|
|
61
|
+
c = color_vb if np.mean(band) <= 0 else color_cb
|
|
62
|
+
ax.plot(d, band, color=c, lw=1.5, alpha=1.0)
|
|
63
|
+
|
|
64
|
+
ax.set_xticks(ticks["distance"])
|
|
65
|
+
clean_labels = [(l or "").replace("$\\mid$", "|") for l in ticks["label"]]
|
|
66
|
+
ax.set_xticklabels(clean_labels, fontsize=14, fontweight="bold")
|
|
67
|
+
|
|
68
|
+
for d in ticks["distance"]:
|
|
69
|
+
ax.axvline(d, color="k", lw=0.8, ls="-", alpha=0.3)
|
|
70
|
+
|
|
71
|
+
ax.axhline(0, color="k", lw=0.8, ls="--", alpha=0.5)
|
|
72
|
+
|
|
73
|
+
ax.set_ylabel("Energy (eV)", fontsize=16, fontweight="bold", labelpad=8)
|
|
74
|
+
if ylim:
|
|
75
|
+
ax.set_ylim(ylim)
|
|
76
|
+
yticks = np.arange(np.ceil(ylim[0]), np.floor(ylim[1]) + 1, 1)
|
|
77
|
+
ax.set_yticks(yticks)
|
|
78
|
+
else:
|
|
79
|
+
ax.set_ylim(-4, 4)
|
|
80
|
+
ax.set_yticks(np.arange(-4, 5, 1))
|
|
81
|
+
|
|
82
|
+
ax.set_xlim(distances[0][0], distances[-1][-1])
|
|
83
|
+
|
|
84
|
+
plt.tight_layout()
|
|
85
|
+
plt.savefig(output, dpi=dpi)
|
|
86
|
+
plt.close(fig)
|