valyte 0.1.7__tar.gz → 0.1.9__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: valyte
3
- Version: 0.1.7
3
+ Version: 0.1.9
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
@@ -13,15 +13,9 @@ Description-Content-Type: text/markdown
13
13
  Requires-Dist: numpy
14
14
  Requires-Dist: matplotlib
15
15
  Requires-Dist: pymatgen
16
- Dynamic: author
17
- Dynamic: author-email
18
- Dynamic: classifier
19
- Dynamic: description
20
- Dynamic: description-content-type
21
- Dynamic: home-page
22
- Dynamic: requires-dist
23
- Dynamic: requires-python
24
- Dynamic: summary
16
+ Requires-Dist: scipy
17
+ Requires-Dist: click
18
+ Requires-Dist: seekpath
25
19
 
26
20
  <p align="center">
27
21
  <img src="valyte/Logo.png" alt="Valyte Logo" width="100%"/>
@@ -119,6 +113,9 @@ valyte supercell 3 3 1 -i POSCAR_primitive -o POSCAR_3x3x1
119
113
 
120
114
  Automatically generate a KPOINTS file with high-symmetry paths for band structure calculations.
121
115
 
116
+ > [!TIP]
117
+ > **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.
118
+
122
119
  ```bash
123
120
  valyte band kpt-gen [options]
124
121
  ```
@@ -127,12 +124,20 @@ valyte band kpt-gen [options]
127
124
  - `-i`, `--input`: Input POSCAR file (default: `POSCAR`).
128
125
  - `-n`, `--npoints`: Points per segment (default: `40`).
129
126
  - `-o`, `--output`: Output filename (default: `KPOINTS`).
127
+ - `--mode`: Path convention. Options: `bradcrack` (Default), `seekpath`, `latimer_munro`, `setyawan_curtarolo`.
130
128
 
131
129
  **Example:**
132
130
  ```bash
133
- valyte band kpt-gen -n 60 -i POSCAR_relaxed -o KPOINTS_band
131
+ # Default (Smart/BradCrack)
132
+ valyte band kpt-gen -n 60
133
+
134
+ # Explicitly use Seekpath convention
135
+ valyte band kpt-gen --mode seekpath
134
136
  ```
135
137
 
138
+ > [!IMPORTANT]
139
+ > 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.
140
+
136
141
  ### 🕸️ Generate K-Points (Interactive)
137
142
 
138
143
  Generate a `KPOINTS` file for SCF calculations interactively.
@@ -1,28 +1,3 @@
1
- Metadata-Version: 2.2
2
- Name: valyte
3
- Version: 0.1.7
4
- Summary: A comprehensive CLI tool for VASP pre-processing (Supercells, K-points) and post-processing (DOS, Band Structure plotting)
5
- Home-page: https://github.com/nikyadav002/Valyte-Project
6
- Author: Nikhil
7
- Author-email: nikhil@example.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.6
12
- Description-Content-Type: text/markdown
13
- Requires-Dist: numpy
14
- Requires-Dist: matplotlib
15
- Requires-Dist: pymatgen
16
- Dynamic: author
17
- Dynamic: author-email
18
- Dynamic: classifier
19
- Dynamic: description
20
- Dynamic: description-content-type
21
- Dynamic: home-page
22
- Dynamic: requires-dist
23
- Dynamic: requires-python
24
- Dynamic: summary
25
-
26
1
  <p align="center">
27
2
  <img src="valyte/Logo.png" alt="Valyte Logo" width="100%"/>
28
3
  </p>
@@ -119,6 +94,9 @@ valyte supercell 3 3 1 -i POSCAR_primitive -o POSCAR_3x3x1
119
94
 
120
95
  Automatically generate a KPOINTS file with high-symmetry paths for band structure calculations.
121
96
 
97
+ > [!TIP]
98
+ > **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.
99
+
122
100
  ```bash
123
101
  valyte band kpt-gen [options]
124
102
  ```
@@ -127,12 +105,20 @@ valyte band kpt-gen [options]
127
105
  - `-i`, `--input`: Input POSCAR file (default: `POSCAR`).
128
106
  - `-n`, `--npoints`: Points per segment (default: `40`).
129
107
  - `-o`, `--output`: Output filename (default: `KPOINTS`).
108
+ - `--mode`: Path convention. Options: `bradcrack` (Default), `seekpath`, `latimer_munro`, `setyawan_curtarolo`.
130
109
 
131
110
  **Example:**
