Initial commit
This commit is contained in:
233
skills/pymatgen/scripts/phase_diagram_generator.py
Normal file
233
skills/pymatgen/scripts/phase_diagram_generator.py
Normal file
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Phase diagram generator using Materials Project data.
|
||||
|
||||
This script generates phase diagrams for chemical systems using data from the
|
||||
Materials Project database via pymatgen's MPRester.
|
||||
|
||||
Usage:
|
||||
python phase_diagram_generator.py chemical_system [options]
|
||||
|
||||
Examples:
|
||||
python phase_diagram_generator.py Li-Fe-O
|
||||
python phase_diagram_generator.py Li-Fe-O --output li_fe_o_pd.png
|
||||
python phase_diagram_generator.py Fe-O --show
|
||||
python phase_diagram_generator.py Li-Fe-O --analyze "LiFeO2"
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from pymatgen.core import Composition
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram, PDPlotter
|
||||
except ImportError:
|
||||
print("Error: pymatgen is not installed. Install with: pip install pymatgen")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from mp_api.client import MPRester
|
||||
except ImportError:
|
||||
print("Error: mp-api is not installed. Install with: pip install mp-api")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_api_key() -> str:
|
||||
"""Get Materials Project API key from environment."""
|
||||
api_key = os.environ.get("MP_API_KEY")
|
||||
if not api_key:
|
||||
print("Error: MP_API_KEY environment variable not set.")
|
||||
print("Get your API key from https://next-gen.materialsproject.org/")
|
||||
print("Then set it with: export MP_API_KEY='your_key_here'")
|
||||
sys.exit(1)
|
||||
return api_key
|
||||
|
||||
|
||||
def generate_phase_diagram(chemsys: str, args):
|
||||
"""
|
||||
Generate and analyze phase diagram for a chemical system.
|
||||
|
||||
Args:
|
||||
chemsys: Chemical system (e.g., "Li-Fe-O")
|
||||
args: Command line arguments
|
||||
"""
|
||||
api_key = get_api_key()
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"PHASE DIAGRAM: {chemsys}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
# Get entries from Materials Project
|
||||
print("Fetching data from Materials Project...")
|
||||
with MPRester(api_key) as mpr:
|
||||
entries = mpr.get_entries_in_chemsys(chemsys)
|
||||
|
||||
print(f"✓ Retrieved {len(entries)} entries")
|
||||
|
||||
if len(entries) == 0:
|
||||
print(f"Error: No entries found for chemical system {chemsys}")
|
||||
sys.exit(1)
|
||||
|
||||
# Build phase diagram
|
||||
print("Building phase diagram...")
|
||||
pd = PhaseDiagram(entries)
|
||||
|
||||
# Get stable entries
|
||||
stable_entries = pd.stable_entries
|
||||
print(f"✓ Phase diagram constructed with {len(stable_entries)} stable phases")
|
||||
|
||||
# Print stable phases
|
||||
print("\n--- STABLE PHASES ---")
|
||||
for entry in stable_entries:
|
||||
formula = entry.composition.reduced_formula
|
||||
energy = entry.energy_per_atom
|
||||
print(f" {formula:<20} E = {energy:.4f} eV/atom")
|
||||
|
||||
# Analyze specific composition if requested
|
||||
if args.analyze:
|
||||
print(f"\n--- STABILITY ANALYSIS: {args.analyze} ---")
|
||||
try:
|
||||
comp = Composition(args.analyze)
|
||||
|
||||
# Find closest entry
|
||||
closest_entry = None
|
||||
min_distance = float('inf')
|
||||
|
||||
for entry in entries:
|
||||
if entry.composition.reduced_formula == comp.reduced_formula:
|
||||
closest_entry = entry
|
||||
break
|
||||
|
||||
if closest_entry:
|
||||
# Calculate energy above hull
|
||||
e_above_hull = pd.get_e_above_hull(closest_entry)
|
||||
print(f"Energy above hull: {e_above_hull:.4f} eV/atom")
|
||||
|
||||
if e_above_hull < 0.001:
|
||||
print(f"Status: STABLE (on convex hull)")
|
||||
elif e_above_hull < 0.05:
|
||||
print(f"Status: METASTABLE (nearly stable)")
|
||||
else:
|
||||
print(f"Status: UNSTABLE")
|
||||
|
||||
# Get decomposition
|
||||
decomp = pd.get_decomposition(comp)
|
||||
print(f"\nDecomposes to:")
|
||||
for entry, fraction in decomp.items():
|
||||
formula = entry.composition.reduced_formula
|
||||
print(f" {fraction:.3f} × {formula}")
|
||||
|
||||
# Get reaction energy
|
||||
rxn_energy = pd.get_equilibrium_reaction_energy(closest_entry)
|
||||
print(f"\nDecomposition energy: {rxn_energy:.4f} eV/atom")
|
||||
|
||||
else:
|
||||
print(f"No entry found for composition {args.analyze}")
|
||||
print("Checking stability of hypothetical composition...")
|
||||
|
||||
# Analyze hypothetical composition
|
||||
decomp = pd.get_decomposition(comp)
|
||||
print(f"\nWould decompose to:")
|
||||
for entry, fraction in decomp.items():
|
||||
formula = entry.composition.reduced_formula
|
||||
print(f" {fraction:.3f} × {formula}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error analyzing composition: {e}")
|
||||
|
||||
# Get chemical potentials
|
||||
if args.chemical_potentials:
|
||||
print("\n--- CHEMICAL POTENTIALS ---")
|
||||
print("(at stability regions)")
|
||||
try:
|
||||
chempots = pd.get_all_chempots()
|
||||
for element, potentials in chempots.items():
|
||||
print(f"\n{element}:")
|
||||
for potential in potentials[:5]: # Show first 5
|
||||
print(f" {potential:.4f} eV")
|
||||
except Exception as e:
|
||||
print(f"Could not calculate chemical potentials: {e}")
|
||||
|
||||
# Plot phase diagram
|
||||
print("\n--- GENERATING PLOT ---")
|
||||
plotter = PDPlotter(pd, show_unstable=args.show_unstable)
|
||||
|
||||
if args.output:
|
||||
output_path = Path(args.output)
|
||||
plotter.write_image(str(output_path), image_format=output_path.suffix[1:])
|
||||
print(f"✓ Phase diagram saved to {output_path}")
|
||||
|
||||
if args.show:
|
||||
print("Opening interactive plot...")
|
||||
plotter.show()
|
||||
|
||||
print(f"\n{'='*60}\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate phase diagrams using Materials Project data",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Requirements:
|
||||
- Materials Project API key (set MP_API_KEY environment variable)
|
||||
- mp-api package: pip install mp-api
|
||||
|
||||
Examples:
|
||||
%(prog)s Li-Fe-O
|
||||
%(prog)s Li-Fe-O --output li_fe_o_phase_diagram.png
|
||||
%(prog)s Fe-O --show --analyze "Fe2O3"
|
||||
%(prog)s Li-Fe-O --analyze "LiFeO2" --show-unstable
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"chemsys",
|
||||
help="Chemical system (e.g., Li-Fe-O, Fe-O)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--output", "-o",
|
||||
help="Output file for phase diagram plot (PNG, PDF, SVG)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--show", "-s",
|
||||
action="store_true",
|
||||
help="Show interactive plot"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--analyze", "-a",
|
||||
help="Analyze stability of specific composition (e.g., LiFeO2)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--show-unstable",
|
||||
action="store_true",
|
||||
help="Include unstable phases in plot"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--chemical-potentials",
|
||||
action="store_true",
|
||||
help="Calculate chemical potentials"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate chemical system format
|
||||
elements = args.chemsys.split("-")
|
||||
if len(elements) < 2:
|
||||
print("Error: Chemical system must contain at least 2 elements")
|
||||
print("Example: Li-Fe-O")
|
||||
sys.exit(1)
|
||||
|
||||
# Generate phase diagram
|
||||
generate_phase_diagram(args.chemsys, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
266
skills/pymatgen/scripts/structure_analyzer.py
Normal file
266
skills/pymatgen/scripts/structure_analyzer.py
Normal file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Structure analysis tool using pymatgen.
|
||||
|
||||
Analyzes crystal structures and provides comprehensive information including:
|
||||
- Composition and formula
|
||||
- Space group and symmetry
|
||||
- Lattice parameters
|
||||
- Density
|
||||
- Coordination environment
|
||||
- Bond lengths and angles
|
||||
|
||||
Usage:
|
||||
python structure_analyzer.py structure_file [options]
|
||||
|
||||
Examples:
|
||||
python structure_analyzer.py POSCAR
|
||||
python structure_analyzer.py structure.cif --symmetry --neighbors
|
||||
python structure_analyzer.py POSCAR --export json
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from pymatgen.core import Structure
|
||||
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
||||
from pymatgen.analysis.local_env import CrystalNN
|
||||
except ImportError:
|
||||
print("Error: pymatgen is not installed. Install with: pip install pymatgen")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def analyze_structure(struct: Structure, args) -> dict:
|
||||
"""
|
||||
Perform comprehensive structure analysis.
|
||||
|
||||
Args:
|
||||
struct: Pymatgen Structure object
|
||||
args: Command line arguments
|
||||
|
||||
Returns:
|
||||
Dictionary containing analysis results
|
||||
"""
|
||||
results = {}
|
||||
|
||||
# Basic information
|
||||
print("\n" + "="*60)
|
||||
print("STRUCTURE ANALYSIS")
|
||||
print("="*60)
|
||||
|
||||
print("\n--- COMPOSITION ---")
|
||||
print(f"Formula (reduced): {struct.composition.reduced_formula}")
|
||||
print(f"Formula (full): {struct.composition.formula}")
|
||||
print(f"Formula (Hill): {struct.composition.hill_formula}")
|
||||
print(f"Chemical system: {struct.composition.chemical_system}")
|
||||
print(f"Number of sites: {len(struct)}")
|
||||
print(f"Number of species: {len(struct.composition.elements)}")
|
||||
print(f"Molecular weight: {struct.composition.weight:.2f} amu")
|
||||
|
||||
results['composition'] = {
|
||||
'reduced_formula': struct.composition.reduced_formula,
|
||||
'formula': struct.composition.formula,
|
||||
'hill_formula': struct.composition.hill_formula,
|
||||
'chemical_system': struct.composition.chemical_system,
|
||||
'num_sites': len(struct),
|
||||
'molecular_weight': struct.composition.weight,
|
||||
}
|
||||
|
||||
# Lattice information
|
||||
print("\n--- LATTICE ---")
|
||||
print(f"a = {struct.lattice.a:.4f} Å")
|
||||
print(f"b = {struct.lattice.b:.4f} Å")
|
||||
print(f"c = {struct.lattice.c:.4f} Å")
|
||||
print(f"α = {struct.lattice.alpha:.2f}°")
|
||||
print(f"β = {struct.lattice.beta:.2f}°")
|
||||
print(f"γ = {struct.lattice.gamma:.2f}°")
|
||||
print(f"Volume: {struct.volume:.2f} ų")
|
||||
print(f"Density: {struct.density:.3f} g/cm³")
|
||||
|
||||
results['lattice'] = {
|
||||
'a': struct.lattice.a,
|
||||
'b': struct.lattice.b,
|
||||
'c': struct.lattice.c,
|
||||
'alpha': struct.lattice.alpha,
|
||||
'beta': struct.lattice.beta,
|
||||
'gamma': struct.lattice.gamma,
|
||||
'volume': struct.volume,
|
||||
'density': struct.density,
|
||||
}
|
||||
|
||||
# Symmetry analysis
|
||||
if args.symmetry:
|
||||
print("\n--- SYMMETRY ---")
|
||||
try:
|
||||
sga = SpacegroupAnalyzer(struct)
|
||||
|
||||
spacegroup_symbol = sga.get_space_group_symbol()
|
||||
spacegroup_number = sga.get_space_group_number()
|
||||
crystal_system = sga.get_crystal_system()
|
||||
point_group = sga.get_point_group_symbol()
|
||||
|
||||
print(f"Space group: {spacegroup_symbol} (#{spacegroup_number})")
|
||||
print(f"Crystal system: {crystal_system}")
|
||||
print(f"Point group: {point_group}")
|
||||
|
||||
# Get symmetry operations
|
||||
symm_ops = sga.get_symmetry_operations()
|
||||
print(f"Symmetry operations: {len(symm_ops)}")
|
||||
|
||||
results['symmetry'] = {
|
||||
'spacegroup_symbol': spacegroup_symbol,
|
||||
'spacegroup_number': spacegroup_number,
|
||||
'crystal_system': crystal_system,
|
||||
'point_group': point_group,
|
||||
'num_symmetry_ops': len(symm_ops),
|
||||
}
|
||||
|
||||
# Show equivalent sites
|
||||
sym_struct = sga.get_symmetrized_structure()
|
||||
print(f"Symmetry-equivalent site groups: {len(sym_struct.equivalent_sites)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not determine symmetry: {e}")
|
||||
|
||||
# Site information
|
||||
print("\n--- SITES ---")
|
||||
print(f"{'Index':<6} {'Species':<10} {'Wyckoff':<10} {'Frac Coords':<30}")
|
||||
print("-" * 60)
|
||||
|
||||
for i, site in enumerate(struct):
|
||||
coords_str = f"[{site.frac_coords[0]:.4f}, {site.frac_coords[1]:.4f}, {site.frac_coords[2]:.4f}]"
|
||||
wyckoff = "N/A"
|
||||
|
||||
if args.symmetry:
|
||||
try:
|
||||
sga = SpacegroupAnalyzer(struct)
|
||||
sym_struct = sga.get_symmetrized_structure()
|
||||
wyckoff = sym_struct.equivalent_sites[0][0].species_string # Simplified
|
||||
except:
|
||||
pass
|
||||
|
||||
print(f"{i:<6} {site.species_string:<10} {wyckoff:<10} {coords_str:<30}")
|
||||
|
||||
# Neighbor analysis
|
||||
if args.neighbors:
|
||||
print("\n--- COORDINATION ENVIRONMENT ---")
|
||||
try:
|
||||
cnn = CrystalNN()
|
||||
|
||||
for i, site in enumerate(struct):
|
||||
neighbors = cnn.get_nn_info(struct, i)
|
||||
print(f"\nSite {i} ({site.species_string}):")
|
||||
print(f" Coordination number: {len(neighbors)}")
|
||||
|
||||
if len(neighbors) > 0 and len(neighbors) <= 12:
|
||||
print(f" Neighbors:")
|
||||
for j, neighbor in enumerate(neighbors):
|
||||
neighbor_site = struct[neighbor['site_index']]
|
||||
distance = site.distance(neighbor_site)
|
||||
print(f" {neighbor_site.species_string} at {distance:.3f} Å")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not analyze coordination: {e}")
|
||||
|
||||
# Distance matrix (for small structures)
|
||||
if args.distances and len(struct) <= 20:
|
||||
print("\n--- DISTANCE MATRIX (Å) ---")
|
||||
distance_matrix = struct.distance_matrix
|
||||
|
||||
# Print header
|
||||
print(f"{'':>4}", end="")
|
||||
for i in range(len(struct)):
|
||||
print(f"{i:>8}", end="")
|
||||
print()
|
||||
|
||||
# Print matrix
|
||||
for i in range(len(struct)):
|
||||
print(f"{i:>4}", end="")
|
||||
for j in range(len(struct)):
|
||||
if i == j:
|
||||
print(f"{'---':>8}", end="")
|
||||
else:
|
||||
print(f"{distance_matrix[i][j]:>8.3f}", end="")
|
||||
print()
|
||||
|
||||
print("\n" + "="*60)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Analyze crystal structures using pymatgen",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"structure_file",
|
||||
help="Structure file to analyze (CIF, POSCAR, etc.)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--symmetry", "-s",
|
||||
action="store_true",
|
||||
help="Perform symmetry analysis"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--neighbors", "-n",
|
||||
action="store_true",
|
||||
help="Analyze coordination environment"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--distances", "-d",
|
||||
action="store_true",
|
||||
help="Show distance matrix (for structures with ≤20 atoms)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export", "-e",
|
||||
choices=["json", "yaml"],
|
||||
help="Export analysis results to file"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--output", "-o",
|
||||
help="Output file for exported results"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Read structure
|
||||
try:
|
||||
struct = Structure.from_file(args.structure_file)
|
||||
except Exception as e:
|
||||
print(f"Error reading structure file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Analyze structure
|
||||
results = analyze_structure(struct, args)
|
||||
|
||||
# Export results
|
||||
if args.export:
|
||||
output_file = args.output or f"analysis.{args.export}"
|
||||
|
||||
if args.export == "json":
|
||||
with open(output_file, "w") as f:
|
||||
json.dump(results, f, indent=2)
|
||||
print(f"\n✓ Analysis exported to {output_file}")
|
||||
|
||||
elif args.export == "yaml":
|
||||
try:
|
||||
import yaml
|
||||
with open(output_file, "w") as f:
|
||||
yaml.dump(results, f, default_flow_style=False)
|
||||
print(f"\n✓ Analysis exported to {output_file}")
|
||||
except ImportError:
|
||||
print("Error: PyYAML is not installed. Install with: pip install pyyaml")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
169
skills/pymatgen/scripts/structure_converter.py
Normal file
169
skills/pymatgen/scripts/structure_converter.py
Normal file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Structure file format converter using pymatgen.
|
||||
|
||||
This script converts between different structure file formats supported by pymatgen.
|
||||
Supports automatic format detection and batch conversion.
|
||||
|
||||
Usage:
|
||||
python structure_converter.py input_file output_file
|
||||
python structure_converter.py input_file --format cif
|
||||
python structure_converter.py *.cif --output-dir ./converted --format poscar
|
||||
|
||||
Examples:
|
||||
python structure_converter.py POSCAR structure.cif
|
||||
python structure_converter.py structure.cif --format json
|
||||
python structure_converter.py *.vasp --output-dir ./cif_files --format cif
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
try:
|
||||
from pymatgen.core import Structure
|
||||
except ImportError:
|
||||
print("Error: pymatgen is not installed. Install with: pip install pymatgen")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def convert_structure(input_path: Path, output_path: Path = None, output_format: str = None) -> bool:
|
||||
"""
|
||||
Convert a structure file to a different format.
|
||||
|
||||
Args:
|
||||
input_path: Path to input structure file
|
||||
output_path: Path to output file (optional if output_format is specified)
|
||||
output_format: Target format (e.g., 'cif', 'poscar', 'json', 'yaml')
|
||||
|
||||
Returns:
|
||||
True if conversion succeeded, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Read structure with automatic format detection
|
||||
struct = Structure.from_file(str(input_path))
|
||||
print(f"✓ Read structure: {struct.composition.reduced_formula} from {input_path}")
|
||||
|
||||
# Determine output path
|
||||
if output_path is None and output_format:
|
||||
output_path = input_path.with_suffix(f".{output_format}")
|
||||
elif output_path is None:
|
||||
print("Error: Must specify either output_path or output_format")
|
||||
return False
|
||||
|
||||
# Write structure
|
||||
struct.to(filename=str(output_path))
|
||||
print(f"✓ Wrote structure to {output_path}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error converting {input_path}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def batch_convert(input_files: List[Path], output_dir: Path, output_format: str) -> None:
|
||||
"""
|
||||
Convert multiple structure files to a common format.
|
||||
|
||||
Args:
|
||||
input_files: List of input structure files
|
||||
output_dir: Directory for output files
|
||||
output_format: Target format for all files
|
||||
"""
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
success_count = 0
|
||||
for input_file in input_files:
|
||||
output_file = output_dir / f"{input_file.stem}.{output_format}"
|
||||
if convert_structure(input_file, output_file):
|
||||
success_count += 1
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Conversion complete: {success_count}/{len(input_files)} files converted successfully")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert structure files between different formats using pymatgen",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Supported formats:
|
||||
Input: CIF, POSCAR, CONTCAR, XYZ, PDB, JSON, YAML, and many more
|
||||
Output: CIF, POSCAR, XYZ, PDB, JSON, YAML, XSF, and many more
|
||||
|
||||
Examples:
|
||||
%(prog)s POSCAR structure.cif
|
||||
%(prog)s structure.cif --format json
|
||||
%(prog)s *.cif --output-dir ./poscar_files --format poscar
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"input",
|
||||
nargs="+",
|
||||
help="Input structure file(s). Supports wildcards for batch conversion."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"output",
|
||||
nargs="?",
|
||||
help="Output structure file (ignored if --output-dir is used)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--format", "-f",
|
||||
help="Output format (e.g., cif, poscar, json, yaml, xyz)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--output-dir", "-o",
|
||||
type=Path,
|
||||
help="Output directory for batch conversion"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Expand wildcards and convert to Path objects
|
||||
input_files = []
|
||||
for pattern in args.input:
|
||||
matches = list(Path.cwd().glob(pattern))
|
||||
if matches:
|
||||
input_files.extend(matches)
|
||||
else:
|
||||
input_files.append(Path(pattern))
|
||||
|
||||
# Filter to files only
|
||||
input_files = [f for f in input_files if f.is_file()]
|
||||
|
||||
if not input_files:
|
||||
print("Error: No input files found")
|
||||
sys.exit(1)
|
||||
|
||||
# Batch conversion mode
|
||||
if args.output_dir or len(input_files) > 1:
|
||||
if not args.format:
|
||||
print("Error: --format is required for batch conversion")
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = args.output_dir or Path("./converted")
|
||||
batch_convert(input_files, output_dir, args.format)
|
||||
|
||||
# Single file conversion
|
||||
elif len(input_files) == 1:
|
||||
input_file = input_files[0]
|
||||
|
||||
if args.output:
|
||||
output_file = Path(args.output)
|
||||
convert_structure(input_file, output_file)
|
||||
elif args.format:
|
||||
convert_structure(input_file, output_format=args.format)
|
||||
else:
|
||||
print("Error: Must specify output file or --format")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user