valyte 0.1.8__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.
Files changed (31) hide show
  1. {valyte-0.1.8/valyte.egg-info → valyte-0.1.11}/PKG-INFO +72 -2
  2. {valyte-0.1.8 → valyte-0.1.11}/README.md +71 -1
  3. valyte-0.1.11/pyproject.toml +3 -0
  4. {valyte-0.1.8 → valyte-0.1.11}/setup.py +1 -1
  5. valyte-0.1.11/valyte/band.py +247 -0
  6. valyte-0.1.11/valyte/band_plot.py +86 -0
  7. {valyte-0.1.8 → valyte-0.1.11}/valyte/cli.py +81 -78
  8. valyte-0.1.11/valyte/dos_plot.py +380 -0
  9. valyte-0.1.11/valyte/ipr.py +186 -0
  10. valyte-0.1.11/valyte/kpoints.py +80 -0
  11. valyte-0.1.11/valyte/potcar.py +36 -0
  12. valyte-0.1.11/valyte/supercell.py +18 -0
  13. {valyte-0.1.8 → valyte-0.1.11/valyte.egg-info}/PKG-INFO +72 -2
  14. {valyte-0.1.8 → valyte-0.1.11}/valyte.egg-info/SOURCES.txt +3 -0
  15. valyte-0.1.8/valyte/band.py +0 -278
  16. valyte-0.1.8/valyte/band_plot.py +0 -127
  17. valyte-0.1.8/valyte/dos_plot.py +0 -549
  18. valyte-0.1.8/valyte/kpoints.py +0 -96
  19. valyte-0.1.8/valyte/supercell.py +0 -35
  20. {valyte-0.1.8 → valyte-0.1.11}/MANIFEST.in +0 -0
  21. {valyte-0.1.8 → valyte-0.1.11}/setup.cfg +0 -0
  22. {valyte-0.1.8 → valyte-0.1.11}/valyte/Logo.png +0 -0
  23. {valyte-0.1.8 → valyte-0.1.11}/valyte/__init__.py +0 -0
  24. {valyte-0.1.8 → valyte-0.1.11}/valyte/data/__init__.py +0 -0
  25. {valyte-0.1.8 → valyte-0.1.11}/valyte/data/bradcrack.json +0 -0
  26. {valyte-0.1.8 → valyte-0.1.11}/valyte/valyte_band.png +0 -0
  27. {valyte-0.1.8 → valyte-0.1.11}/valyte/valyte_dos.png +0 -0
  28. {valyte-0.1.8 → valyte-0.1.11}/valyte.egg-info/dependency_links.txt +0 -0
  29. {valyte-0.1.8 → valyte-0.1.11}/valyte.egg-info/entry_points.txt +0 -0
  30. {valyte-0.1.8 → valyte-0.1.11}/valyte.egg-info/requires.txt +0 -0
  31. {valyte-0.1.8 → 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.8
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
@@ -113,6 +114,9 @@ valyte supercell 3 3 1 -i POSCAR_primitive -o POSCAR_3x3x1
113
114
 
114
115
  Automatically generate a KPOINTS file with high-symmetry paths for band structure calculations.
115
116
 
117
+ > [!TIP]
118
+ > **Smart K-Path Generation (New in v0.1.7+)**: Valyte now automatically determines the standard path (e.g., `\Gamma - Y - V` for Monoclinic cells) using the **Bradley-Cracknell** convention by default. This ensures clean, publication-ready labels without external dependencies.
119
+
116
120
  ```bash
117
121
  valyte band kpt-gen [options]
118
122
  ```
@@ -121,12 +125,20 @@ valyte band kpt-gen [options]
121
125
  - `-i`, `--input`: Input POSCAR file (default: `POSCAR`).
122
126
  - `-n`, `--npoints`: Points per segment (default: `40`).
123
127
  - `-o`, `--output`: Output filename (default: `KPOINTS`).
128
+ - `--mode`: Path convention. Options: `bradcrack` (Default), `seekpath`, `latimer_munro`, `setyawan_curtarolo`.
124
129
 
125
130
  **Example:**
126
131
  ```bash
127
- valyte band kpt-gen -n 60 -i POSCAR_relaxed -o KPOINTS_band
132
+ # Default (Smart/BradCrack)
133
+ valyte band kpt-gen -n 60
134
+
135
+ # Explicitly use Seekpath convention
136
+ valyte band kpt-gen --mode seekpath
128
137
  ```
129
138
 
139
+ > [!IMPORTANT]
140
+ > The command will generate a **`POSCAR_standard`** file. You **MUST** use this structure for your band structure calculation (i.e., `cp POSCAR_standard POSCAR`) because the K-path corresponds to this specific orientation. Using your original POSCAR may result in incorrect paths.
141
+
130
142
  ### 🕸️ Generate K-Points (Interactive)
131
143
 
132
144
  Generate a `KPOINTS` file for SCF calculations interactively.
@@ -141,6 +153,38 @@ This command will prompt you for:
141
153
 
142
154
  It automatically calculates the optimal grid based on your `POSCAR` structure.
143
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
+
144
188
  #### 2. Plot Band Structure
145
189
 
146
190
  Plot the electronic band structure from `vasprun.xml`.
@@ -205,4 +249,30 @@ valyte dos -e Fe "Fe(d)"
205
249
  valyte dos ./vasp_data --xlim -5 5 -o my_dos.png
206
250
  ```
207
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
+
208
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
@@ -94,6 +95,9 @@ valyte supercell 3 3 1 -i POSCAR_primitive -o POSCAR_3x3x1
94
95
 
95
96
  Automatically generate a KPOINTS file with high-symmetry paths for band structure calculations.
96
97
 
98
+ > [!TIP]
99
+ > **Smart K-Path Generation (New in v0.1.7+)**: Valyte now automatically determines the standard path (e.g., `\Gamma - Y - V` for Monoclinic cells) using the **Bradley-Cracknell** convention by default. This ensures clean, publication-ready labels without external dependencies.
100
+
97
101
  ```bash
98
102
  valyte band kpt-gen [options]
99
103
  ```
@@ -102,12 +106,20 @@ valyte band kpt-gen [options]
102
106
  - `-i`, `--input`: Input POSCAR file (default: `POSCAR`).
103
107
  - `-n`, `--npoints`: Points per segment (default: `40`).
104
108
  - `-o`, `--output`: Output filename (default: `KPOINTS`).
109
+ - `--mode`: Path convention. Options: `bradcrack` (Default), `seekpath`, `latimer_munro`, `setyawan_curtarolo`.
105
110
 
106
111
  **Example:**
107
112
  ```bash
108
- valyte band kpt-gen -n 60 -i POSCAR_relaxed -o KPOINTS_band
113
+ # Default (Smart/BradCrack)
114
+ valyte band kpt-gen -n 60
115
+
116
+ # Explicitly use Seekpath convention
117
+ valyte band kpt-gen --mode seekpath
109
118
  ```
110
119
 
120
+ > [!IMPORTANT]
121
+ > The command will generate a **`POSCAR_standard`** file. You **MUST** use this structure for your band structure calculation (i.e., `cp POSCAR_standard POSCAR`) because the K-path corresponds to this specific orientation. Using your original POSCAR may result in incorrect paths.
122
+
111
123
  ### 🕸️ Generate K-Points (Interactive)
112
124
 
113
125
  Generate a `KPOINTS` file for SCF calculations interactively.
@@ -122,6 +134,38 @@ This command will prompt you for:
122
134
 
123
135
  It automatically calculates the optimal grid based on your `POSCAR` structure.
124
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
+
125
169
  #### 2. Plot Band Structure
126
170
 
127
171
  Plot the electronic band structure from `vasprun.xml`.
@@ -186,4 +230,30 @@ valyte dos -e Fe "Fe(d)"
186
230
  valyte dos ./vasp_data --xlim -5 5 -o my_dos.png
187
231
  ```
188
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
+
189
259
  </details>
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="valyte",
5
- version="0.1.8",
5
+ version="0.1.11",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "numpy",
@@ -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)