132
111
  ```bash
133
- valyte band kpt-gen -n 60 -i POSCAR_relaxed -o KPOINTS_band
112
+ # Default (Smart/BradCrack)
113
+ valyte band kpt-gen -n 60
114
+
115
+ # Explicitly use Seekpath convention
116
+ valyte band kpt-gen --mode seekpath
134
117
  ```
135
118
 
119
+ > [!IMPORTANT]
120
+ > 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.
121
+
136
122
  ### 🕸️ Generate K-Points (Interactive)
137
123
 
138
124
  Generate a `KPOINTS` file for SCF calculations interactively.
@@ -2,16 +2,20 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="valyte",
5
- version="0.1.7",
5
+ version="0.1.9",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "numpy",
9
9
  "matplotlib",
10
10
  "pymatgen",
11
+ "scipy",
12
+ "click",
13
+ "seekpath"
11
14
  ],
12
15
  include_package_data=True,
13
16
  package_data={
14
17
  "valyte": ["*.png"],
18
+ "valyte.data": ["*.json"],
15
19
  },
16
20
  entry_points={
17
21
  "console_scripts": [
@@ -0,0 +1,289 @@
1
+ """
2
+ Band structure KPOINTS generation module for Valyte.
3
+ """
4
+
5
+ import os
6
+ import json
7
+ import numpy as np
8
+ import seekpath
9
+ import spglib
10
+ from pymatgen.core import Structure
11
+ from pymatgen.symmetry.bandstructure import HighSymmKpath
12
+ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
13
+ try:
14
+ from importlib.resources import files as ilr_files
15
+ except ImportError:
16
+ import importlib_resources as ilr_files
17
+
18
+ from valyte.potcar import generate_potcar
19
+
20
+
21
+ def generate_band_kpoints(poscar_path="POSCAR", npoints=40, output="KPOINTS", symprec=0.01, mode="bradcrack"):
22
+ """
23
+ Generates KPOINTS file in line-mode for band structure calculations.
24
+ Uses SeeK-path method for high-symmetry path determination.
25
+
26
+ IMPORTANT: Writes a standardized POSCAR (POSCAR_standard) that MUST be used
27
+ for the band structure calculation to ensure K-points are valid.
28
+
29
+ Args:
30
+ poscar_path (str): Path to input POSCAR file.
31
+ npoints (int): Number of points per segment (default: 40).
32
+ output (str): Output filename for KPOINTS.
33
+ symprec (float): Symmetry precision for standardization (default: 0.01).
34
+ mode (str): Standardization convention (default: "bradcrack").
35
+ """
36
+
37
+ if not os.path.exists(poscar_path):
38
+ raise FileNotFoundError(f"{poscar_path} not found")
39
+
40
+ # Read structure
41
+ structure = Structure.from_file(poscar_path)
42
+
43
+ # --- K-Point Generation Logic ---
44
+ if mode == "bradcrack":
45
+ try:
46
+ kpath = BradCrackKpath(structure, symprec=symprec)
47
+ prim_std = kpath.prim
48
+ path = kpath.path
49
+ kpoints = kpath.kpoints
50
+
51
+ # Write standardized POSCAR from BradCrack logic
52
+ standard_filename = "POSCAR_standard"
53
+ prim_std.to(filename=standard_filename)
54
+ except Exception as e:
55
+ print(f"❌ Error generating BradCrack path: {e}")
56
+ return
57
+
58
+ else:
59
+ # Fallback to Pymatgen logic for other modes
60
+ try:
61
+ # Map 'seekpath' alias to 'hinuma' which pymatgen uses (wrapper around seekpath)
62
+ if mode == "seekpath":
63
+ mode = "hinuma"
64
+
65
+ # Standardize structure first using SpacegroupAnalyzer
66
+ sga = SpacegroupAnalyzer(structure, symprec=symprec)
67
+ prim_std = sga.get_primitive_standard_structure()
68
+ except Exception as e:
69
+ print(f"❌ Error during standardization: {e}")
70
+ return
71
+
72
+ # Get high-symmetry path for the STANDARDIZED structure
73
+ try:
74
+ kpath = HighSymmKpath(prim_std, path_type=mode, symprec=symprec)
75
+
76
+ # Write the standardized primitive structure
77
+ standard_filename = "POSCAR_standard"
78
+ prim_std.to(filename=standard_filename)
79
+
80
+ # Get the path
81
+ path = kpath.kpath["path"]
82
+ kpoints = kpath.kpath["kpoints"]
83
+ except Exception as e:
84
+ print(f"❌ Error generating K-path: {e}")
85
+ return
86
+
87
+ # Write KPOINTS file
88
+ try:
89
+ with open(output, "w") as f:
90
+ f.write("KPOINTS for Band Structure\n")
91
+ f.write(f"{npoints}\n")
92
+ f.write("Line-mode\n")
93
+ f.write("Reciprocal\n")
94
+
95
+ for subpath in path:
96
+ for i in range(len(subpath) - 1):
97
+ start_label = subpath[i]
98
+ end_label = subpath[i+1]
99
+
100
+ start_coords = kpoints[start_label]
101
+ end_coords = kpoints[end_label]
102
+
103
+ f.write(f"{start_coords[0]:10.6f} {start_coords[1]:10.6f} {start_coords[2]:10.6f} ! {start_label}\n")
104
+ f.write(f"{end_coords[0]:10.6f} {end_coords[1]:10.6f} {end_coords[2]:10.6f} ! {end_label}\n")
105
+ f.write("\n") # Optional newline between segments
106
+
107
+ print(f"✅ Generated {output} ({' - '.join([' - '.join(seg) for seg in path])})")
108
+ print(f"✅ Generated {standard_filename} (Standardized Primitive Cell)")
109
+ print(f"\n⚠️ IMPORTANT: You MUST use '{standard_filename}' for your band calculation!")
110
+ print(f" The K-points are generated for this standardized orientation.")
111
+ print(f" Using your original POSCAR may result in incorrect paths or 'Reciprocal lattice' errors.")
112
+
113
+ except Exception as e:
114
+ print(f"❌ Error writing KPOINTS file: {e}")
115
+
116
+ # --- POTCAR Generation ---
117
+ try:
118
+ print("ℹ️ Generating default POTCAR (PBE)...")
119
+ generate_potcar(poscar_path=poscar_path, functional="PBE", output="POTCAR")
120
+ except Exception as e:
121
+ print(f"⚠️ Could not generate POTCAR: {e}")
122
+ print(" (Proceeding without stopping, as KPOINTS are already generated)")
123
+
124
+
125
+
126
+ class BradCrackKpath:
127
+ """
128
+ Native implementation of Bradley-Cracknell K-path generation.
129
+ Replicates logic from Sumo/SeeK-path to determine standard paths.
130
+ """
131
+ def __init__(self, structure, symprec=0.01):
132
+ self.structure = structure
133
+ self.symprec = symprec
134
+
135
+ # Use SpacegroupAnalyzer for basic data
136
+ sga = SpacegroupAnalyzer(structure, symprec=symprec)
137
+ self._spg_data = sga.get_symmetry_dataset()
138
+
139
+ # Use SeeK-path to get primitive/conventional structures matches Sumo Kpath.__init__
140
+
141
+ # refine_cell logic from Sumo base class
142
+ # atom_numbers = [site.specie.number for site in structure]
143
+ # But pymatgen structure to spglib cell tuple:
144
+ # cell = (lattice, positions, numbers)
145
+ cell = (structure.lattice.matrix, structure.frac_coords, [s.specie.number for s in structure])
146
+
147
+ # Sumo uses spglib.refine_cell on the cell first?
148
+ # "std = spglib.refine_cell(sym._cell, symprec=symprec)"
149
+ # pymatgen sga._cell is (lattice, positions, numbers)
150
+
151
+ # seekpath.get_path takes the cell structure
152
+ # output is dictionary
153
+ self._seek_data = seekpath.get_path(cell, symprec=symprec)
154
+
155
+ # Reconstruct primitive structure from seekpath output
156
+ prim_lattice = self._seek_data["primitive_lattice"]
157
+ prim_pos = self._seek_data["primitive_positions"]
158
+ prim_types = self._seek_data["primitive_types"]
159
+ # Map types back to species?
160
+ # We need a map from number to Element.
161
+ # unique_species from sga?
162
+ # Let's just use explicit element list from input structure, assuming types are consistent?
163
+ # Or better, use sga to map Z to elements.
164
+
165
+ # Setup element mapping
166
+ # Create a map from atomic number to Element object from input structure
167
+ z_to_specie = {s.specie.number: s.specie for s in structure}
168
+ prim_species = [z_to_specie[z] for z in prim_types]
169
+
170
+ self.prim = Structure(prim_lattice, prim_species, prim_pos)
171
+
172
+ conv_lattice = self._seek_data["conv_lattice"]
173
+ conv_pos = self._seek_data["conv_positions"]
174
+ conv_types = self._seek_data["conv_types"]
175
+ conv_species = [z_to_specie[z] for z in conv_types]
176
+ self.conv = Structure(conv_lattice, conv_species, conv_pos)
177
+
178
+ # Now determine Bravais lattice for BradCrack
179
+ self._get_bradcrack_path()
180
+
181
+ def _get_bradcrack_path(self):
182
+
183
+ # Determine lattice parameters from CONVENTIONAL cell
184
+ a, b, c = self.conv.lattice.abc
185
+ angles = self.conv.lattice.angles
186
+ # finding unique axis for monoclinic
187
+ # logic from BradCrackKpath.__init__
188
+ # "unique = angles.index(min(angles, key=angles.count))"
189
+ # usually 90, 90, beta. So unique is beta (non-90) index? No.
190
+ # Monoclinic: alpha=gamma=90, beta!=90. 90 appears twice. non-90 appears once.
191
+ # min count of angle values?
192
+ # if angles are [90, 90, 105], counts are {90:2, 105:1}. min count is 1. value is 105. index is 2.
193
+ # so unique is index of non-90 degree angle.
194
+
195
+ # Round angles to avoid float issues
196
+ angles_r = [round(x, 3) for x in angles]
197
+ unique_val = min(angles_r, key=angles_r.count)
198
+ unique = angles_r.index(unique_val)
199
+
200
+ # Get Space Group Symbol and Number
201
+ # From seekpath? or sga?
202
+ # Sumo uses: "spg_symbol = self.spg_symbol" which is "self._spg_data['international']"
203
+ # spglib dataset returns 'international'
204
+ spg_symbol = self._spg_data["international"]
205
+ spg_number = self._spg_data["number"]
206
+
207
+ lattice_type = self.get_lattice_type(spg_number)
208
+
209
+ bravais = self._get_bravais_lattice(spg_symbol, lattice_type, a, b, c, unique)
210
+
211
+ # Load JSON
212
+
213
+ json_file = ilr_files("valyte.data").joinpath("bradcrack.json")
214
+ with open(json_file, 'r') as f:
215
+ data = json.load(f)
216
+
217
+ if bravais not in data:
218
+ raise ValueError(f"Bravais lattice code '{bravais}' not found in BradCrack data.")
219
+
220
+ self.bradcrack_data = data[bravais]
221
+ self.kpoints = self.bradcrack_data["kpoints"]
222
+ self.path = self.bradcrack_data["path"]
223
+
224
+ def get_lattice_type(self, number):
225
+ # Logic from Sumo
226
+ if 1 <= number <= 2: return "triclinic"
227
+ if 3 <= number <= 15: return "monoclinic"
228
+ if 16 <= number <= 74: return "orthorhombic"
229
+ if 75 <= number <= 142: return "tetragonal"
230
+ if 143 <= number <= 167:
231
+ if number in [146, 148, 155, 160, 161, 166, 167]: return "rhombohedral"
232
+ return "trigonal"
233
+ if 168 <= number <= 194: return "hexagonal"
234
+ if 195 <= number <= 230: return "cubic"
235
+ return "unknown"
236
+
237
+ def _get_bravais_lattice(self, spg_symbol, lattice_type, a, b, c, unique):
238
+ # Logic from Sumo BradCrackKpath._get_bravais_lattice
239
+ if lattice_type == "triclinic": return "triclinic"
240
+
241
+ elif lattice_type == "monoclinic":
242
+ if "P" in spg_symbol:
243
+ if unique == 0: return "mon_p_a"
244
+ elif unique == 1: return "mon_p_b"
245
+ elif unique == 2: return "mon_p_c"
246
+ elif "C" in spg_symbol:
247
+ if unique == 0: return "mon_c_a"
248
+ elif unique == 1: return "mon_c_b"
249
+ elif unique == 2: return "mon_c_c"
250
+
251
+ elif lattice_type == "orthorhombic":
252
+ if "P" in spg_symbol: return "orth_p"
253
+ elif "A" in spg_symbol or "C" in spg_symbol:
254
+ if a > b: return "orth_c_a"
255
+ elif b > a: return "orth_c_b"
256
+ elif "F" in spg_symbol:
257
+ # 1/a^2 etc conditions... need to replicate exact math
258
+ # Copied from Sumo source view
259
+ inv_a2 = 1/a**2; inv_b2 = 1/b**2; inv_c2 = 1/c**2
260
+ if (inv_a2 < inv_b2 + inv_c2) and (inv_b2 < inv_c2 + inv_a2) and (inv_c2 < inv_a2 + inv_b2):
261
+ return "orth_f_1"
262
+ elif inv_c2 > inv_a2 + inv_b2: return "orth_f_2"
263
+ elif inv_b2 > inv_a2 + inv_c2: return "orth_f_3"
264
+ elif inv_a2 > inv_c2 + inv_b2: return "orth_f_4"
265
+ elif "I" in spg_symbol:
266
+ if a > b and a > c: return "orth_i_a"
267
+ elif b > a and b > c: return "orth_i_b"
268
+ elif c > a and c > b: return "orth_i_c"
269
+
270
+ elif lattice_type == "tetragonal":
271
+ if "P" in spg_symbol: return "tet_p"
272
+ elif "I" in spg_symbol:
273
+ if a > c: return "tet_i_a"
274
+ else: return "tet_i_c"
275
+
276
+ elif lattice_type in ["trigonal", "hexagonal", "rhombohedral"]:
277
+ if "R" in spg_symbol:
278
+ if a > np.sqrt(2)*c: return "trig_r_a"
279
+ else: return "trig_r_c"
280
+ elif "P" in spg_symbol:
281
+ if unique == 0: return "trig_p_a"
282
+ elif unique == 2: return "trig_p_c"
283
+
284
+ elif lattice_type == "cubic":
285
+ if "P" in spg_symbol: return "cubic_p"
286
+ elif "I" in spg_symbol: return "cubic_i"
287
+ elif "F" in spg_symbol: return "cubic_f"
288
+
289
+ return "unknown"
@@ -27,6 +27,7 @@ from valyte.band import generate_band_kpoints
27
27
  from valyte.band_plot import plot_band_structure
28
28
  from valyte.dos_plot import load_dos, plot_dos
29
29
  from valyte.kpoints import generate_kpoints_interactive
30
+ from valyte.potcar import generate_potcar
30
31
 
31
32
  def parse_element_selection(inputs):
32
33
  """
@@ -116,10 +117,19 @@ def main():
116
117
  kpt_gen_parser.add_argument("-i", "--input", default="POSCAR", help="Input POSCAR file")
117
118
  kpt_gen_parser.add_argument("-n", "--npoints", type=int, default=40, help="Points per segment")
118
119
  kpt_gen_parser.add_argument("-o", "--output", default="KPOINTS", help="Output filename")
120
+ kpt_gen_parser.add_argument("--symprec", type=float, default=0.01, help="Symmetry precision (default: 0.01)")
121
+
122
+ kpt_gen_parser.add_argument("--mode", default="bradcrack", help="Standardization mode (default: bradcrack)")
119
123
 
120
124
  # --- K-Point Generation (Interactive) ---
121
125
  subparsers.add_parser("kpt", help="Interactive K-Point Generation (SCF)")
122
126
 
127
+ # --- POTCAR Generation ---
128
+ potcar_parser = subparsers.add_parser("potcar", help="Generate POTCAR")
129
+ potcar_parser.add_argument("-i", "--input", default="POSCAR", help="Input POSCAR file")
130
+ potcar_parser.add_argument("-o", "--output", default="POTCAR", help="Output filename")
131
+ potcar_parser.add_argument("--functional", default="PBE", help="Functional (default: PBE)")
132
+
123
133
  args = parser.parse_args()
124
134
 
125
135
  if args.command == "dos":
@@ -169,13 +179,26 @@ def main():
169
179
  print(f"❌ Error: {e}")
170
180
  sys.exit(1)
171
181
 
182
+ elif args.command == "potcar":
183
+ try:
184
+ generate_potcar(
185
+ poscar_path=args.input,
186
+ functional=args.functional,
187
+ output=args.output
188
+ )
189
+ except Exception as e:
190
+ print(f"❌ Error: {e}")
191
+ sys.exit(1)
192
+
172
193
  elif args.command == "band":
173
194
  if args.band_command == "kpt-gen":
174
195
  try:
175
196
  generate_band_kpoints(
176
197
  poscar_path=args.input,
177
198
  npoints=args.npoints,
178
- output=args.output
199
+ output=args.output,
200
+ symprec=args.symprec,
201
+ mode=args.mode
179
202
  )
180
203
  except Exception as e:
181
204
  print(f"❌ Error: {e}")
File without changes