Initial commit
This commit is contained in:
685
skills/pymatgen/SKILL.md
Normal file
685
skills/pymatgen/SKILL.md
Normal file
@@ -0,0 +1,685 @@
|
||||
---
|
||||
name: pymatgen
|
||||
description: "Materials science toolkit. Crystal structures (CIF, POSCAR), phase diagrams, band structure, DOS, Materials Project integration, format conversion, for computational materials science."
|
||||
---
|
||||
|
||||
# Pymatgen - Python Materials Genomics
|
||||
|
||||
## Overview
|
||||
|
||||
Pymatgen is a comprehensive Python library for materials analysis that powers the Materials Project. Create, analyze, and manipulate crystal structures and molecules, compute phase diagrams and thermodynamic properties, analyze electronic structure (band structures, DOS), generate surfaces and interfaces, and access Materials Project's database of computed materials. Supports 100+ file formats from various computational codes.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
This skill should be used when:
|
||||
- Working with crystal structures or molecular systems in materials science
|
||||
- Converting between structure file formats (CIF, POSCAR, XYZ, etc.)
|
||||
- Analyzing symmetry, space groups, or coordination environments
|
||||
- Computing phase diagrams or assessing thermodynamic stability
|
||||
- Analyzing electronic structure data (band gaps, DOS, band structures)
|
||||
- Generating surfaces, slabs, or studying interfaces
|
||||
- Accessing the Materials Project database programmatically
|
||||
- Setting up high-throughput computational workflows
|
||||
- Analyzing diffusion, magnetism, or mechanical properties
|
||||
- Working with VASP, Gaussian, Quantum ESPRESSO, or other computational codes
|
||||
|
||||
## Quick Start Guide
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Core pymatgen
|
||||
uv pip install pymatgen
|
||||
|
||||
# With Materials Project API access
|
||||
uv pip install pymatgen mp-api
|
||||
|
||||
# Optional dependencies for extended functionality
|
||||
uv pip install pymatgen[analysis] # Additional analysis tools
|
||||
uv pip install pymatgen[vis] # Visualization tools
|
||||
```
|
||||
|
||||
### Basic Structure Operations
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure, Lattice
|
||||
|
||||
# Read structure from file (automatic format detection)
|
||||
struct = Structure.from_file("POSCAR")
|
||||
|
||||
# Create structure from scratch
|
||||
lattice = Lattice.cubic(3.84)
|
||||
struct = Structure(lattice, ["Si", "Si"], [[0,0,0], [0.25,0.25,0.25]])
|
||||
|
||||
# Write to different format
|
||||
struct.to(filename="structure.cif")
|
||||
|
||||
# Basic properties
|
||||
print(f"Formula: {struct.composition.reduced_formula}")
|
||||
print(f"Space group: {struct.get_space_group_info()}")
|
||||
print(f"Density: {struct.density:.2f} g/cm³")
|
||||
```
|
||||
|
||||
### Materials Project Integration
|
||||
|
||||
```bash
|
||||
# Set up API key
|
||||
export MP_API_KEY="your_api_key_here"
|
||||
```
|
||||
|
||||
```python
|
||||
from mp_api.client import MPRester
|
||||
|
||||
with MPRester() as mpr:
|
||||
# Get structure by material ID
|
||||
struct = mpr.get_structure_by_material_id("mp-149")
|
||||
|
||||
# Search for materials
|
||||
materials = mpr.materials.summary.search(
|
||||
formula="Fe2O3",
|
||||
energy_above_hull=(0, 0.05)
|
||||
)
|
||||
```
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Structure Creation and Manipulation
|
||||
|
||||
Create structures using various methods and perform transformations.
|
||||
|
||||
**From files:**
|
||||
```python
|
||||
# Automatic format detection
|
||||
struct = Structure.from_file("structure.cif")
|
||||
struct = Structure.from_file("POSCAR")
|
||||
mol = Molecule.from_file("molecule.xyz")
|
||||
```
|
||||
|
||||
**From scratch:**
|
||||
```python
|
||||
from pymatgen.core import Structure, Lattice
|
||||
|
||||
# Using lattice parameters
|
||||
lattice = Lattice.from_parameters(a=3.84, b=3.84, c=3.84,
|
||||
alpha=120, beta=90, gamma=60)
|
||||
coords = [[0, 0, 0], [0.75, 0.5, 0.75]]
|
||||
struct = Structure(lattice, ["Si", "Si"], coords)
|
||||
|
||||
# From space group
|
||||
struct = Structure.from_spacegroup(
|
||||
"Fm-3m",
|
||||
Lattice.cubic(3.5),
|
||||
["Si"],
|
||||
[[0, 0, 0]]
|
||||
)
|
||||
```
|
||||
|
||||
**Transformations:**
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import (
|
||||
SupercellTransformation,
|
||||
SubstitutionTransformation,
|
||||
PrimitiveCellTransformation
|
||||
)
|
||||
|
||||
# Create supercell
|
||||
trans = SupercellTransformation([[2,0,0],[0,2,0],[0,0,2]])
|
||||
supercell = trans.apply_transformation(struct)
|
||||
|
||||
# Substitute elements
|
||||
trans = SubstitutionTransformation({"Fe": "Mn"})
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
|
||||
# Get primitive cell
|
||||
trans = PrimitiveCellTransformation()
|
||||
primitive = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
**Reference:** See `references/core_classes.md` for comprehensive documentation of Structure, Lattice, Molecule, and related classes.
|
||||
|
||||
### 2. File Format Conversion
|
||||
|
||||
Convert between 100+ file formats with automatic format detection.
|
||||
|
||||
**Using convenience methods:**
|
||||
```python
|
||||
# Read any format
|
||||
struct = Structure.from_file("input_file")
|
||||
|
||||
# Write to any format
|
||||
struct.to(filename="output.cif")
|
||||
struct.to(filename="POSCAR")
|
||||
struct.to(filename="output.xyz")
|
||||
```
|
||||
|
||||
**Using the conversion script:**
|
||||
```bash
|
||||
# Single file conversion
|
||||
python scripts/structure_converter.py POSCAR structure.cif
|
||||
|
||||
# Batch conversion
|
||||
python scripts/structure_converter.py *.cif --output-dir ./poscar_files --format poscar
|
||||
```
|
||||
|
||||
**Reference:** See `references/io_formats.md` for detailed documentation of all supported formats and code integrations.
|
||||
|
||||
### 3. Structure Analysis and Symmetry
|
||||
|
||||
Analyze structures for symmetry, coordination, and other properties.
|
||||
|
||||
**Symmetry analysis:**
|
||||
```python
|
||||
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
||||
|
||||
sga = SpacegroupAnalyzer(struct)
|
||||
|
||||
# Get space group information
|
||||
print(f"Space group: {sga.get_space_group_symbol()}")
|
||||
print(f"Number: {sga.get_space_group_number()}")
|
||||
print(f"Crystal system: {sga.get_crystal_system()}")
|
||||
|
||||
# Get conventional/primitive cells
|
||||
conventional = sga.get_conventional_standard_structure()
|
||||
primitive = sga.get_primitive_standard_structure()
|
||||
```
|
||||
|
||||
**Coordination environment:**
|
||||
```python
|
||||
from pymatgen.analysis.local_env import CrystalNN
|
||||
|
||||
cnn = CrystalNN()
|
||||
neighbors = cnn.get_nn_info(struct, n=0) # Neighbors of site 0
|
||||
|
||||
print(f"Coordination number: {len(neighbors)}")
|
||||
for neighbor in neighbors:
|
||||
site = struct[neighbor['site_index']]
|
||||
print(f" {site.species_string} at {neighbor['weight']:.3f} Å")
|
||||
```
|
||||
|
||||
**Using the analysis script:**
|
||||
```bash
|
||||
# Comprehensive analysis
|
||||
python scripts/structure_analyzer.py POSCAR --symmetry --neighbors
|
||||
|
||||
# Export results
|
||||
python scripts/structure_analyzer.py structure.cif --symmetry --export json
|
||||
```
|
||||
|
||||
**Reference:** See `references/analysis_modules.md` for detailed documentation of all analysis capabilities.
|
||||
|
||||
### 4. Phase Diagrams and Thermodynamics
|
||||
|
||||
Construct phase diagrams and analyze thermodynamic stability.
|
||||
|
||||
**Phase diagram construction:**
|
||||
```python
|
||||
from mp_api.client import MPRester
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram, PDPlotter
|
||||
|
||||
# Get entries from Materials Project
|
||||
with MPRester() as mpr:
|
||||
entries = mpr.get_entries_in_chemsys("Li-Fe-O")
|
||||
|
||||
# Build phase diagram
|
||||
pd = PhaseDiagram(entries)
|
||||
|
||||
# Check stability
|
||||
from pymatgen.core import Composition
|
||||
comp = Composition("LiFeO2")
|
||||
|
||||
# Find entry for composition
|
||||
for entry in entries:
|
||||
if entry.composition.reduced_formula == comp.reduced_formula:
|
||||
e_above_hull = pd.get_e_above_hull(entry)
|
||||
print(f"Energy above hull: {e_above_hull:.4f} eV/atom")
|
||||
|
||||
if e_above_hull > 0.001:
|
||||
# Get decomposition
|
||||
decomp = pd.get_decomposition(comp)
|
||||
print("Decomposes to:", decomp)
|
||||
|
||||
# Plot
|
||||
plotter = PDPlotter(pd)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
**Using the phase diagram script:**
|
||||
```bash
|
||||
# Generate phase diagram
|
||||
python scripts/phase_diagram_generator.py Li-Fe-O --output li_fe_o.png
|
||||
|
||||
# Analyze specific composition
|
||||
python scripts/phase_diagram_generator.py Li-Fe-O --analyze "LiFeO2" --show
|
||||
```
|
||||
|
||||
**Reference:** See `references/analysis_modules.md` (Phase Diagrams section) and `references/transformations_workflows.md` (Workflow 2) for detailed examples.
|
||||
|
||||
### 5. Electronic Structure Analysis
|
||||
|
||||
Analyze band structures, density of states, and electronic properties.
|
||||
|
||||
**Band structure:**
|
||||
```python
|
||||
from pymatgen.io.vasp import Vasprun
|
||||
from pymatgen.electronic_structure.plotter import BSPlotter
|
||||
|
||||
# Read from VASP calculation
|
||||
vasprun = Vasprun("vasprun.xml")
|
||||
bs = vasprun.get_band_structure()
|
||||
|
||||
# Analyze
|
||||
band_gap = bs.get_band_gap()
|
||||
print(f"Band gap: {band_gap['energy']:.3f} eV")
|
||||
print(f"Direct: {band_gap['direct']}")
|
||||
print(f"Is metal: {bs.is_metal()}")
|
||||
|
||||
# Plot
|
||||
plotter = BSPlotter(bs)
|
||||
plotter.save_plot("band_structure.png")
|
||||
```
|
||||
|
||||
**Density of states:**
|
||||
```python
|
||||
from pymatgen.electronic_structure.plotter import DosPlotter
|
||||
|
||||
dos = vasprun.complete_dos
|
||||
|
||||
# Get element-projected DOS
|
||||
element_dos = dos.get_element_dos()
|
||||
for element, element_dos_obj in element_dos.items():
|
||||
print(f"{element}: {element_dos_obj.get_gap():.3f} eV")
|
||||
|
||||
# Plot
|
||||
plotter = DosPlotter()
|
||||
plotter.add_dos("Total DOS", dos)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
**Reference:** See `references/analysis_modules.md` (Electronic Structure section) and `references/io_formats.md` (VASP section).
|
||||
|
||||
### 6. Surface and Interface Analysis
|
||||
|
||||
Generate slabs, analyze surfaces, and study interfaces.
|
||||
|
||||
**Slab generation:**
|
||||
```python
|
||||
from pymatgen.core.surface import SlabGenerator
|
||||
|
||||
# Generate slabs for specific Miller index
|
||||
slabgen = SlabGenerator(
|
||||
struct,
|
||||
miller_index=(1, 1, 1),
|
||||
min_slab_size=10.0, # Å
|
||||
min_vacuum_size=10.0, # Å
|
||||
center_slab=True
|
||||
)
|
||||
|
||||
slabs = slabgen.get_slabs()
|
||||
|
||||
# Write slabs
|
||||
for i, slab in enumerate(slabs):
|
||||
slab.to(filename=f"slab_{i}.cif")
|
||||
```
|
||||
|
||||
**Wulff shape construction:**
|
||||
```python
|
||||
from pymatgen.analysis.wulff import WulffShape
|
||||
|
||||
# Define surface energies
|
||||
surface_energies = {
|
||||
(1, 0, 0): 1.0,
|
||||
(1, 1, 0): 1.1,
|
||||
(1, 1, 1): 0.9,
|
||||
}
|
||||
|
||||
wulff = WulffShape(struct.lattice, surface_energies)
|
||||
print(f"Surface area: {wulff.surface_area:.2f} Ų")
|
||||
print(f"Volume: {wulff.volume:.2f} ų")
|
||||
|
||||
wulff.show()
|
||||
```
|
||||
|
||||
**Adsorption site finding:**
|
||||
```python
|
||||
from pymatgen.analysis.adsorption import AdsorbateSiteFinder
|
||||
from pymatgen.core import Molecule
|
||||
|
||||
asf = AdsorbateSiteFinder(slab)
|
||||
|
||||
# Find sites
|
||||
ads_sites = asf.find_adsorption_sites()
|
||||
print(f"On-top sites: {len(ads_sites['ontop'])}")
|
||||
print(f"Bridge sites: {len(ads_sites['bridge'])}")
|
||||
print(f"Hollow sites: {len(ads_sites['hollow'])}")
|
||||
|
||||
# Add adsorbate
|
||||
adsorbate = Molecule("O", [[0, 0, 0]])
|
||||
ads_struct = asf.add_adsorbate(adsorbate, ads_sites["ontop"][0])
|
||||
```
|
||||
|
||||
**Reference:** See `references/analysis_modules.md` (Surface and Interface section) and `references/transformations_workflows.md` (Workflows 3 and 9).
|
||||
|
||||
### 7. Materials Project Database Access
|
||||
|
||||
Programmatically access the Materials Project database.
|
||||
|
||||
**Setup:**
|
||||
1. Get API key from https://next-gen.materialsproject.org/
|
||||
2. Set environment variable: `export MP_API_KEY="your_key_here"`
|
||||
|
||||
**Search and retrieve:**
|
||||
```python
|
||||
from mp_api.client import MPRester
|
||||
|
||||
with MPRester() as mpr:
|
||||
# Search by formula
|
||||
materials = mpr.materials.summary.search(formula="Fe2O3")
|
||||
|
||||
# Search by chemical system
|
||||
materials = mpr.materials.summary.search(chemsys="Li-Fe-O")
|
||||
|
||||
# Filter by properties
|
||||
materials = mpr.materials.summary.search(
|
||||
chemsys="Li-Fe-O",
|
||||
energy_above_hull=(0, 0.05), # Stable/metastable
|
||||
band_gap=(1.0, 3.0) # Semiconducting
|
||||
)
|
||||
|
||||
# Get structure
|
||||
struct = mpr.get_structure_by_material_id("mp-149")
|
||||
|
||||
# Get band structure
|
||||
bs = mpr.get_bandstructure_by_material_id("mp-149")
|
||||
|
||||
# Get entries for phase diagram
|
||||
entries = mpr.get_entries_in_chemsys("Li-Fe-O")
|
||||
```
|
||||
|
||||
**Reference:** See `references/materials_project_api.md` for comprehensive API documentation and examples.
|
||||
|
||||
### 8. Computational Workflow Setup
|
||||
|
||||
Set up calculations for various electronic structure codes.
|
||||
|
||||
**VASP input generation:**
|
||||
```python
|
||||
from pymatgen.io.vasp.sets import MPRelaxSet, MPStaticSet, MPNonSCFSet
|
||||
|
||||
# Relaxation
|
||||
relax = MPRelaxSet(struct)
|
||||
relax.write_input("./relax_calc")
|
||||
|
||||
# Static calculation
|
||||
static = MPStaticSet(struct)
|
||||
static.write_input("./static_calc")
|
||||
|
||||
# Band structure (non-self-consistent)
|
||||
nscf = MPNonSCFSet(struct, mode="line")
|
||||
nscf.write_input("./bandstructure_calc")
|
||||
|
||||
# Custom parameters
|
||||
custom = MPRelaxSet(struct, user_incar_settings={"ENCUT": 600})
|
||||
custom.write_input("./custom_calc")
|
||||
```
|
||||
|
||||
**Other codes:**
|
||||
```python
|
||||
# Gaussian
|
||||
from pymatgen.io.gaussian import GaussianInput
|
||||
|
||||
gin = GaussianInput(
|
||||
mol,
|
||||
functional="B3LYP",
|
||||
basis_set="6-31G(d)",
|
||||
route_parameters={"Opt": None}
|
||||
)
|
||||
gin.write_file("input.gjf")
|
||||
|
||||
# Quantum ESPRESSO
|
||||
from pymatgen.io.pwscf import PWInput
|
||||
|
||||
pwin = PWInput(struct, control={"calculation": "scf"})
|
||||
pwin.write_file("pw.in")
|
||||
```
|
||||
|
||||
**Reference:** See `references/io_formats.md` (Electronic Structure Code I/O section) and `references/transformations_workflows.md` for workflow examples.
|
||||
|
||||
### 9. Advanced Analysis
|
||||
|
||||
**Diffraction patterns:**
|
||||
```python
|
||||
from pymatgen.analysis.diffraction.xrd import XRDCalculator
|
||||
|
||||
xrd = XRDCalculator()
|
||||
pattern = xrd.get_pattern(struct)
|
||||
|
||||
# Get peaks
|
||||
for peak in pattern.hkls:
|
||||
print(f"2θ = {peak['2theta']:.2f}°, hkl = {peak['hkl']}")
|
||||
|
||||
pattern.plot()
|
||||
```
|
||||
|
||||
**Elastic properties:**
|
||||
```python
|
||||
from pymatgen.analysis.elasticity import ElasticTensor
|
||||
|
||||
# From elastic tensor matrix
|
||||
elastic_tensor = ElasticTensor.from_voigt(matrix)
|
||||
|
||||
print(f"Bulk modulus: {elastic_tensor.k_voigt:.1f} GPa")
|
||||
print(f"Shear modulus: {elastic_tensor.g_voigt:.1f} GPa")
|
||||
print(f"Young's modulus: {elastic_tensor.y_mod:.1f} GPa")
|
||||
```
|
||||
|
||||
**Magnetic ordering:**
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import MagOrderingTransformation
|
||||
|
||||
# Enumerate magnetic orderings
|
||||
trans = MagOrderingTransformation({"Fe": 5.0})
|
||||
mag_structs = trans.apply_transformation(struct, return_ranked_list=True)
|
||||
|
||||
# Get lowest energy magnetic structure
|
||||
lowest_energy_struct = mag_structs[0]['structure']
|
||||
```
|
||||
|
||||
**Reference:** See `references/analysis_modules.md` for comprehensive analysis module documentation.
|
||||
|
||||
## Bundled Resources
|
||||
|
||||
### Scripts (`scripts/`)
|
||||
|
||||
Executable Python scripts for common tasks:
|
||||
|
||||
- **`structure_converter.py`**: Convert between structure file formats
|
||||
- Supports batch conversion and automatic format detection
|
||||
- Usage: `python scripts/structure_converter.py POSCAR structure.cif`
|
||||
|
||||
- **`structure_analyzer.py`**: Comprehensive structure analysis
|
||||
- Symmetry, coordination, lattice parameters, distance matrix
|
||||
- Usage: `python scripts/structure_analyzer.py structure.cif --symmetry --neighbors`
|
||||
|
||||
- **`phase_diagram_generator.py`**: Generate phase diagrams from Materials Project
|
||||
- Stability analysis and thermodynamic properties
|
||||
- Usage: `python scripts/phase_diagram_generator.py Li-Fe-O --analyze "LiFeO2"`
|
||||
|
||||
All scripts include detailed help: `python scripts/script_name.py --help`
|
||||
|
||||
### References (`references/`)
|
||||
|
||||
Comprehensive documentation loaded into context as needed:
|
||||
|
||||
- **`core_classes.md`**: Element, Structure, Lattice, Molecule, Composition classes
|
||||
- **`io_formats.md`**: File format support and code integration (VASP, Gaussian, etc.)
|
||||
- **`analysis_modules.md`**: Phase diagrams, surfaces, electronic structure, symmetry
|
||||
- **`materials_project_api.md`**: Complete Materials Project API guide
|
||||
- **`transformations_workflows.md`**: Transformations framework and common workflows
|
||||
|
||||
Load references when detailed information is needed about specific modules or workflows.
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### High-Throughput Structure Generation
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import SubstitutionTransformation
|
||||
from pymatgen.io.vasp.sets import MPRelaxSet
|
||||
|
||||
# Generate doped structures
|
||||
base_struct = Structure.from_file("POSCAR")
|
||||
dopants = ["Mn", "Co", "Ni", "Cu"]
|
||||
|
||||
for dopant in dopants:
|
||||
trans = SubstitutionTransformation({"Fe": dopant})
|
||||
doped_struct = trans.apply_transformation(base_struct)
|
||||
|
||||
# Generate VASP inputs
|
||||
vasp_input = MPRelaxSet(doped_struct)
|
||||
vasp_input.write_input(f"./calcs/Fe_{dopant}")
|
||||
```
|
||||
|
||||
### Band Structure Calculation Workflow
|
||||
|
||||
```python
|
||||
# 1. Relaxation
|
||||
relax = MPRelaxSet(struct)
|
||||
relax.write_input("./1_relax")
|
||||
|
||||
# 2. Static (after relaxation)
|
||||
relaxed = Structure.from_file("1_relax/CONTCAR")
|
||||
static = MPStaticSet(relaxed)
|
||||
static.write_input("./2_static")
|
||||
|
||||
# 3. Band structure (non-self-consistent)
|
||||
nscf = MPNonSCFSet(relaxed, mode="line")
|
||||
nscf.write_input("./3_bandstructure")
|
||||
|
||||
# 4. Analysis
|
||||
from pymatgen.io.vasp import Vasprun
|
||||
vasprun = Vasprun("3_bandstructure/vasprun.xml")
|
||||
bs = vasprun.get_band_structure()
|
||||
bs.get_band_gap()
|
||||
```
|
||||
|
||||
### Surface Energy Calculation
|
||||
|
||||
```python
|
||||
# 1. Get bulk energy
|
||||
bulk_vasprun = Vasprun("bulk/vasprun.xml")
|
||||
bulk_E_per_atom = bulk_vasprun.final_energy / len(bulk)
|
||||
|
||||
# 2. Generate and calculate slabs
|
||||
slabgen = SlabGenerator(bulk, (1,1,1), 10, 15)
|
||||
slab = slabgen.get_slabs()[0]
|
||||
|
||||
MPRelaxSet(slab).write_input("./slab_calc")
|
||||
|
||||
# 3. Calculate surface energy (after calculation)
|
||||
slab_vasprun = Vasprun("slab_calc/vasprun.xml")
|
||||
E_surf = (slab_vasprun.final_energy - len(slab) * bulk_E_per_atom) / (2 * slab.surface_area)
|
||||
E_surf *= 16.021766 # Convert eV/Ų to J/m²
|
||||
```
|
||||
|
||||
**More workflows:** See `references/transformations_workflows.md` for 10 detailed workflow examples.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Structure Handling
|
||||
|
||||
1. **Use automatic format detection**: `Structure.from_file()` handles most formats
|
||||
2. **Prefer immutable structures**: Use `IStructure` when structure shouldn't change
|
||||
3. **Check symmetry**: Use `SpacegroupAnalyzer` to reduce to primitive cell
|
||||
4. **Validate structures**: Check for overlapping atoms or unreasonable bond lengths
|
||||
|
||||
### File I/O
|
||||
|
||||
1. **Use convenience methods**: `from_file()` and `to()` are preferred
|
||||
2. **Specify formats explicitly**: When automatic detection fails
|
||||
3. **Handle exceptions**: Wrap file I/O in try-except blocks
|
||||
4. **Use serialization**: `as_dict()`/`from_dict()` for version-safe storage
|
||||
|
||||
### Materials Project API
|
||||
|
||||
1. **Use context manager**: Always use `with MPRester() as mpr:`
|
||||
2. **Batch queries**: Request multiple items at once
|
||||
3. **Cache results**: Save frequently used data locally
|
||||
4. **Filter effectively**: Use property filters to reduce data transfer
|
||||
|
||||
### Computational Workflows
|
||||
|
||||
1. **Use input sets**: Prefer `MPRelaxSet`, `MPStaticSet` over manual INCAR
|
||||
2. **Check convergence**: Always verify calculations converged
|
||||
3. **Track transformations**: Use `TransformedStructure` for provenance
|
||||
4. **Organize calculations**: Use clear directory structures
|
||||
|
||||
### Performance
|
||||
|
||||
1. **Reduce symmetry**: Use primitive cells when possible
|
||||
2. **Limit neighbor searches**: Specify reasonable cutoff radii
|
||||
3. **Use appropriate methods**: Different analysis tools have different speed/accuracy tradeoffs
|
||||
4. **Parallelize when possible**: Many operations can be parallelized
|
||||
|
||||
## Units and Conventions
|
||||
|
||||
Pymatgen uses atomic units throughout:
|
||||
- **Lengths**: Angstroms (Å)
|
||||
- **Energies**: Electronvolts (eV)
|
||||
- **Angles**: Degrees (°)
|
||||
- **Magnetic moments**: Bohr magnetons (μB)
|
||||
- **Time**: Femtoseconds (fs)
|
||||
|
||||
Convert units using `pymatgen.core.units` when needed.
|
||||
|
||||
## Integration with Other Tools
|
||||
|
||||
Pymatgen integrates seamlessly with:
|
||||
- **ASE** (Atomic Simulation Environment)
|
||||
- **Phonopy** (phonon calculations)
|
||||
- **BoltzTraP** (transport properties)
|
||||
- **Atomate/Fireworks** (workflow management)
|
||||
- **AiiDA** (provenance tracking)
|
||||
- **Zeo++** (pore analysis)
|
||||
- **OpenBabel** (molecule conversion)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Import errors**: Install missing dependencies
|
||||
```bash
|
||||
uv pip install pymatgen[analysis,vis]
|
||||
```
|
||||
|
||||
**API key not found**: Set MP_API_KEY environment variable
|
||||
```bash
|
||||
export MP_API_KEY="your_key_here"
|
||||
```
|
||||
|
||||
**Structure read failures**: Check file format and syntax
|
||||
```python
|
||||
# Try explicit format specification
|
||||
struct = Structure.from_file("file.txt", fmt="cif")
|
||||
```
|
||||
|
||||
**Symmetry analysis fails**: Structure may have numerical precision issues
|
||||
```python
|
||||
# Increase tolerance
|
||||
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
||||
sga = SpacegroupAnalyzer(struct, symprec=0.1)
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **Documentation**: https://pymatgen.org/
|
||||
- **Materials Project**: https://materialsproject.org/
|
||||
- **GitHub**: https://github.com/materialsproject/pymatgen
|
||||
- **Forum**: https://matsci.org/
|
||||
- **Example notebooks**: https://matgenb.materialsvirtuallab.org/
|
||||
|
||||
## Version Notes
|
||||
|
||||
This skill is designed for pymatgen 2024.x and later. For the Materials Project API, use the `mp-api` package (separate from legacy `pymatgen.ext.matproj`).
|
||||
|
||||
Requirements:
|
||||
- Python 3.10 or higher
|
||||
- pymatgen >= 2023.x
|
||||
- mp-api (for Materials Project access)
|
||||
530
skills/pymatgen/references/analysis_modules.md
Normal file
530
skills/pymatgen/references/analysis_modules.md
Normal file
@@ -0,0 +1,530 @@
|
||||
# Pymatgen Analysis Modules Reference
|
||||
|
||||
This reference documents pymatgen's extensive analysis capabilities for materials characterization, property prediction, and computational analysis.
|
||||
|
||||
## Phase Diagrams and Thermodynamics
|
||||
|
||||
### Phase Diagram Construction
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram, PDPlotter
|
||||
from pymatgen.entries.computed_entries import ComputedEntry
|
||||
|
||||
# Create entries (composition and energy per atom)
|
||||
entries = [
|
||||
ComputedEntry("Fe", -8.4),
|
||||
ComputedEntry("O2", -4.9),
|
||||
ComputedEntry("FeO", -6.7),
|
||||
ComputedEntry("Fe2O3", -8.3),
|
||||
ComputedEntry("Fe3O4", -9.1),
|
||||
]
|
||||
|
||||
# Build phase diagram
|
||||
pd = PhaseDiagram(entries)
|
||||
|
||||
# Get stable entries
|
||||
stable_entries = pd.stable_entries
|
||||
|
||||
# Get energy above hull (stability)
|
||||
entry_to_test = ComputedEntry("Fe2O3", -8.0)
|
||||
energy_above_hull = pd.get_e_above_hull(entry_to_test)
|
||||
|
||||
# Get decomposition products
|
||||
decomp = pd.get_decomposition(entry_to_test.composition)
|
||||
# Returns: {entry1: fraction1, entry2: fraction2, ...}
|
||||
|
||||
# Get equilibrium reaction energy
|
||||
rxn_energy = pd.get_equilibrium_reaction_energy(entry_to_test)
|
||||
|
||||
# Plot phase diagram
|
||||
plotter = PDPlotter(pd)
|
||||
plotter.show()
|
||||
plotter.write_image("phase_diagram.png")
|
||||
```
|
||||
|
||||
### Chemical Potential Diagrams
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.phase_diagram import ChemicalPotentialDiagram
|
||||
|
||||
# Create chemical potential diagram
|
||||
cpd = ChemicalPotentialDiagram(entries, limits={"O": (-10, 0)})
|
||||
|
||||
# Get domains (stability regions)
|
||||
domains = cpd.domains
|
||||
```
|
||||
|
||||
### Pourbaix Diagrams
|
||||
|
||||
Electrochemical phase diagrams with pH and potential axes.
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.pourbaix_diagram import PourbaixDiagram, PourbaixPlotter
|
||||
from pymatgen.entries.computed_entries import ComputedEntry
|
||||
|
||||
# Create entries with corrections for aqueous species
|
||||
entries = [...] # Include solids and ions
|
||||
|
||||
# Build Pourbaix diagram
|
||||
pb = PourbaixDiagram(entries)
|
||||
|
||||
# Get stable entry at specific pH and potential
|
||||
stable_entry = pb.get_stable_entry(pH=7, V=0)
|
||||
|
||||
# Plot
|
||||
plotter = PourbaixPlotter(pb)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
## Structure Analysis
|
||||
|
||||
### Structure Matching and Comparison
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.structure_matcher import StructureMatcher
|
||||
|
||||
matcher = StructureMatcher()
|
||||
|
||||
# Check if structures match
|
||||
is_match = matcher.fit(struct1, struct2)
|
||||
|
||||
# Get mapping between structures
|
||||
mapping = matcher.get_mapping(struct1, struct2)
|
||||
|
||||
# Group similar structures
|
||||
grouped = matcher.group_structures([struct1, struct2, struct3, ...])
|
||||
```
|
||||
|
||||
### Ewald Summation
|
||||
|
||||
Calculate electrostatic energy of ionic structures.
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.ewald import EwaldSummation
|
||||
|
||||
ewald = EwaldSummation(struct)
|
||||
total_energy = ewald.total_energy # In eV
|
||||
forces = ewald.forces # Forces on each site
|
||||
```
|
||||
|
||||
### Symmetry Analysis
|
||||
|
||||
```python
|
||||
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
||||
|
||||
sga = SpacegroupAnalyzer(struct)
|
||||
|
||||
# Get space group information
|
||||
spacegroup_symbol = sga.get_space_group_symbol() # e.g., "Fm-3m"
|
||||
spacegroup_number = sga.get_space_group_number() # e.g., 225
|
||||
crystal_system = sga.get_crystal_system() # e.g., "cubic"
|
||||
|
||||
# Get symmetrized structure
|
||||
sym_struct = sga.get_symmetrized_structure()
|
||||
equivalent_sites = sym_struct.equivalent_sites
|
||||
|
||||
# Get conventional/primitive cells
|
||||
conventional = sga.get_conventional_standard_structure()
|
||||
primitive = sga.get_primitive_standard_structure()
|
||||
|
||||
# Get symmetry operations
|
||||
symmetry_ops = sga.get_symmetry_operations()
|
||||
```
|
||||
|
||||
## Local Environment Analysis
|
||||
|
||||
### Coordination Environment
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.local_env import (
|
||||
VoronoiNN, # Voronoi tessellation
|
||||
CrystalNN, # Crystal-based
|
||||
MinimumDistanceNN, # Distance cutoff
|
||||
BrunnerNN_real, # Brunner method
|
||||
)
|
||||
|
||||
# Voronoi nearest neighbors
|
||||
voronoi = VoronoiNN()
|
||||
neighbors = voronoi.get_nn_info(struct, n=0) # Neighbors of site 0
|
||||
|
||||
# CrystalNN (recommended for most cases)
|
||||
crystalnn = CrystalNN()
|
||||
neighbors = crystalnn.get_nn_info(struct, n=0)
|
||||
|
||||
# Analyze all sites
|
||||
for i, site in enumerate(struct):
|
||||
neighbors = voronoi.get_nn_info(struct, i)
|
||||
coordination_number = len(neighbors)
|
||||
print(f"Site {i} ({site.species_string}): CN = {coordination_number}")
|
||||
```
|
||||
|
||||
### Coordination Geometry (ChemEnv)
|
||||
|
||||
Detailed coordination environment identification.
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import LocalGeometryFinder
|
||||
from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import SimplestChemenvStrategy
|
||||
|
||||
lgf = LocalGeometryFinder()
|
||||
lgf.setup_structure(struct)
|
||||
|
||||
# Get coordination environment for site
|
||||
se = lgf.compute_structure_environments(only_indices=[0])
|
||||
strategy = SimplestChemenvStrategy()
|
||||
lse = strategy.get_site_coordination_environment(se[0])
|
||||
|
||||
print(f"Coordination: {lse}")
|
||||
```
|
||||
|
||||
### Bond Valence Sum
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.bond_valence import BVAnalyzer
|
||||
|
||||
bva = BVAnalyzer()
|
||||
|
||||
# Calculate oxidation states
|
||||
valences = bva.get_valences(struct)
|
||||
|
||||
# Get structure with oxidation states
|
||||
struct_with_oxi = bva.get_oxi_state_decorated_structure(struct)
|
||||
```
|
||||
|
||||
## Surface and Interface Analysis
|
||||
|
||||
### Surface (Slab) Generation
|
||||
|
||||
```python
|
||||
from pymatgen.core.surface import SlabGenerator, generate_all_slabs
|
||||
|
||||
# Generate slabs for a specific Miller index
|
||||
slabgen = SlabGenerator(
|
||||
struct,
|
||||
miller_index=(1, 1, 1),
|
||||
min_slab_size=10.0, # Minimum slab thickness (Å)
|
||||
min_vacuum_size=10.0, # Minimum vacuum thickness (Å)
|
||||
center_slab=True
|
||||
)
|
||||
|
||||
slabs = slabgen.get_slabs()
|
||||
|
||||
# Generate all slabs up to a Miller index
|
||||
all_slabs = generate_all_slabs(
|
||||
struct,
|
||||
max_index=2,
|
||||
min_slab_size=10.0,
|
||||
min_vacuum_size=10.0
|
||||
)
|
||||
```
|
||||
|
||||
### Wulff Shape Construction
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.wulff import WulffShape
|
||||
|
||||
# Define surface energies (J/m²)
|
||||
surface_energies = {
|
||||
(1, 0, 0): 1.0,
|
||||
(1, 1, 0): 1.1,
|
||||
(1, 1, 1): 0.9,
|
||||
}
|
||||
|
||||
wulff = WulffShape(struct.lattice, surface_energies, symm_reduce=True)
|
||||
|
||||
# Get effective radius and surface area
|
||||
effective_radius = wulff.effective_radius
|
||||
surface_area = wulff.surface_area
|
||||
volume = wulff.volume
|
||||
|
||||
# Visualize
|
||||
wulff.show()
|
||||
```
|
||||
|
||||
### Adsorption Site Finding
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.adsorption import AdsorbateSiteFinder
|
||||
|
||||
asf = AdsorbateSiteFinder(slab)
|
||||
|
||||
# Find adsorption sites
|
||||
ads_sites = asf.find_adsorption_sites()
|
||||
# Returns dictionary: {"ontop": [...], "bridge": [...], "hollow": [...]}
|
||||
|
||||
# Generate structures with adsorbates
|
||||
from pymatgen.core import Molecule
|
||||
adsorbate = Molecule("O", [[0, 0, 0]])
|
||||
|
||||
ads_structs = asf.generate_adsorption_structures(
|
||||
adsorbate,
|
||||
repeat=[2, 2, 1], # Supercell to reduce adsorbate coverage
|
||||
)
|
||||
```
|
||||
|
||||
### Interface Construction
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.interfaces.coherent_interfaces import CoherentInterfaceBuilder
|
||||
|
||||
# Build interface between two materials
|
||||
builder = CoherentInterfaceBuilder(
|
||||
substrate_structure=substrate,
|
||||
film_structure=film,
|
||||
substrate_miller=(0, 0, 1),
|
||||
film_miller=(1, 1, 1),
|
||||
)
|
||||
|
||||
interfaces = builder.get_interfaces()
|
||||
```
|
||||
|
||||
## Magnetism
|
||||
|
||||
### Magnetic Structure Analysis
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.magnetism import CollinearMagneticStructureAnalyzer
|
||||
|
||||
analyzer = CollinearMagneticStructureAnalyzer(struct)
|
||||
|
||||
# Get magnetic ordering
|
||||
ordering = analyzer.ordering # e.g., "FM" (ferromagnetic), "AFM", "FiM"
|
||||
|
||||
# Get magnetic space group
|
||||
mag_space_group = analyzer.get_structure_with_spin().get_space_group_info()
|
||||
```
|
||||
|
||||
### Magnetic Ordering Enumeration
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import MagOrderingTransformation
|
||||
|
||||
# Enumerate possible magnetic orderings
|
||||
mag_trans = MagOrderingTransformation({"Fe": 5.0}) # Magnetic moment in μB
|
||||
transformed_structures = mag_trans.apply_transformation(struct, return_ranked_list=True)
|
||||
```
|
||||
|
||||
## Electronic Structure Analysis
|
||||
|
||||
### Band Structure Analysis
|
||||
|
||||
```python
|
||||
from pymatgen.electronic_structure.bandstructure import BandStructureSymmLine
|
||||
from pymatgen.electronic_structure.plotter import BSPlotter
|
||||
|
||||
# Read band structure from VASP calculation
|
||||
from pymatgen.io.vasp import Vasprun
|
||||
vasprun = Vasprun("vasprun.xml")
|
||||
bs = vasprun.get_band_structure()
|
||||
|
||||
# Get band gap
|
||||
band_gap = bs.get_band_gap()
|
||||
# Returns: {'energy': gap_value, 'direct': True/False, 'transition': '...'}
|
||||
|
||||
# Check if metal
|
||||
is_metal = bs.is_metal()
|
||||
|
||||
# Get VBM and CBM
|
||||
vbm = bs.get_vbm()
|
||||
cbm = bs.get_cbm()
|
||||
|
||||
# Plot band structure
|
||||
plotter = BSPlotter(bs)
|
||||
plotter.show()
|
||||
plotter.save_plot("band_structure.png")
|
||||
```
|
||||
|
||||
### Density of States (DOS)
|
||||
|
||||
```python
|
||||
from pymatgen.electronic_structure.dos import CompleteDos
|
||||
from pymatgen.electronic_structure.plotter import DosPlotter
|
||||
|
||||
# Read DOS from VASP calculation
|
||||
vasprun = Vasprun("vasprun.xml")
|
||||
dos = vasprun.complete_dos
|
||||
|
||||
# Get total DOS
|
||||
total_dos = dos.densities
|
||||
|
||||
# Get projected DOS
|
||||
pdos = dos.get_element_dos() # By element
|
||||
site_dos = dos.get_site_dos(struct[0]) # For specific site
|
||||
spd_dos = dos.get_spd_dos() # By orbital (s, p, d)
|
||||
|
||||
# Plot DOS
|
||||
plotter = DosPlotter()
|
||||
plotter.add_dos("Total", dos)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
### Fermi Surface
|
||||
|
||||
```python
|
||||
from pymatgen.electronic_structure.boltztrap2 import BoltztrapRunner
|
||||
|
||||
runner = BoltztrapRunner(struct, nelec=n_electrons)
|
||||
runner.run()
|
||||
|
||||
# Get transport properties at different temperatures
|
||||
results = runner.get_results()
|
||||
```
|
||||
|
||||
## Diffraction
|
||||
|
||||
### X-ray Diffraction (XRD)
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.diffraction.xrd import XRDCalculator
|
||||
|
||||
xrd = XRDCalculator()
|
||||
|
||||
pattern = xrd.get_pattern(struct, two_theta_range=(0, 90))
|
||||
|
||||
# Get peak data
|
||||
for peak in pattern.hkls:
|
||||
print(f"2θ = {peak['2theta']:.2f}°, hkl = {peak['hkl']}, I = {peak['intensity']:.1f}")
|
||||
|
||||
# Plot pattern
|
||||
pattern.plot()
|
||||
```
|
||||
|
||||
### Neutron Diffraction
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.diffraction.neutron import NDCalculator
|
||||
|
||||
nd = NDCalculator()
|
||||
pattern = nd.get_pattern(struct)
|
||||
```
|
||||
|
||||
## Elasticity and Mechanical Properties
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.elasticity import ElasticTensor, Stress, Strain
|
||||
|
||||
# Create elastic tensor from matrix
|
||||
elastic_tensor = ElasticTensor([[...]]) # 6x6 or 3x3x3x3 matrix
|
||||
|
||||
# Get mechanical properties
|
||||
bulk_modulus = elastic_tensor.k_voigt # Voigt bulk modulus (GPa)
|
||||
shear_modulus = elastic_tensor.g_voigt # Shear modulus (GPa)
|
||||
youngs_modulus = elastic_tensor.y_mod # Young's modulus (GPa)
|
||||
|
||||
# Apply strain
|
||||
strain = Strain([[0.01, 0, 0], [0, 0, 0], [0, 0, 0]])
|
||||
stress = elastic_tensor.calculate_stress(strain)
|
||||
```
|
||||
|
||||
## Reaction Analysis
|
||||
|
||||
### Reaction Computation
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.reaction_calculator import ComputedReaction
|
||||
|
||||
reactants = [ComputedEntry("Fe", -8.4), ComputedEntry("O2", -4.9)]
|
||||
products = [ComputedEntry("Fe2O3", -8.3)]
|
||||
|
||||
rxn = ComputedReaction(reactants, products)
|
||||
|
||||
# Get balanced equation
|
||||
balanced_rxn = rxn.normalized_repr # e.g., "2 Fe + 1.5 O2 -> Fe2O3"
|
||||
|
||||
# Get reaction energy
|
||||
energy = rxn.calculated_reaction_energy # eV per formula unit
|
||||
```
|
||||
|
||||
### Reaction Path Finding
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.path_finder import ChgcarPotential, NEBPathfinder
|
||||
|
||||
# Read charge density
|
||||
chgcar_potential = ChgcarPotential.from_file("CHGCAR")
|
||||
|
||||
# Find diffusion path
|
||||
neb_path = NEBPathfinder(
|
||||
start_struct,
|
||||
end_struct,
|
||||
relax_sites=[i for i in range(len(start_struct))],
|
||||
v=chgcar_potential
|
||||
)
|
||||
|
||||
images = neb_path.images # Interpolated structures for NEB
|
||||
```
|
||||
|
||||
## Molecular Analysis
|
||||
|
||||
### Bond Analysis
|
||||
|
||||
```python
|
||||
# Get covalent bonds
|
||||
bonds = mol.get_covalent_bonds()
|
||||
|
||||
for bond in bonds:
|
||||
print(f"{bond.site1.species_string} - {bond.site2.species_string}: {bond.length:.2f} Å")
|
||||
```
|
||||
|
||||
### Molecule Graph
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.graphs import MoleculeGraph
|
||||
from pymatgen.analysis.local_env import OpenBabelNN
|
||||
|
||||
# Build molecule graph
|
||||
mg = MoleculeGraph.with_local_env_strategy(mol, OpenBabelNN())
|
||||
|
||||
# Get fragments
|
||||
fragments = mg.get_disconnected_fragments()
|
||||
|
||||
# Find rings
|
||||
rings = mg.find_rings()
|
||||
```
|
||||
|
||||
## Spectroscopy
|
||||
|
||||
### X-ray Absorption Spectroscopy (XAS)
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.xas.spectrum import XAS
|
||||
|
||||
# Read XAS spectrum
|
||||
xas = XAS.from_file("xas.dat")
|
||||
|
||||
# Normalize and process
|
||||
xas.normalize()
|
||||
```
|
||||
|
||||
## Additional Analysis Tools
|
||||
|
||||
### Grain Boundaries
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.gb.grain import GrainBoundaryGenerator
|
||||
|
||||
gb_gen = GrainBoundaryGenerator(struct)
|
||||
gb_structures = gb_gen.generate_grain_boundaries(
|
||||
rotation_axis=[0, 0, 1],
|
||||
rotation_angle=36.87, # degrees
|
||||
)
|
||||
```
|
||||
|
||||
### Prototypes and Structure Matching
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.prototypes import AflowPrototypeMatcher
|
||||
|
||||
matcher = AflowPrototypeMatcher()
|
||||
prototype = matcher.get_prototypes(struct)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start simple**: Use basic analysis before advanced methods
|
||||
2. **Validate results**: Cross-check analysis with multiple methods
|
||||
3. **Consider symmetry**: Use `SpacegroupAnalyzer` to reduce computational cost
|
||||
4. **Check convergence**: Ensure input structures are well-converged
|
||||
5. **Use appropriate methods**: Different analyses have different accuracy/speed tradeoffs
|
||||
6. **Visualize results**: Use built-in plotters for quick validation
|
||||
7. **Save intermediate results**: Complex analyses can be time-consuming
|
||||
318
skills/pymatgen/references/core_classes.md
Normal file
318
skills/pymatgen/references/core_classes.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# Pymatgen Core Classes Reference
|
||||
|
||||
This reference documents the fundamental classes in `pymatgen.core` that form the foundation for materials analysis.
|
||||
|
||||
## Architecture Principles
|
||||
|
||||
Pymatgen follows an object-oriented design where elements, sites, and structures are represented as objects. The framework emphasizes periodic boundary conditions for crystal representation while maintaining flexibility for molecular systems.
|
||||
|
||||
**Unit Conventions**: All units in pymatgen are typically assumed to be in atomic units:
|
||||
- Lengths: angstroms (Å)
|
||||
- Energies: electronvolts (eV)
|
||||
- Angles: degrees
|
||||
|
||||
## Element and Periodic Table
|
||||
|
||||
### Element
|
||||
Represents periodic table elements with comprehensive properties.
|
||||
|
||||
**Creation methods:**
|
||||
```python
|
||||
from pymatgen.core import Element
|
||||
|
||||
# Create from symbol
|
||||
si = Element("Si")
|
||||
# Create from atomic number
|
||||
si = Element.from_Z(14)
|
||||
# Create from name
|
||||
si = Element.from_name("silicon")
|
||||
```
|
||||
|
||||
**Key properties:**
|
||||
- `atomic_mass`: Atomic mass in amu
|
||||
- `atomic_radius`: Atomic radius in angstroms
|
||||
- `electronegativity`: Pauling electronegativity
|
||||
- `ionization_energy`: First ionization energy in eV
|
||||
- `common_oxidation_states`: List of common oxidation states
|
||||
- `is_metal`, `is_halogen`, `is_noble_gas`, etc.: Boolean properties
|
||||
- `X`: Element symbol as string
|
||||
|
||||
### Species
|
||||
Extends Element for charged ions and specific oxidation states.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Species
|
||||
|
||||
# Create an Fe2+ ion
|
||||
fe2 = Species("Fe", 2)
|
||||
# Or with explicit sign
|
||||
fe2 = Species("Fe", +2)
|
||||
```
|
||||
|
||||
### DummySpecies
|
||||
Placeholder atoms for special structural representations (e.g., vacancies).
|
||||
|
||||
```python
|
||||
from pymatgen.core import DummySpecies
|
||||
|
||||
vacancy = DummySpecies("X")
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
Represents chemical formulas and compositions, enabling chemical analysis and manipulation.
|
||||
|
||||
### Creation
|
||||
```python
|
||||
from pymatgen.core import Composition
|
||||
|
||||
# From string formula
|
||||
comp = Composition("Fe2O3")
|
||||
# From dictionary
|
||||
comp = Composition({"Fe": 2, "O": 3})
|
||||
# From weight dictionary
|
||||
comp = Composition.from_weight_dict({"Fe": 111.69, "O": 48.00})
|
||||
```
|
||||
|
||||
### Key methods
|
||||
- `get_reduced_formula_and_factor()`: Returns reduced formula and multiplication factor
|
||||
- `oxi_state_guesses()`: Attempts to determine oxidation states
|
||||
- `replace(replacements_dict)`: Replace elements
|
||||
- `add_charges_from_oxi_state_guesses()`: Infer and add oxidation states
|
||||
- `is_element`: Check if composition is a single element
|
||||
|
||||
### Key properties
|
||||
- `weight`: Molecular weight
|
||||
- `reduced_formula`: Reduced chemical formula
|
||||
- `hill_formula`: Formula in Hill notation (C, H, then alphabetical)
|
||||
- `num_atoms`: Total number of atoms
|
||||
- `chemical_system`: Alphabetically sorted elements (e.g., "Fe-O")
|
||||
- `element_composition`: Dictionary of element to amount
|
||||
|
||||
## Lattice
|
||||
|
||||
Defines unit cell geometry for crystal structures.
|
||||
|
||||
### Creation
|
||||
```python
|
||||
from pymatgen.core import Lattice
|
||||
|
||||
# From lattice parameters
|
||||
lattice = Lattice.from_parameters(a=3.84, b=3.84, c=3.84,
|
||||
alpha=120, beta=90, gamma=60)
|
||||
|
||||
# From matrix (row vectors are lattice vectors)
|
||||
lattice = Lattice([[3.84, 0, 0],
|
||||
[0, 3.84, 0],
|
||||
[0, 0, 3.84]])
|
||||
|
||||
# Cubic lattice
|
||||
lattice = Lattice.cubic(3.84)
|
||||
# Hexagonal lattice
|
||||
lattice = Lattice.hexagonal(a=2.95, c=4.68)
|
||||
```
|
||||
|
||||
### Key methods
|
||||
- `get_niggli_reduced_lattice()`: Returns Niggli-reduced lattice
|
||||
- `get_distance_and_image(frac_coords1, frac_coords2)`: Distance between fractional coordinates with periodic boundary conditions
|
||||
- `get_all_distances(frac_coords1, frac_coords2)`: Distances including periodic images
|
||||
|
||||
### Key properties
|
||||
- `volume`: Volume of the unit cell (ų)
|
||||
- `abc`: Lattice parameters (a, b, c) as tuple
|
||||
- `angles`: Lattice angles (alpha, beta, gamma) as tuple
|
||||
- `matrix`: 3x3 matrix of lattice vectors
|
||||
- `reciprocal_lattice`: Reciprocal lattice object
|
||||
- `is_orthogonal`: Whether lattice vectors are orthogonal
|
||||
|
||||
## Sites
|
||||
|
||||
### Site
|
||||
Represents an atomic position in non-periodic systems.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Site
|
||||
|
||||
site = Site("Si", [0.0, 0.0, 0.0]) # Species and Cartesian coordinates
|
||||
```
|
||||
|
||||
### PeriodicSite
|
||||
Represents an atomic position in a periodic lattice with fractional coordinates.
|
||||
|
||||
```python
|
||||
from pymatgen.core import PeriodicSite
|
||||
|
||||
site = PeriodicSite("Si", [0.5, 0.5, 0.5], lattice) # Species, fractional coords, lattice
|
||||
```
|
||||
|
||||
**Key methods:**
|
||||
- `distance(other_site)`: Distance to another site
|
||||
- `is_periodic_image(other_site)`: Check if sites are periodic images
|
||||
|
||||
**Key properties:**
|
||||
- `species`: Species or element at the site
|
||||
- `coords`: Cartesian coordinates
|
||||
- `frac_coords`: Fractional coordinates (for PeriodicSite)
|
||||
- `x`, `y`, `z`: Individual Cartesian coordinates
|
||||
|
||||
## Structure
|
||||
|
||||
Represents a crystal structure as a collection of periodic sites. `Structure` is mutable, while `IStructure` is immutable.
|
||||
|
||||
### Creation
|
||||
```python
|
||||
from pymatgen.core import Structure, Lattice
|
||||
|
||||
# From scratch
|
||||
coords = [[0, 0, 0], [0.75, 0.5, 0.75]]
|
||||
lattice = Lattice.from_parameters(a=3.84, b=3.84, c=3.84,
|
||||
alpha=120, beta=90, gamma=60)
|
||||
struct = Structure(lattice, ["Si", "Si"], coords)
|
||||
|
||||
# From file (automatic format detection)
|
||||
struct = Structure.from_file("POSCAR")
|
||||
struct = Structure.from_file("structure.cif")
|
||||
|
||||
# From spacegroup
|
||||
struct = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.5),
|
||||
["Si"], [[0, 0, 0]])
|
||||
```
|
||||
|
||||
### File I/O
|
||||
```python
|
||||
# Write to file (format inferred from extension)
|
||||
struct.to(filename="output.cif")
|
||||
struct.to(filename="POSCAR")
|
||||
struct.to(filename="structure.xyz")
|
||||
|
||||
# Get string representation
|
||||
cif_string = struct.to(fmt="cif")
|
||||
poscar_string = struct.to(fmt="poscar")
|
||||
```
|
||||
|
||||
### Key methods
|
||||
|
||||
**Structure modification:**
|
||||
- `append(species, coords)`: Add a site
|
||||
- `insert(i, species, coords)`: Insert site at index
|
||||
- `remove_sites(indices)`: Remove sites by index
|
||||
- `replace(i, species)`: Replace species at index
|
||||
- `apply_strain(strain)`: Apply strain to structure
|
||||
- `perturb(distance)`: Randomly perturb atomic positions
|
||||
- `make_supercell(scaling_matrix)`: Create supercell
|
||||
- `get_primitive_structure()`: Get primitive cell
|
||||
|
||||
**Analysis:**
|
||||
- `get_distance(i, j)`: Distance between sites i and j
|
||||
- `get_neighbors(site, r)`: Get neighbors within radius r
|
||||
- `get_all_neighbors(r)`: Get all neighbors for all sites
|
||||
- `get_space_group_info()`: Get space group information
|
||||
- `matches(other_struct)`: Check if structures match
|
||||
|
||||
**Interpolation:**
|
||||
- `interpolate(end_structure, nimages)`: Interpolate between structures
|
||||
|
||||
### Key properties
|
||||
- `lattice`: Lattice object
|
||||
- `species`: List of species at each site
|
||||
- `sites`: List of PeriodicSite objects
|
||||
- `num_sites`: Number of sites
|
||||
- `volume`: Volume of the structure
|
||||
- `density`: Density in g/cm³
|
||||
- `composition`: Composition object
|
||||
- `formula`: Chemical formula
|
||||
- `distance_matrix`: Matrix of pairwise distances
|
||||
|
||||
## Molecule
|
||||
|
||||
Represents non-periodic collections of atoms. `Molecule` is mutable, while `IMolecule` is immutable.
|
||||
|
||||
### Creation
|
||||
```python
|
||||
from pymatgen.core import Molecule
|
||||
|
||||
# From scratch
|
||||
coords = [[0.00, 0.00, 0.00],
|
||||
[0.00, 0.00, 1.08]]
|
||||
mol = Molecule(["C", "O"], coords)
|
||||
|
||||
# From file
|
||||
mol = Molecule.from_file("molecule.xyz")
|
||||
mol = Molecule.from_file("molecule.mol")
|
||||
```
|
||||
|
||||
### Key methods
|
||||
- `get_covalent_bonds()`: Returns bonds based on covalent radii
|
||||
- `get_neighbors(site, r)`: Get neighbors within radius
|
||||
- `get_zmatrix()`: Get Z-matrix representation
|
||||
- `get_distance(i, j)`: Distance between sites
|
||||
- `get_centered_molecule()`: Center molecule at origin
|
||||
|
||||
### Key properties
|
||||
- `species`: List of species
|
||||
- `sites`: List of Site objects
|
||||
- `num_sites`: Number of atoms
|
||||
- `charge`: Total charge of molecule
|
||||
- `spin_multiplicity`: Spin multiplicity
|
||||
- `center_of_mass`: Center of mass coordinates
|
||||
|
||||
## Serialization
|
||||
|
||||
All core objects implement `as_dict()` and `from_dict()` methods for robust JSON/YAML persistence.
|
||||
|
||||
```python
|
||||
# Serialize to dictionary
|
||||
struct_dict = struct.as_dict()
|
||||
|
||||
# Write to JSON
|
||||
import json
|
||||
with open("structure.json", "w") as f:
|
||||
json.dump(struct_dict, f)
|
||||
|
||||
# Read from JSON
|
||||
with open("structure.json", "r") as f:
|
||||
struct_dict = json.load(f)
|
||||
struct = Structure.from_dict(struct_dict)
|
||||
```
|
||||
|
||||
This approach addresses limitations of Python pickling and maintains compatibility across pymatgen versions.
|
||||
|
||||
## Additional Core Classes
|
||||
|
||||
### CovalentBond
|
||||
Represents bonds in molecules.
|
||||
|
||||
**Key properties:**
|
||||
- `length`: Bond length
|
||||
- `get_bond_order()`: Returns bond order (single, double, triple)
|
||||
|
||||
### Ion
|
||||
Represents charged ionic species with oxidation states.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Ion
|
||||
|
||||
# Create Fe2+ ion
|
||||
fe2_ion = Ion.from_formula("Fe2+")
|
||||
```
|
||||
|
||||
### Interface
|
||||
Represents substrate-film combinations for heterojunction analysis.
|
||||
|
||||
### GrainBoundary
|
||||
Represents crystallographic grain boundaries.
|
||||
|
||||
### Spectrum
|
||||
Represents spectroscopic data with methods for normalization and processing.
|
||||
|
||||
**Key methods:**
|
||||
- `normalize(mode="max")`: Normalize spectrum
|
||||
- `smear(sigma)`: Apply Gaussian smearing
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Immutability**: Use immutable versions (`IStructure`, `IMolecule`) when structures shouldn't be modified
|
||||
2. **Serialization**: Prefer `as_dict()`/`from_dict()` over pickle for long-term storage
|
||||
3. **Units**: Always work in atomic units (Å, eV) - conversions are available in `pymatgen.core.units`
|
||||
4. **File I/O**: Use `from_file()` for automatic format detection
|
||||
5. **Coordinates**: Pay attention to whether methods expect Cartesian or fractional coordinates
|
||||
469
skills/pymatgen/references/io_formats.md
Normal file
469
skills/pymatgen/references/io_formats.md
Normal file
@@ -0,0 +1,469 @@
|
||||
# Pymatgen I/O and File Format Reference
|
||||
|
||||
This reference documents pymatgen's extensive input/output capabilities for reading and writing structural and computational data across 100+ file formats.
|
||||
|
||||
## General I/O Philosophy
|
||||
|
||||
Pymatgen provides a unified interface for file operations through the `from_file()` and `to()` methods, with automatic format detection based on file extensions.
|
||||
|
||||
### Reading Files
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure, Molecule
|
||||
|
||||
# Automatic format detection
|
||||
struct = Structure.from_file("POSCAR")
|
||||
struct = Structure.from_file("structure.cif")
|
||||
mol = Molecule.from_file("molecule.xyz")
|
||||
|
||||
# Explicit format specification
|
||||
struct = Structure.from_file("file.txt", fmt="cif")
|
||||
```
|
||||
|
||||
### Writing Files
|
||||
|
||||
```python
|
||||
# Write to file (format inferred from extension)
|
||||
struct.to(filename="output.cif")
|
||||
struct.to(filename="POSCAR")
|
||||
struct.to(filename="structure.xyz")
|
||||
|
||||
# Get string representation without writing
|
||||
cif_string = struct.to(fmt="cif")
|
||||
poscar_string = struct.to(fmt="poscar")
|
||||
```
|
||||
|
||||
## Structure File Formats
|
||||
|
||||
### CIF (Crystallographic Information File)
|
||||
Standard format for crystallographic data.
|
||||
|
||||
```python
|
||||
from pymatgen.io.cif import CifParser, CifWriter
|
||||
|
||||
# Reading
|
||||
parser = CifParser("structure.cif")
|
||||
structure = parser.get_structures()[0] # Returns list of structures
|
||||
|
||||
# Writing
|
||||
writer = CifWriter(struct)
|
||||
writer.write_file("output.cif")
|
||||
|
||||
# Or using convenience methods
|
||||
struct = Structure.from_file("structure.cif")
|
||||
struct.to(filename="output.cif")
|
||||
```
|
||||
|
||||
**Key features:**
|
||||
- Supports symmetry information
|
||||
- Can contain multiple structures
|
||||
- Preserves space group and symmetry operations
|
||||
- Handles partial occupancies
|
||||
|
||||
### POSCAR/CONTCAR (VASP)
|
||||
VASP's structure format.
|
||||
|
||||
```python
|
||||
from pymatgen.io.vasp import Poscar
|
||||
|
||||
# Reading
|
||||
poscar = Poscar.from_file("POSCAR")
|
||||
structure = poscar.structure
|
||||
|
||||
# Writing
|
||||
poscar = Poscar(struct)
|
||||
poscar.write_file("POSCAR")
|
||||
|
||||
# Or using convenience methods
|
||||
struct = Structure.from_file("POSCAR")
|
||||
struct.to(filename="POSCAR")
|
||||
```
|
||||
|
||||
**Key features:**
|
||||
- Supports selective dynamics
|
||||
- Can include velocities (XDATCAR format)
|
||||
- Preserves lattice and coordinate precision
|
||||
|
||||
### XYZ
|
||||
Simple molecular coordinates format.
|
||||
|
||||
```python
|
||||
# For molecules
|
||||
mol = Molecule.from_file("molecule.xyz")
|
||||
mol.to(filename="output.xyz")
|
||||
|
||||
# For structures (Cartesian coordinates)
|
||||
struct.to(filename="structure.xyz")
|
||||
```
|
||||
|
||||
### PDB (Protein Data Bank)
|
||||
Common format for biomolecules.
|
||||
|
||||
```python
|
||||
mol = Molecule.from_file("protein.pdb")
|
||||
mol.to(filename="output.pdb")
|
||||
```
|
||||
|
||||
### JSON/YAML
|
||||
Serialization via dictionaries.
|
||||
|
||||
```python
|
||||
import json
|
||||
import yaml
|
||||
|
||||
# JSON
|
||||
with open("structure.json", "w") as f:
|
||||
json.dump(struct.as_dict(), f)
|
||||
|
||||
with open("structure.json", "r") as f:
|
||||
struct = Structure.from_dict(json.load(f))
|
||||
|
||||
# YAML
|
||||
with open("structure.yaml", "w") as f:
|
||||
yaml.dump(struct.as_dict(), f)
|
||||
|
||||
with open("structure.yaml", "r") as f:
|
||||
struct = Structure.from_dict(yaml.safe_load(f))
|
||||
```
|
||||
|
||||
## Electronic Structure Code I/O
|
||||
|
||||
### VASP
|
||||
|
||||
The most comprehensive integration in pymatgen.
|
||||
|
||||
#### Input Files
|
||||
|
||||
```python
|
||||
from pymatgen.io.vasp.inputs import Incar, Poscar, Potcar, Kpoints, VaspInput
|
||||
|
||||
# INCAR (calculation parameters)
|
||||
incar = Incar.from_file("INCAR")
|
||||
incar = Incar({"ENCUT": 520, "ISMEAR": 0, "SIGMA": 0.05})
|
||||
incar.write_file("INCAR")
|
||||
|
||||
# KPOINTS (k-point mesh)
|
||||
from pymatgen.io.vasp.inputs import Kpoints
|
||||
kpoints = Kpoints.automatic(20) # 20x20x20 Gamma-centered mesh
|
||||
kpoints = Kpoints.automatic_density(struct, 1000) # By density
|
||||
kpoints.write_file("KPOINTS")
|
||||
|
||||
# POTCAR (pseudopotentials)
|
||||
potcar = Potcar(["Fe_pv", "O"]) # Specify functional variants
|
||||
|
||||
# Complete input set
|
||||
vasp_input = VaspInput(incar, kpoints, poscar, potcar)
|
||||
vasp_input.write_input("./vasp_calc")
|
||||
```
|
||||
|
||||
#### Output Files
|
||||
|
||||
```python
|
||||
from pymatgen.io.vasp.outputs import Vasprun, Outcar, Oszicar, Eigenval
|
||||
|
||||
# vasprun.xml (comprehensive output)
|
||||
vasprun = Vasprun("vasprun.xml")
|
||||
final_structure = vasprun.final_structure
|
||||
energy = vasprun.final_energy
|
||||
band_structure = vasprun.get_band_structure()
|
||||
dos = vasprun.complete_dos
|
||||
|
||||
# OUTCAR
|
||||
outcar = Outcar("OUTCAR")
|
||||
magnetization = outcar.total_mag
|
||||
elastic_tensor = outcar.elastic_tensor
|
||||
|
||||
# OSZICAR (convergence information)
|
||||
oszicar = Oszicar("OSZICAR")
|
||||
```
|
||||
|
||||
#### Input Sets
|
||||
|
||||
Pymatgen provides pre-configured input sets for common calculations:
|
||||
|
||||
```python
|
||||
from pymatgen.io.vasp.sets import (
|
||||
MPRelaxSet, # Materials Project relaxation
|
||||
MPStaticSet, # Static calculation
|
||||
MPNonSCFSet, # Non-self-consistent (band structure)
|
||||
MPSOCSet, # Spin-orbit coupling
|
||||
MPHSERelaxSet, # HSE06 hybrid functional
|
||||
)
|
||||
|
||||
# Create input set
|
||||
relax = MPRelaxSet(struct)
|
||||
relax.write_input("./relax_calc")
|
||||
|
||||
# Customize parameters
|
||||
static = MPStaticSet(struct, user_incar_settings={"ENCUT": 600})
|
||||
static.write_input("./static_calc")
|
||||
```
|
||||
|
||||
### Gaussian
|
||||
|
||||
Quantum chemistry package integration.
|
||||
|
||||
```python
|
||||
from pymatgen.io.gaussian import GaussianInput, GaussianOutput
|
||||
|
||||
# Input
|
||||
gin = GaussianInput(
|
||||
mol,
|
||||
charge=0,
|
||||
spin_multiplicity=1,
|
||||
functional="B3LYP",
|
||||
basis_set="6-31G(d)",
|
||||
route_parameters={"Opt": None, "Freq": None}
|
||||
)
|
||||
gin.write_file("input.gjf")
|
||||
|
||||
# Output
|
||||
gout = GaussianOutput("output.log")
|
||||
final_mol = gout.final_structure
|
||||
energy = gout.final_energy
|
||||
frequencies = gout.frequencies
|
||||
```
|
||||
|
||||
### LAMMPS
|
||||
|
||||
Classical molecular dynamics.
|
||||
|
||||
```python
|
||||
from pymatgen.io.lammps.data import LammpsData
|
||||
from pymatgen.io.lammps.inputs import LammpsInputFile
|
||||
|
||||
# Structure to LAMMPS data file
|
||||
lammps_data = LammpsData.from_structure(struct)
|
||||
lammps_data.write_file("data.lammps")
|
||||
|
||||
# LAMMPS input script
|
||||
lammps_input = LammpsInputFile.from_file("in.lammps")
|
||||
```
|
||||
|
||||
### Quantum ESPRESSO
|
||||
|
||||
```python
|
||||
from pymatgen.io.pwscf import PWInput, PWOutput
|
||||
|
||||
# Input
|
||||
pwin = PWInput(
|
||||
struct,
|
||||
control={"calculation": "scf"},
|
||||
system={"ecutwfc": 50, "ecutrho": 400},
|
||||
electrons={"conv_thr": 1e-8}
|
||||
)
|
||||
pwin.write_file("pw.in")
|
||||
|
||||
# Output
|
||||
pwout = PWOutput("pw.out")
|
||||
final_structure = pwout.final_structure
|
||||
energy = pwout.final_energy
|
||||
```
|
||||
|
||||
### ABINIT
|
||||
|
||||
```python
|
||||
from pymatgen.io.abinit import AbinitInput
|
||||
|
||||
abin = AbinitInput(struct, pseudos)
|
||||
abin.set_vars(ecut=10, nband=10)
|
||||
abin.write("abinit.in")
|
||||
```
|
||||
|
||||
### CP2K
|
||||
|
||||
```python
|
||||
from pymatgen.io.cp2k.inputs import Cp2kInput
|
||||
from pymatgen.io.cp2k.outputs import Cp2kOutput
|
||||
|
||||
# Input
|
||||
cp2k_input = Cp2kInput.from_file("cp2k.inp")
|
||||
|
||||
# Output
|
||||
cp2k_output = Cp2kOutput("cp2k.out")
|
||||
```
|
||||
|
||||
### FEFF (XAS/XANES)
|
||||
|
||||
```python
|
||||
from pymatgen.io.feff import FeffInput
|
||||
|
||||
feff_input = FeffInput(struct, absorbing_atom="Fe")
|
||||
feff_input.write_file("feff.inp")
|
||||
```
|
||||
|
||||
### LMTO (Stuttgart TB-LMTO-ASA)
|
||||
|
||||
```python
|
||||
from pymatgen.io.lmto import LMTOCtrl
|
||||
|
||||
ctrl = LMTOCtrl.from_file("CTRL")
|
||||
ctrl.structure
|
||||
```
|
||||
|
||||
### Q-Chem
|
||||
|
||||
```python
|
||||
from pymatgen.io.qchem.inputs import QCInput
|
||||
from pymatgen.io.qchem.outputs import QCOutput
|
||||
|
||||
# Input
|
||||
qc_input = QCInput(
|
||||
mol,
|
||||
rem={"method": "B3LYP", "basis": "6-31G*", "job_type": "opt"}
|
||||
)
|
||||
qc_input.write_file("mol.qin")
|
||||
|
||||
# Output
|
||||
qc_output = QCOutput("mol.qout")
|
||||
```
|
||||
|
||||
### Exciting
|
||||
|
||||
```python
|
||||
from pymatgen.io.exciting import ExcitingInput
|
||||
|
||||
exc_input = ExcitingInput(struct)
|
||||
exc_input.write_file("input.xml")
|
||||
```
|
||||
|
||||
### ATAT (Alloy Theoretic Automated Toolkit)
|
||||
|
||||
```python
|
||||
from pymatgen.io.atat import Mcsqs
|
||||
|
||||
mcsqs = Mcsqs(struct)
|
||||
mcsqs.write_input(".")
|
||||
```
|
||||
|
||||
## Special Purpose Formats
|
||||
|
||||
### Phonopy
|
||||
|
||||
```python
|
||||
from pymatgen.io.phonopy import get_phonopy_structure, get_pmg_structure
|
||||
|
||||
# Convert to phonopy structure
|
||||
phonopy_struct = get_phonopy_structure(struct)
|
||||
|
||||
# Convert from phonopy
|
||||
struct = get_pmg_structure(phonopy_struct)
|
||||
```
|
||||
|
||||
### ASE (Atomic Simulation Environment)
|
||||
|
||||
```python
|
||||
from pymatgen.io.ase import AseAtomsAdaptor
|
||||
|
||||
adaptor = AseAtomsAdaptor()
|
||||
|
||||
# Pymatgen to ASE
|
||||
atoms = adaptor.get_atoms(struct)
|
||||
|
||||
# ASE to Pymatgen
|
||||
struct = adaptor.get_structure(atoms)
|
||||
```
|
||||
|
||||
### Zeo++ (Porous Materials)
|
||||
|
||||
```python
|
||||
from pymatgen.io.zeopp import get_voronoi_nodes, get_high_accuracy_voronoi_nodes
|
||||
|
||||
# Analyze pore structure
|
||||
vor_nodes = get_voronoi_nodes(struct)
|
||||
```
|
||||
|
||||
### BabelMolAdaptor (OpenBabel)
|
||||
|
||||
```python
|
||||
from pymatgen.io.babel import BabelMolAdaptor
|
||||
|
||||
adaptor = BabelMolAdaptor(mol)
|
||||
|
||||
# Convert to different formats
|
||||
pdb_str = adaptor.pdbstring
|
||||
sdf_str = adaptor.write_file("mol.sdf", file_format="sdf")
|
||||
|
||||
# Generate 3D coordinates
|
||||
adaptor.add_hydrogen()
|
||||
adaptor.make3d()
|
||||
```
|
||||
|
||||
## Alchemy and Transformation I/O
|
||||
|
||||
### TransformedStructure
|
||||
|
||||
Structures that track their transformation history.
|
||||
|
||||
```python
|
||||
from pymatgen.alchemy.materials import TransformedStructure
|
||||
from pymatgen.transformations.standard_transformations import (
|
||||
SupercellTransformation,
|
||||
SubstitutionTransformation
|
||||
)
|
||||
|
||||
# Create transformed structure
|
||||
ts = TransformedStructure(struct, [])
|
||||
ts.append_transformation(SupercellTransformation([[2,0,0],[0,2,0],[0,0,2]]))
|
||||
ts.append_transformation(SubstitutionTransformation({"Fe": "Mn"}))
|
||||
|
||||
# Write with history
|
||||
ts.write_vasp_input("./calc_dir")
|
||||
|
||||
# Read from SNL (Structure Notebook Language)
|
||||
ts = TransformedStructure.from_snl(snl)
|
||||
```
|
||||
|
||||
## Batch Operations
|
||||
|
||||
### CifTransmuter
|
||||
|
||||
Process multiple CIF files.
|
||||
|
||||
```python
|
||||
from pymatgen.alchemy.transmuters import CifTransmuter
|
||||
|
||||
transmuter = CifTransmuter.from_filenames(
|
||||
["structure1.cif", "structure2.cif"],
|
||||
[SupercellTransformation([[2,0,0],[0,2,0],[0,0,2]])]
|
||||
)
|
||||
|
||||
# Write all structures
|
||||
transmuter.write_vasp_input("./batch_calc")
|
||||
```
|
||||
|
||||
### PoscarTransmuter
|
||||
|
||||
Similar for POSCAR files.
|
||||
|
||||
```python
|
||||
from pymatgen.alchemy.transmuters import PoscarTransmuter
|
||||
|
||||
transmuter = PoscarTransmuter.from_filenames(
|
||||
["POSCAR1", "POSCAR2"],
|
||||
[transformation1, transformation2]
|
||||
)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Automatic format detection**: Use `from_file()` and `to()` methods whenever possible
|
||||
2. **Error handling**: Always wrap file I/O in try-except blocks
|
||||
3. **Format-specific parsers**: Use specialized parsers (e.g., `Vasprun`) for detailed output analysis
|
||||
4. **Input sets**: Prefer pre-configured input sets over manual parameter specification
|
||||
5. **Serialization**: Use JSON/YAML for long-term storage and version control
|
||||
6. **Batch processing**: Use transmuters for applying transformations to multiple structures
|
||||
|
||||
## Supported Format Summary
|
||||
|
||||
### Structure formats:
|
||||
CIF, POSCAR/CONTCAR, XYZ, PDB, XSF, PWMAT, Res, CSSR, JSON, YAML
|
||||
|
||||
### Electronic structure codes:
|
||||
VASP, Gaussian, LAMMPS, Quantum ESPRESSO, ABINIT, CP2K, FEFF, Q-Chem, LMTO, Exciting, NWChem, AIMS, Crystallographic data formats
|
||||
|
||||
### Molecular formats:
|
||||
XYZ, PDB, MOL, SDF, PQR, via OpenBabel (many additional formats)
|
||||
|
||||
### Special purpose:
|
||||
Phonopy, ASE, Zeo++, Lobster, BoltzTraP
|
||||
517
skills/pymatgen/references/materials_project_api.md
Normal file
517
skills/pymatgen/references/materials_project_api.md
Normal file
@@ -0,0 +1,517 @@
|
||||
# Materials Project API Reference
|
||||
|
||||
This reference documents how to access and use the Materials Project database through pymatgen's API integration.
|
||||
|
||||
## Overview
|
||||
|
||||
The Materials Project is a comprehensive database of computed materials properties, containing data on hundreds of thousands of inorganic crystals and molecules. The API provides programmatic access to this data through the `MPRester` client.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
The Materials Project API client is now in a separate package:
|
||||
|
||||
```bash
|
||||
pip install mp-api
|
||||
```
|
||||
|
||||
### Getting an API Key
|
||||
|
||||
1. Visit https://next-gen.materialsproject.org/
|
||||
2. Create an account or log in
|
||||
3. Navigate to your dashboard/settings
|
||||
4. Generate an API key
|
||||
5. Store it as an environment variable:
|
||||
|
||||
```bash
|
||||
export MP_API_KEY="your_api_key_here"
|
||||
```
|
||||
|
||||
Or add to your shell configuration file (~/.bashrc, ~/.zshrc, etc.)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Initialization
|
||||
|
||||
```python
|
||||
from mp_api.client import MPRester
|
||||
|
||||
# Using environment variable (recommended)
|
||||
with MPRester() as mpr:
|
||||
# Perform queries
|
||||
pass
|
||||
|
||||
# Or explicitly pass API key
|
||||
with MPRester("your_api_key_here") as mpr:
|
||||
# Perform queries
|
||||
pass
|
||||
```
|
||||
|
||||
**Important**: Always use the `with` context manager to ensure sessions are properly closed.
|
||||
|
||||
## Querying Materials Data
|
||||
|
||||
### Search by Formula
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get all materials with formula
|
||||
materials = mpr.materials.summary.search(formula="Fe2O3")
|
||||
|
||||
for mat in materials:
|
||||
print(f"Material ID: {mat.material_id}")
|
||||
print(f"Formula: {mat.formula_pretty}")
|
||||
print(f"Energy above hull: {mat.energy_above_hull} eV/atom")
|
||||
print(f"Band gap: {mat.band_gap} eV")
|
||||
print()
|
||||
```
|
||||
|
||||
### Search by Material ID
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get specific material
|
||||
material = mpr.materials.summary.search(material_ids=["mp-149"])[0]
|
||||
|
||||
print(f"Formula: {material.formula_pretty}")
|
||||
print(f"Space group: {material.symmetry.symbol}")
|
||||
print(f"Density: {material.density} g/cm³")
|
||||
```
|
||||
|
||||
### Search by Chemical System
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get all materials in Fe-O system
|
||||
materials = mpr.materials.summary.search(chemsys="Fe-O")
|
||||
|
||||
# Get materials in ternary system
|
||||
materials = mpr.materials.summary.search(chemsys="Li-Fe-O")
|
||||
```
|
||||
|
||||
### Search by Elements
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Materials containing Fe and O
|
||||
materials = mpr.materials.summary.search(elements=["Fe", "O"])
|
||||
|
||||
# Materials containing ONLY Fe and O (excluding others)
|
||||
materials = mpr.materials.summary.search(
|
||||
elements=["Fe", "O"],
|
||||
exclude_elements=True
|
||||
)
|
||||
```
|
||||
|
||||
## Getting Structures
|
||||
|
||||
### Structure from Material ID
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get structure
|
||||
structure = mpr.get_structure_by_material_id("mp-149")
|
||||
|
||||
# Get multiple structures
|
||||
structures = mpr.get_structures(["mp-149", "mp-510", "mp-19017"])
|
||||
```
|
||||
|
||||
### All Structures for a Formula
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get all Fe2O3 structures
|
||||
materials = mpr.materials.summary.search(formula="Fe2O3")
|
||||
|
||||
for mat in materials:
|
||||
structure = mpr.get_structure_by_material_id(mat.material_id)
|
||||
print(f"{mat.material_id}: {structure.get_space_group_info()}")
|
||||
```
|
||||
|
||||
## Advanced Queries
|
||||
|
||||
### Property Filtering
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Materials with specific property ranges
|
||||
materials = mpr.materials.summary.search(
|
||||
chemsys="Li-Fe-O",
|
||||
energy_above_hull=(0, 0.05), # Stable or near-stable
|
||||
band_gap=(1.0, 3.0), # Semiconducting
|
||||
)
|
||||
|
||||
# Magnetic materials
|
||||
materials = mpr.materials.summary.search(
|
||||
elements=["Fe"],
|
||||
is_magnetic=True
|
||||
)
|
||||
|
||||
# Metals only
|
||||
materials = mpr.materials.summary.search(
|
||||
chemsys="Fe-Ni",
|
||||
is_metal=True
|
||||
)
|
||||
```
|
||||
|
||||
### Sorting and Limiting
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get most stable materials
|
||||
materials = mpr.materials.summary.search(
|
||||
chemsys="Li-Fe-O",
|
||||
sort_fields=["energy_above_hull"],
|
||||
num_chunks=1,
|
||||
chunk_size=10 # Limit to 10 results
|
||||
)
|
||||
```
|
||||
|
||||
## Electronic Structure Data
|
||||
|
||||
### Band Structure
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get band structure
|
||||
bs = mpr.get_bandstructure_by_material_id("mp-149")
|
||||
|
||||
# Analyze band structure
|
||||
if bs:
|
||||
print(f"Band gap: {bs.get_band_gap()}")
|
||||
print(f"Is metal: {bs.is_metal()}")
|
||||
print(f"Direct gap: {bs.get_band_gap()['direct']}")
|
||||
|
||||
# Plot
|
||||
from pymatgen.electronic_structure.plotter import BSPlotter
|
||||
plotter = BSPlotter(bs)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
### Density of States
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get DOS
|
||||
dos = mpr.get_dos_by_material_id("mp-149")
|
||||
|
||||
if dos:
|
||||
# Get band gap from DOS
|
||||
gap = dos.get_gap()
|
||||
print(f"Band gap from DOS: {gap} eV")
|
||||
|
||||
# Plot DOS
|
||||
from pymatgen.electronic_structure.plotter import DosPlotter
|
||||
plotter = DosPlotter()
|
||||
plotter.add_dos("Total DOS", dos)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
### Fermi Surface
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get electronic structure data for Fermi surface
|
||||
bs = mpr.get_bandstructure_by_material_id("mp-149", line_mode=False)
|
||||
```
|
||||
|
||||
## Thermodynamic Data
|
||||
|
||||
### Phase Diagram Construction
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram, PDPlotter
|
||||
|
||||
with MPRester() as mpr:
|
||||
# Get entries for phase diagram
|
||||
entries = mpr.get_entries_in_chemsys("Li-Fe-O")
|
||||
|
||||
# Build phase diagram
|
||||
pd = PhaseDiagram(entries)
|
||||
|
||||
# Plot
|
||||
plotter = PDPlotter(pd)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
### Pourbaix Diagram
|
||||
|
||||
```python
|
||||
from pymatgen.analysis.pourbaix_diagram import PourbaixDiagram, PourbaixPlotter
|
||||
|
||||
with MPRester() as mpr:
|
||||
# Get entries for Pourbaix diagram
|
||||
entries = mpr.get_pourbaix_entries(["Fe"])
|
||||
|
||||
# Build Pourbaix diagram
|
||||
pb = PourbaixDiagram(entries)
|
||||
|
||||
# Plot
|
||||
plotter = PourbaixPlotter(pb)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
### Formation Energy
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
materials = mpr.materials.summary.search(material_ids=["mp-149"])
|
||||
|
||||
for mat in materials:
|
||||
print(f"Formation energy: {mat.formation_energy_per_atom} eV/atom")
|
||||
print(f"Energy above hull: {mat.energy_above_hull} eV/atom")
|
||||
```
|
||||
|
||||
## Elasticity and Mechanical Properties
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Search for materials with elastic data
|
||||
materials = mpr.materials.elasticity.search(
|
||||
chemsys="Fe-O",
|
||||
bulk_modulus_vrh=(100, 300) # GPa
|
||||
)
|
||||
|
||||
for mat in materials:
|
||||
print(f"{mat.material_id}: K = {mat.bulk_modulus_vrh} GPa")
|
||||
```
|
||||
|
||||
## Dielectric Properties
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get dielectric data
|
||||
materials = mpr.materials.dielectric.search(
|
||||
material_ids=["mp-149"]
|
||||
)
|
||||
|
||||
for mat in materials:
|
||||
print(f"Dielectric constant: {mat.e_electronic}")
|
||||
print(f"Refractive index: {mat.n}")
|
||||
```
|
||||
|
||||
## Piezoelectric Properties
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get piezoelectric materials
|
||||
materials = mpr.materials.piezoelectric.search(
|
||||
piezoelectric_modulus=(1, 100)
|
||||
)
|
||||
```
|
||||
|
||||
## Surface Properties
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get surface data
|
||||
surfaces = mpr.materials.surface_properties.search(
|
||||
material_ids=["mp-149"]
|
||||
)
|
||||
```
|
||||
|
||||
## Molecule Data (For Molecular Materials)
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Search molecules
|
||||
molecules = mpr.molecules.summary.search(
|
||||
formula="H2O"
|
||||
)
|
||||
|
||||
for mol in molecules:
|
||||
print(f"Molecule ID: {mol.molecule_id}")
|
||||
print(f"Formula: {mol.formula_pretty}")
|
||||
```
|
||||
|
||||
## Bulk Data Download
|
||||
|
||||
### Download All Data for Materials
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get comprehensive data
|
||||
materials = mpr.materials.summary.search(
|
||||
material_ids=["mp-149"],
|
||||
fields=[
|
||||
"material_id",
|
||||
"formula_pretty",
|
||||
"structure",
|
||||
"energy_above_hull",
|
||||
"band_gap",
|
||||
"density",
|
||||
"symmetry",
|
||||
"elasticity",
|
||||
"magnetic_ordering"
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Provenance and Calculation Details
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get calculation details
|
||||
materials = mpr.materials.summary.search(
|
||||
material_ids=["mp-149"],
|
||||
fields=["material_id", "origins"]
|
||||
)
|
||||
|
||||
for mat in materials:
|
||||
print(f"Origins: {mat.origins}")
|
||||
```
|
||||
|
||||
## Working with Entries
|
||||
|
||||
### ComputedEntry for Thermodynamic Analysis
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get entries (includes energy and composition)
|
||||
entries = mpr.get_entries_in_chemsys("Li-Fe-O")
|
||||
|
||||
# Entries can be used directly in phase diagram analysis
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram
|
||||
pd = PhaseDiagram(entries)
|
||||
|
||||
# Check stability
|
||||
for entry in entries[:5]:
|
||||
e_above_hull = pd.get_e_above_hull(entry)
|
||||
print(f"{entry.composition.reduced_formula}: {e_above_hull:.3f} eV/atom")
|
||||
```
|
||||
|
||||
## Rate Limiting and Best Practices
|
||||
|
||||
### Rate Limits
|
||||
|
||||
The Materials Project API has rate limits to ensure fair usage:
|
||||
- Be mindful of request frequency
|
||||
- Use batch queries when possible
|
||||
- Cache results locally for repeated analysis
|
||||
|
||||
### Efficient Querying
|
||||
|
||||
```python
|
||||
# Bad: Multiple separate queries
|
||||
with MPRester() as mpr:
|
||||
for mp_id in ["mp-149", "mp-510", "mp-19017"]:
|
||||
struct = mpr.get_structure_by_material_id(mp_id) # 3 API calls
|
||||
|
||||
# Good: Single batch query
|
||||
with MPRester() as mpr:
|
||||
structs = mpr.get_structures(["mp-149", "mp-510", "mp-19017"]) # 1 API call
|
||||
```
|
||||
|
||||
### Caching Results
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
# Save results for later use
|
||||
with MPRester() as mpr:
|
||||
materials = mpr.materials.summary.search(chemsys="Li-Fe-O")
|
||||
|
||||
# Save to file
|
||||
with open("li_fe_o_materials.json", "w") as f:
|
||||
json.dump([mat.dict() for mat in materials], f)
|
||||
|
||||
# Load cached results
|
||||
with open("li_fe_o_materials.json", "r") as f:
|
||||
cached_data = json.load(f)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
from mp_api.client.core.client import MPRestError
|
||||
|
||||
try:
|
||||
with MPRester() as mpr:
|
||||
materials = mpr.materials.summary.search(material_ids=["invalid-id"])
|
||||
except MPRestError as e:
|
||||
print(f"API Error: {e}")
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}")
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Finding Stable Compounds
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get all stable compounds in a chemical system
|
||||
materials = mpr.materials.summary.search(
|
||||
chemsys="Li-Fe-O",
|
||||
energy_above_hull=(0, 0.001) # Essentially on convex hull
|
||||
)
|
||||
|
||||
print(f"Found {len(materials)} stable compounds")
|
||||
for mat in materials:
|
||||
print(f" {mat.formula_pretty} ({mat.material_id})")
|
||||
```
|
||||
|
||||
### Battery Material Screening
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Screen for potential cathode materials
|
||||
materials = mpr.materials.summary.search(
|
||||
elements=["Li"], # Must contain Li
|
||||
energy_above_hull=(0, 0.05), # Near stable
|
||||
band_gap=(0, 0.5), # Metallic or small gap
|
||||
)
|
||||
|
||||
print(f"Found {len(materials)} potential cathode materials")
|
||||
```
|
||||
|
||||
### Finding Materials with Specific Crystal Structure
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Find materials with specific space group
|
||||
materials = mpr.materials.summary.search(
|
||||
chemsys="Fe-O",
|
||||
spacegroup_number=167 # R-3c (corundum structure)
|
||||
)
|
||||
```
|
||||
|
||||
## Integration with Other Pymatgen Features
|
||||
|
||||
All data retrieved from the Materials Project can be directly used with pymatgen's analysis tools:
|
||||
|
||||
```python
|
||||
with MPRester() as mpr:
|
||||
# Get structure
|
||||
struct = mpr.get_structure_by_material_id("mp-149")
|
||||
|
||||
# Use with pymatgen analysis
|
||||
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
|
||||
sga = SpacegroupAnalyzer(struct)
|
||||
|
||||
# Generate surfaces
|
||||
from pymatgen.core.surface import SlabGenerator
|
||||
slabgen = SlabGenerator(struct, (1,0,0), 10, 10)
|
||||
slabs = slabgen.get_slabs()
|
||||
|
||||
# Phase diagram analysis
|
||||
entries = mpr.get_entries_in_chemsys(struct.composition.chemical_system)
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram
|
||||
pd = PhaseDiagram(entries)
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **API Documentation**: https://docs.materialsproject.org/
|
||||
- **Materials Project Website**: https://next-gen.materialsproject.org/
|
||||
- **GitHub**: https://github.com/materialsproject/api
|
||||
- **Forum**: https://matsci.org/
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Always use context manager**: Use `with MPRester() as mpr:`
|
||||
2. **Store API key as environment variable**: Never hardcode API keys
|
||||
3. **Batch queries**: Request multiple items at once when possible
|
||||
4. **Cache results**: Save frequently used data locally
|
||||
5. **Handle errors**: Wrap API calls in try-except blocks
|
||||
6. **Be specific**: Use filters to limit results and reduce data transfer
|
||||
7. **Check data availability**: Not all properties are available for all materials
|
||||
591
skills/pymatgen/references/transformations_workflows.md
Normal file
591
skills/pymatgen/references/transformations_workflows.md
Normal file
@@ -0,0 +1,591 @@
|
||||
# Pymatgen Transformations and Common Workflows
|
||||
|
||||
This reference documents pymatgen's transformation framework and provides recipes for common materials science workflows.
|
||||
|
||||
## Transformation Framework
|
||||
|
||||
Transformations provide a systematic way to modify structures while tracking the history of modifications.
|
||||
|
||||
### Standard Transformations
|
||||
|
||||
Located in `pymatgen.transformations.standard_transformations`.
|
||||
|
||||
#### SupercellTransformation
|
||||
|
||||
Create supercells with arbitrary scaling matrices.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import SupercellTransformation
|
||||
|
||||
# Simple 2x2x2 supercell
|
||||
trans = SupercellTransformation([[2,0,0], [0,2,0], [0,0,2]])
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
|
||||
# Non-orthogonal supercell
|
||||
trans = SupercellTransformation([[2,1,0], [0,2,0], [0,0,2]])
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### SubstitutionTransformation
|
||||
|
||||
Replace species in a structure.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import SubstitutionTransformation
|
||||
|
||||
# Replace all Fe with Mn
|
||||
trans = SubstitutionTransformation({"Fe": "Mn"})
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
|
||||
# Partial substitution (50% Fe -> Mn)
|
||||
trans = SubstitutionTransformation({"Fe": {"Mn": 0.5, "Fe": 0.5}})
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### RemoveSpeciesTransformation
|
||||
|
||||
Remove specific species from structure.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import RemoveSpeciesTransformation
|
||||
|
||||
trans = RemoveSpeciesTransformation(["H"]) # Remove all hydrogen
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### OrderDisorderedStructureTransformation
|
||||
|
||||
Order disordered structures with partial occupancies.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import OrderDisorderedStructureTransformation
|
||||
|
||||
trans = OrderDisorderedStructureTransformation()
|
||||
new_struct = trans.apply_transformation(disordered_struct)
|
||||
```
|
||||
|
||||
#### PrimitiveCellTransformation
|
||||
|
||||
Convert to primitive cell.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import PrimitiveCellTransformation
|
||||
|
||||
trans = PrimitiveCellTransformation()
|
||||
primitive_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### ConventionalCellTransformation
|
||||
|
||||
Convert to conventional cell.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import ConventionalCellTransformation
|
||||
|
||||
trans = ConventionalCellTransformation()
|
||||
conventional_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### RotationTransformation
|
||||
|
||||
Rotate structure.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import RotationTransformation
|
||||
|
||||
# Rotate by axis and angle
|
||||
trans = RotationTransformation([0, 0, 1], 45) # 45° around z-axis
|
||||
new_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### ScaleToRelaxedTransformation
|
||||
|
||||
Scale lattice to match a relaxed structure.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.standard_transformations import ScaleToRelaxedTransformation
|
||||
|
||||
trans = ScaleToRelaxedTransformation(relaxed_struct)
|
||||
scaled_struct = trans.apply_transformation(unrelaxed_struct)
|
||||
```
|
||||
|
||||
### Advanced Transformations
|
||||
|
||||
Located in `pymatgen.transformations.advanced_transformations`.
|
||||
|
||||
#### EnumerateStructureTransformation
|
||||
|
||||
Enumerate all symmetrically distinct ordered structures from a disordered structure.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import EnumerateStructureTransformation
|
||||
|
||||
# Enumerate structures up to max 8 atoms per unit cell
|
||||
trans = EnumerateStructureTransformation(max_cell_size=8)
|
||||
structures = trans.apply_transformation(struct, return_ranked_list=True)
|
||||
|
||||
# Returns list of ranked structures
|
||||
for s in structures[:5]: # Top 5 structures
|
||||
print(f"Energy: {s['energy']}, Structure: {s['structure']}")
|
||||
```
|
||||
|
||||
#### MagOrderingTransformation
|
||||
|
||||
Enumerate magnetic orderings.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import MagOrderingTransformation
|
||||
|
||||
# Specify magnetic moments for each species
|
||||
trans = MagOrderingTransformation({"Fe": 5.0, "Ni": 2.0})
|
||||
mag_structures = trans.apply_transformation(struct, return_ranked_list=True)
|
||||
```
|
||||
|
||||
#### DopingTransformation
|
||||
|
||||
Systematically dope a structure.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import DopingTransformation
|
||||
|
||||
# Replace 12.5% of Fe sites with Mn
|
||||
trans = DopingTransformation("Mn", min_length=10)
|
||||
doped_structs = trans.apply_transformation(struct, return_ranked_list=True)
|
||||
```
|
||||
|
||||
#### ChargeBalanceTransformation
|
||||
|
||||
Balance charge in a structure by oxidation state manipulation.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import ChargeBalanceTransformation
|
||||
|
||||
trans = ChargeBalanceTransformation("Li")
|
||||
charged_struct = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
#### SlabTransformation
|
||||
|
||||
Generate surface slabs.
|
||||
|
||||
```python
|
||||
from pymatgen.transformations.advanced_transformations import SlabTransformation
|
||||
|
||||
trans = SlabTransformation(
|
||||
miller_index=[1, 0, 0],
|
||||
min_slab_size=10,
|
||||
min_vacuum_size=10,
|
||||
shift=0,
|
||||
lll_reduce=True
|
||||
)
|
||||
slab = trans.apply_transformation(struct)
|
||||
```
|
||||
|
||||
### Chaining Transformations
|
||||
|
||||
```python
|
||||
from pymatgen.alchemy.materials import TransformedStructure
|
||||
|
||||
# Create transformed structure that tracks history
|
||||
ts = TransformedStructure(struct, [])
|
||||
|
||||
# Apply multiple transformations
|
||||
ts.append_transformation(SupercellTransformation([[2,0,0],[0,2,0],[0,0,2]]))
|
||||
ts.append_transformation(SubstitutionTransformation({"Fe": "Mn"}))
|
||||
ts.append_transformation(PrimitiveCellTransformation())
|
||||
|
||||
# Get final structure
|
||||
final_struct = ts.final_structure
|
||||
|
||||
# View transformation history
|
||||
print(ts.history)
|
||||
```
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Workflow 1: High-Throughput Structure Generation
|
||||
|
||||
Generate multiple structures for screening studies.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure
|
||||
from pymatgen.transformations.standard_transformations import (
|
||||
SubstitutionTransformation,
|
||||
SupercellTransformation
|
||||
)
|
||||
from pymatgen.io.vasp.sets import MPRelaxSet
|
||||
|
||||
# Starting structure
|
||||
base_struct = Structure.from_file("POSCAR")
|
||||
|
||||
# Define substitutions
|
||||
dopants = ["Mn", "Co", "Ni", "Cu"]
|
||||
structures = {}
|
||||
|
||||
for dopant in dopants:
|
||||
# Create substituted structure
|
||||
trans = SubstitutionTransformation({"Fe": dopant})
|
||||
new_struct = trans.apply_transformation(base_struct)
|
||||
|
||||
# Generate VASP inputs
|
||||
vasp_input = MPRelaxSet(new_struct)
|
||||
vasp_input.write_input(f"./calcs/Fe_{dopant}")
|
||||
|
||||
structures[dopant] = new_struct
|
||||
|
||||
print(f"Generated {len(structures)} structures")
|
||||
```
|
||||
|
||||
### Workflow 2: Phase Diagram Construction
|
||||
|
||||
Build and analyze phase diagrams from Materials Project data.
|
||||
|
||||
```python
|
||||
from mp_api.client import MPRester
|
||||
from pymatgen.analysis.phase_diagram import PhaseDiagram, PDPlotter
|
||||
from pymatgen.core import Composition
|
||||
|
||||
# Get data from Materials Project
|
||||
with MPRester() as mpr:
|
||||
entries = mpr.get_entries_in_chemsys("Li-Fe-O")
|
||||
|
||||
# Build phase diagram
|
||||
pd = PhaseDiagram(entries)
|
||||
|
||||
# Analyze specific composition
|
||||
comp = Composition("LiFeO2")
|
||||
e_above_hull = pd.get_e_above_hull(entries[0])
|
||||
|
||||
# Get decomposition products
|
||||
decomp = pd.get_decomposition(comp)
|
||||
print(f"Decomposition: {decomp}")
|
||||
|
||||
# Visualize
|
||||
plotter = PDPlotter(pd)
|
||||
plotter.show()
|
||||
```
|
||||
|
||||
### Workflow 3: Surface Energy Calculation
|
||||
|
||||
Calculate surface energies from slab calculations.
|
||||
|
||||
```python
|
||||
from pymatgen.core.surface import SlabGenerator, generate_all_slabs
|
||||
from pymatgen.io.vasp.sets import MPStaticSet, MPRelaxSet
|
||||
from pymatgen.core import Structure
|
||||
|
||||
# Read bulk structure
|
||||
bulk = Structure.from_file("bulk_POSCAR")
|
||||
|
||||
# Get bulk energy (from previous calculation)
|
||||
from pymatgen.io.vasp import Vasprun
|
||||
bulk_vasprun = Vasprun("bulk/vasprun.xml")
|
||||
bulk_energy_per_atom = bulk_vasprun.final_energy / len(bulk)
|
||||
|
||||
# Generate slabs
|
||||
miller_indices = [(1,0,0), (1,1,0), (1,1,1)]
|
||||
surface_energies = {}
|
||||
|
||||
for miller in miller_indices:
|
||||
slabgen = SlabGenerator(
|
||||
bulk,
|
||||
miller_index=miller,
|
||||
min_slab_size=10,
|
||||
min_vacuum_size=15,
|
||||
center_slab=True
|
||||
)
|
||||
|
||||
slab = slabgen.get_slabs()[0]
|
||||
|
||||
# Write VASP input for slab
|
||||
relax = MPRelaxSet(slab)
|
||||
relax.write_input(f"./slab_{miller[0]}{miller[1]}{miller[2]}")
|
||||
|
||||
# After calculation, compute surface energy:
|
||||
# slab_vasprun = Vasprun(f"slab_{miller[0]}{miller[1]}{miller[2]}/vasprun.xml")
|
||||
# slab_energy = slab_vasprun.final_energy
|
||||
# n_atoms = len(slab)
|
||||
# area = slab.surface_area # in Ų
|
||||
#
|
||||
# # Surface energy (J/m²)
|
||||
# surf_energy = (slab_energy - n_atoms * bulk_energy_per_atom) / (2 * area)
|
||||
# surf_energy *= 16.021766 # Convert eV/Ų to J/m²
|
||||
# surface_energies[miller] = surf_energy
|
||||
|
||||
print(f"Set up calculations for {len(miller_indices)} surfaces")
|
||||
```
|
||||
|
||||
### Workflow 4: Band Structure Calculation
|
||||
|
||||
Complete workflow for band structure calculations.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure
|
||||
from pymatgen.io.vasp.sets import MPRelaxSet, MPStaticSet, MPNonSCFSet
|
||||
from pymatgen.symmetry.bandstructure import HighSymmKpath
|
||||
|
||||
# Step 1: Relaxation
|
||||
struct = Structure.from_file("initial_POSCAR")
|
||||
relax = MPRelaxSet(struct)
|
||||
relax.write_input("./1_relax")
|
||||
|
||||
# After relaxation, read structure
|
||||
relaxed_struct = Structure.from_file("1_relax/CONTCAR")
|
||||
|
||||
# Step 2: Static calculation
|
||||
static = MPStaticSet(relaxed_struct)
|
||||
static.write_input("./2_static")
|
||||
|
||||
# Step 3: Band structure (non-self-consistent)
|
||||
kpath = HighSymmKpath(relaxed_struct)
|
||||
nscf = MPNonSCFSet(relaxed_struct, mode="line") # Band structure mode
|
||||
nscf.write_input("./3_bandstructure")
|
||||
|
||||
# After calculations, analyze
|
||||
from pymatgen.io.vasp import Vasprun
|
||||
from pymatgen.electronic_structure.plotter import BSPlotter
|
||||
|
||||
vasprun = Vasprun("3_bandstructure/vasprun.xml")
|
||||
bs = vasprun.get_band_structure(line_mode=True)
|
||||
|
||||
print(f"Band gap: {bs.get_band_gap()}")
|
||||
|
||||
plotter = BSPlotter(bs)
|
||||
plotter.save_plot("band_structure.png")
|
||||
```
|
||||
|
||||
### Workflow 5: Molecular Dynamics Setup
|
||||
|
||||
Set up and analyze molecular dynamics simulations.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure
|
||||
from pymatgen.io.vasp.sets import MVLRelaxSet
|
||||
from pymatgen.io.vasp.inputs import Incar
|
||||
|
||||
# Read structure
|
||||
struct = Structure.from_file("POSCAR")
|
||||
|
||||
# Create 2x2x2 supercell for MD
|
||||
from pymatgen.transformations.standard_transformations import SupercellTransformation
|
||||
trans = SupercellTransformation([[2,0,0],[0,2,0],[0,0,2]])
|
||||
supercell = trans.apply_transformation(struct)
|
||||
|
||||
# Set up VASP input
|
||||
md_input = MVLRelaxSet(supercell)
|
||||
|
||||
# Modify INCAR for MD
|
||||
incar = md_input.incar
|
||||
incar.update({
|
||||
"IBRION": 0, # Molecular dynamics
|
||||
"NSW": 1000, # Number of steps
|
||||
"POTIM": 2, # Time step (fs)
|
||||
"TEBEG": 300, # Initial temperature (K)
|
||||
"TEEND": 300, # Final temperature (K)
|
||||
"SMASS": 0, # NVT ensemble
|
||||
"MDALGO": 2, # Nose-Hoover thermostat
|
||||
})
|
||||
|
||||
md_input.incar = incar
|
||||
md_input.write_input("./md_calc")
|
||||
```
|
||||
|
||||
### Workflow 6: Diffusion Analysis
|
||||
|
||||
Analyze ion diffusion from AIMD trajectories.
|
||||
|
||||
```python
|
||||
from pymatgen.io.vasp import Xdatcar
|
||||
from pymatgen.analysis.diffusion.analyzer import DiffusionAnalyzer
|
||||
|
||||
# Read trajectory from XDATCAR
|
||||
xdatcar = Xdatcar("XDATCAR")
|
||||
structures = xdatcar.structures
|
||||
|
||||
# Analyze diffusion for specific species (e.g., Li)
|
||||
analyzer = DiffusionAnalyzer.from_structures(
|
||||
structures,
|
||||
specie="Li",
|
||||
temperature=300, # K
|
||||
time_step=2, # fs
|
||||
step_skip=10 # Skip initial equilibration
|
||||
)
|
||||
|
||||
# Get diffusivity
|
||||
diffusivity = analyzer.diffusivity # cm²/s
|
||||
conductivity = analyzer.conductivity # mS/cm
|
||||
|
||||
# Get mean squared displacement
|
||||
msd = analyzer.msd
|
||||
|
||||
# Plot MSD
|
||||
analyzer.plot_msd()
|
||||
|
||||
print(f"Diffusivity: {diffusivity:.2e} cm²/s")
|
||||
print(f"Conductivity: {conductivity:.2e} mS/cm")
|
||||
```
|
||||
|
||||
### Workflow 7: Structure Prediction and Enumeration
|
||||
|
||||
Predict and enumerate possible structures.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure, Lattice
|
||||
from pymatgen.transformations.advanced_transformations import (
|
||||
EnumerateStructureTransformation,
|
||||
SubstitutionTransformation
|
||||
)
|
||||
|
||||
# Start with a known structure type (e.g., rocksalt)
|
||||
lattice = Lattice.cubic(4.2)
|
||||
struct = Structure.from_spacegroup("Fm-3m", lattice, ["Li", "O"], [[0,0,0], [0.5,0.5,0.5]])
|
||||
|
||||
# Create disordered structure
|
||||
from pymatgen.core import Species
|
||||
species_on_site = {Species("Li"): 0.5, Species("Na"): 0.5}
|
||||
struct[0] = species_on_site # Mixed occupancy on Li site
|
||||
|
||||
# Enumerate all ordered structures
|
||||
trans = EnumerateStructureTransformation(max_cell_size=4)
|
||||
ordered_structs = trans.apply_transformation(struct, return_ranked_list=True)
|
||||
|
||||
print(f"Found {len(ordered_structs)} distinct ordered structures")
|
||||
|
||||
# Write all structures
|
||||
for i, s_dict in enumerate(ordered_structs[:10]): # Top 10
|
||||
s_dict['structure'].to(filename=f"ordered_struct_{i}.cif")
|
||||
```
|
||||
|
||||
### Workflow 8: Elastic Constant Calculation
|
||||
|
||||
Calculate elastic constants using the stress-strain method.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure
|
||||
from pymatgen.transformations.standard_transformations import DeformStructureTransformation
|
||||
from pymatgen.io.vasp.sets import MPStaticSet
|
||||
|
||||
# Read equilibrium structure
|
||||
struct = Structure.from_file("relaxed_POSCAR")
|
||||
|
||||
# Generate deformed structures
|
||||
strains = [0.00, 0.01, 0.02, -0.01, -0.02] # Applied strains
|
||||
deformation_sets = []
|
||||
|
||||
for strain in strains:
|
||||
# Apply strain in different directions
|
||||
trans = DeformStructureTransformation([[1+strain, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
deformed = trans.apply_transformation(struct)
|
||||
|
||||
# Set up VASP calculation
|
||||
static = MPStaticSet(deformed)
|
||||
static.write_input(f"./strain_{strain:.2f}")
|
||||
|
||||
# After calculations, fit stress vs strain to get elastic constants
|
||||
# from pymatgen.analysis.elasticity import ElasticTensor
|
||||
# ... (collect stress tensors from OUTCAR)
|
||||
# elastic_tensor = ElasticTensor.from_stress_list(stress_list)
|
||||
```
|
||||
|
||||
### Workflow 9: Adsorption Energy Calculation
|
||||
|
||||
Calculate adsorption energies on surfaces.
|
||||
|
||||
```python
|
||||
from pymatgen.core import Structure, Molecule
|
||||
from pymatgen.core.surface import SlabGenerator
|
||||
from pymatgen.analysis.adsorption import AdsorbateSiteFinder
|
||||
from pymatgen.io.vasp.sets import MPRelaxSet
|
||||
|
||||
# Generate slab
|
||||
bulk = Structure.from_file("bulk_POSCAR")
|
||||
slabgen = SlabGenerator(bulk, (1,1,1), 10, 10)
|
||||
slab = slabgen.get_slabs()[0]
|
||||
|
||||
# Find adsorption sites
|
||||
asf = AdsorbateSiteFinder(slab)
|
||||
ads_sites = asf.find_adsorption_sites()
|
||||
|
||||
# Create adsorbate
|
||||
adsorbate = Molecule("O", [[0, 0, 0]])
|
||||
|
||||
# Generate structures with adsorbate
|
||||
ads_structs = asf.add_adsorbate(adsorbate, ads_sites["ontop"][0])
|
||||
|
||||
# Set up calculations
|
||||
relax_slab = MPRelaxSet(slab)
|
||||
relax_slab.write_input("./slab")
|
||||
|
||||
relax_ads = MPRelaxSet(ads_structs)
|
||||
relax_ads.write_input("./slab_with_adsorbate")
|
||||
|
||||
# After calculations:
|
||||
# E_ads = E(slab+adsorbate) - E(slab) - E(adsorbate_gas)
|
||||
```
|
||||
|
||||
### Workflow 10: High-Throughput Materials Screening
|
||||
|
||||
Screen materials database for specific properties.
|
||||
|
||||
```python
|
||||
from mp_api.client import MPRester
|
||||
from pymatgen.core import Structure
|
||||
import pandas as pd
|
||||
|
||||
# Define screening criteria
|
||||
def screen_material(material):
|
||||
"""Screen for potential battery cathode materials"""
|
||||
criteria = {
|
||||
"has_li": "Li" in material.composition.elements,
|
||||
"stable": material.energy_above_hull < 0.05,
|
||||
"good_voltage": 2.5 < material.formation_energy_per_atom < 4.5,
|
||||
"electronically_conductive": material.band_gap < 0.5
|
||||
}
|
||||
return all(criteria.values()), criteria
|
||||
|
||||
# Query Materials Project
|
||||
with MPRester() as mpr:
|
||||
# Get potential materials
|
||||
materials = mpr.materials.summary.search(
|
||||
elements=["Li"],
|
||||
energy_above_hull=(0, 0.05),
|
||||
)
|
||||
|
||||
results = []
|
||||
for mat in materials:
|
||||
passes, criteria = screen_material(mat)
|
||||
if passes:
|
||||
results.append({
|
||||
"material_id": mat.material_id,
|
||||
"formula": mat.formula_pretty,
|
||||
"energy_above_hull": mat.energy_above_hull,
|
||||
"band_gap": mat.band_gap,
|
||||
})
|
||||
|
||||
# Save results
|
||||
df = pd.DataFrame(results)
|
||||
df.to_csv("screened_materials.csv", index=False)
|
||||
|
||||
print(f"Found {len(results)} promising materials")
|
||||
```
|
||||
|
||||
## Best Practices for Workflows
|
||||
|
||||
1. **Modular design**: Break workflows into discrete steps
|
||||
2. **Error handling**: Check file existence and calculation convergence
|
||||
3. **Documentation**: Track transformation history using `TransformedStructure`
|
||||
4. **Version control**: Store input parameters and scripts in git
|
||||
5. **Automation**: Use workflow managers (Fireworks, AiiDA) for complex pipelines
|
||||
6. **Data management**: Organize calculations in clear directory structures
|
||||
7. **Validation**: Always validate intermediate results before proceeding
|
||||
|
||||
## Integration with Workflow Tools
|
||||
|
||||
Pymatgen integrates with several workflow management systems:
|
||||
|
||||
- **Atomate**: Pre-built VASP workflows
|
||||
- **Fireworks**: Workflow execution engine
|
||||
- **AiiDA**: Provenance tracking and workflow management
|
||||
- **Custodian**: Error correction and job monitoring
|
||||
|
||||
These tools provide robust automation for production calculations.
|
||||
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