valyte 0.1.8__py3-none-any.whl → 0.1.11__py3-none-any.whl

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/cli.py CHANGED
@@ -1,17 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- Valyte CLI Tool
4
- ===============
5
-
6
- A post-processing tool for VASP outputs, designed to create publication-quality
7
- plots with a modern aesthetic. Supports DOS and band structure plotting.
8
-
9
- Features:
10
- - DOS plotting with gradient fills
11
- - Band structure plotting
12
- - Smart legend positioning
13
- - Custom font support
14
- """
2
+ """Valyte CLI entry point."""
15
3
 
16
4
  import os
17
5
  import sys
@@ -27,59 +15,52 @@ from valyte.band import generate_band_kpoints
27
15
  from valyte.band_plot import plot_band_structure
28
16
  from valyte.dos_plot import load_dos, plot_dos
29
17
  from valyte.kpoints import generate_kpoints_interactive
18
+ from valyte.potcar import generate_potcar
19
+ from valyte.ipr import run_ipr_interactive
20
+
21
+
22
+ def _normalize_element(symbol: str) -> str:
23
+ if not symbol:
24
+ return symbol
25
+ return symbol[0].upper() + symbol[1:].lower()
26
+
30
27
 
31
28
  def parse_element_selection(inputs):
32
- """
33
- Parses user input for elements and orbitals.
34
-
35
- Args:
36
- inputs (list): List of strings, e.g., ["Ag", "Bi(s)", "O(p)"]
37
-
38
- Returns:
39
- tuple: (elements_to_load, plotting_config)
40
- elements_to_load (list): Elements to extract from VASP data.
41
- plotting_config (list): List of (Element, Orbital) tuples.
42
- """
29
+ """Parse element/orbital selections like "Fe", "Fe(d)", "O(p)"."""
43
30
  if not inputs:
44
31
  return None, None
45
32
 
46
- elements_to_load = set()
33
+ elements_to_load = []
34
+ elements_seen = set()
47
35
  plotting_config = []
48
-
49
- # Regex to match "Element" or "Element(orbital)"
50
- pattern = re.compile(r"^([A-Za-z]+)(?:\(([spdf])\))?$")
51
-
36
+
37
+ pattern = re.compile(r"^([A-Za-z]+)(?:\(([spdf])\))?$", re.IGNORECASE)
38
+
52
39
  for item in inputs:
53
- match = pattern.match(item)
54
- if match:
55
- el = match.group(1)
56
- orb = match.group(2) # None if no orbital specified
57
-
58
- elements_to_load.add(el)
59
- if orb:
60
- plotting_config.append((el, orb))
61
- else:
62
- plotting_config.append((el, 'total'))
63
- else:
64
- print(f"⚠️ Warning: Could not parse '{item}'. Ignoring.")
65
-
40
+ match = pattern.match(item.strip())
41
+ if not match:
42
+ print(f"Warning: could not parse '{item}'. Ignoring.")
43
+ continue
66
44
 
67
- return list(elements_to_load), plotting_config
45
+ el = _normalize_element(match.group(1))
46
+ orb = match.group(2).lower() if match.group(2) else None
47
+
48
+ if el not in elements_seen:
49
+ elements_to_load.append(el)
50
+ elements_seen.add(el)
51
+
52
+ plotting_config.append((el, orb or "total"))
53
+
54
+ return elements_to_load, plotting_config
68
55
 
69
56
 
70
- # ===============================================================
71
- # Main CLI
72
- # ===============================================================
73
57
  def main():
74
- """
75
- Main entry point for the CLI.
76
- """
77
58
  parser = argparse.ArgumentParser(description="Valyte: VASP Post-Processing Tool")
78
59
  subparsers = parser.add_subparsers(dest="command", help="Available commands")
79
60
 
80
- # --- DOS Subcommand ---
61
+ # DOS
81
62
  dos_parser = subparsers.add_parser("dos", help="Plot Density of States (DOS)")
82
- dos_parser.add_argument("filepath", nargs="?", help="Path to vasprun.xml or directory containing it (optional)")
63
+ dos_parser.add_argument("filepath", nargs="?", help="Path to vasprun.xml or directory containing it")
83
64
  dos_parser.add_argument("--vasprun", help="Explicit path to vasprun.xml (alternative to positional argument)")
84
65
  dos_parser.add_argument("-e", "--elements", nargs="+", help="Elements/Orbitals to plot (e.g., 'Fe O' or 'Fe(d) O(p)')")
85
66
  dos_parser.add_argument("-o", "--output", default="valyte_dos.png", help="Output filename")
@@ -91,7 +72,7 @@ def main():
91
72
  dos_parser.add_argument("--legend-cutoff", type=float, default=0.10, help="Threshold for legend visibility (0.0-1.0)")
92
73
  dos_parser.add_argument("--font", default="Arial", help="Font family")
93
74
 
94
- # --- Supercell Subcommand ---
75
+ # Supercell
95
76
  supercell_parser = subparsers.add_parser("supercell", help="Create a supercell")
96
77
  supercell_parser.add_argument("nx", type=int, help="Supercell size x")
97
78
  supercell_parser.add_argument("ny", type=int, help="Supercell size y")
@@ -99,44 +80,51 @@ def main():
99
80
  supercell_parser.add_argument("-i", "--input", default="POSCAR", help="Input POSCAR file")
100
81
  supercell_parser.add_argument("-o", "--output", default="POSCAR_supercell", help="Output filename")
101
82
 
102
- # --- Band Structure Subcommand ---
83
+ # Band
103
84
  band_parser = subparsers.add_parser("band", help="Band structure utilities")
104
85
  band_subparsers = band_parser.add_subparsers(dest="band_command", help="Band commands")
105
86
 
106
- # Band Plotting (default if no subcommand)
107
- # Note: argparse doesn't easily support default subcommands, so we handle this in logic
87
+ # Band plotting (default)
108
88
  band_parser.add_argument("--vasprun", default=".", help="Path to vasprun.xml or directory")
109
89
  band_parser.add_argument("--kpoints", help="Path to KPOINTS file (for labels)")
110
90
  band_parser.add_argument("-o", "--output", default="valyte_band.png", help="Output filename")
111
91
  band_parser.add_argument("--ylim", nargs=2, type=float, help="Energy range (min max)")
112
92
  band_parser.add_argument("--font", default="Arial", help="Font family")
113
93
 
114
- # Band KPOINTS Generation
94
+ # Band KPOINTS generation
115
95
  kpt_gen_parser = band_subparsers.add_parser("kpt-gen", help="Generate KPOINTS for band structure")
116
96
  kpt_gen_parser.add_argument("-i", "--input", default="POSCAR", help="Input POSCAR file")
117
97
  kpt_gen_parser.add_argument("-n", "--npoints", type=int, default=40, help="Points per segment")
118
98
  kpt_gen_parser.add_argument("-o", "--output", default="KPOINTS", help="Output filename")
119
99
  kpt_gen_parser.add_argument("--symprec", type=float, default=0.01, help="Symmetry precision (default: 0.01)")
120
-
121
100
  kpt_gen_parser.add_argument("--mode", default="bradcrack", help="Standardization mode (default: bradcrack)")
122
101
 
123
- # --- K-Point Generation (Interactive) ---
102
+ # KPOINTS (interactive)
124
103
  subparsers.add_parser("kpt", help="Interactive K-Point Generation (SCF)")
125
104
 
105
+ # POTCAR
106
+ potcar_parser = subparsers.add_parser("potcar", help="Generate POTCAR")
107
+ potcar_parser.add_argument("-i", "--input", default="POSCAR", help="Input POSCAR file")
108
+ potcar_parser.add_argument("-o", "--output", default="POTCAR", help="Output filename")
109
+ potcar_parser.add_argument("--functional", default="PBE", help="Functional (default: PBE)")
110
+
111
+ # IPR
112
+ subparsers.add_parser("ipr", help="Compute IPR from PROCAR")
113
+
126
114
  args = parser.parse_args()
127
115
 
128
116
  if args.command == "dos":
129
- # Resolve filepath: positional > flag > current dir
130
117
  target_path = args.filepath if args.filepath else args.vasprun
131
118
  if not target_path:
132
119
  target_path = "."
133
-
120
+
134
121
  elements, plotting_config = parse_element_selection(args.elements)
135
-
122
+
136
123
  try:
137
124
  dos_data, pdos_data = load_dos(target_path, elements)
138
125
  plot_dos(
139
- dos_data, pdos_data,
126
+ dos_data,
127
+ pdos_data,
140
128
  out=args.output,
141
129
  xlim=tuple(args.xlim),
142
130
  ylim=tuple(args.ylim) if args.ylim else None,
@@ -145,11 +133,10 @@ def main():
145
133
  show_total=not args.pdos,
146
134
  plotting_config=plotting_config,
147
135
  legend_cutoff=args.legend_cutoff,
148
- scale_factor=args.scale
136
+ scale_factor=args.scale,
149
137
  )
150
- # print(f"✅ DOS plot saved to {args.output}") # Silent mode
151
138
  except Exception as e:
152
- print(f"Error: {e}")
139
+ print(f"Error: {e}")
153
140
  sys.exit(1)
154
141
 
155
142
  elif args.command == "supercell":
@@ -159,17 +146,35 @@ def main():
159
146
  nx=args.nx,
160
147
  ny=args.ny,
161
148
  nz=args.nz,
162
- output=args.output
149
+ output=args.output,
163
150
  )
164
151
  except Exception as e:
165
- print(f"Error: {e}")
152
+ print(f"Error: {e}")
166
153
  sys.exit(1)
167
-
154
+
168
155
  elif args.command == "kpt":
169
156
  try:
170
157
  generate_kpoints_interactive()
171
158
  except Exception as e:
172
- print(f"Error: {e}")
159
+ print(f"Error: {e}")
160
+ sys.exit(1)
161
+
162
+ elif args.command == "potcar":
163
+ try:
164
+ generate_potcar(
165
+ poscar_path=args.input,
166
+ functional=args.functional,
167
+ output=args.output,
168
+ )
169
+ except Exception as e:
170
+ print(f"Error: {e}")
171
+ sys.exit(1)
172
+
173
+ elif args.command == "ipr":
174
+ try:
175
+ run_ipr_interactive()
176
+ except Exception as e:
177
+ print(f"Error: {e}")
173
178
  sys.exit(1)
174
179
 
175
180
  elif args.command == "band":
@@ -180,23 +185,19 @@ def main():
180
185
  npoints=args.npoints,
181
186
  output=args.output,
182
187
  symprec=args.symprec,
183
- mode=args.mode
188
+ mode=args.mode,
184
189
  )
185
190
  except Exception as e:
186
- print(f"Error: {e}")
191
+ print(f"Error: {e}")
187
192
  sys.exit(1)
188
193
  elif args.band_command == "plot" or args.band_command is None:
189
- # Default behavior for 'valyte band' is plotting
190
194
  try:
191
- # Determine input path: --vasprun > positional > current dir
192
- target_path = args.vasprun or args.filepath or "."
195
+ target_path = args.vasprun or "."
193
196
  if os.path.isdir(target_path):
194
197
  target_path = os.path.join(target_path, "vasprun.xml")
195
-
196
- # Determine KPOINTS path
198
+
197
199
  kpoints_path = args.kpoints
198
200
  if not kpoints_path:
199
- # Try to find KPOINTS in the same directory as vasprun.xml
200
201
  base_dir = os.path.dirname(target_path)
201
202
  potential_kpoints = os.path.join(base_dir, "KPOINTS")
202
203
  if os.path.exists(potential_kpoints):
@@ -207,10 +208,11 @@ def main():
207
208
  kpoints_path=kpoints_path,
208
209
  output=args.output,
209
210
  ylim=tuple(args.ylim) if args.ylim else None,
210
- font=args.font
211
+ font=args.font,
211
212
  )
212
213
  except Exception:
213
214
  import traceback
215
+
214
216
  traceback.print_exc()
215
217
  sys.exit(1)
216
218
  else:
@@ -218,5 +220,6 @@ def main():
218
220
  else:
219
221
  parser.print_help()
220
222
 
223
+
221
224
  if __name__ == "__main__":
222
225
  main()