skills/pymatgen/references/io_formats.md
This reference targets pymatgen-core==2026.7.16, which now owns core and
electronic-structure-code I/O under the unchanged pymatgen.io namespace.
Parsing and writing are not neutral byte operations. Before any conversion, record:
Structure or non-periodic MoleculeNever overwrite the input or an existing output. Write a new artifact, round-trip it, and compare the properties the workflow depends on.
from pymatgen.core import Molecule, Structure
structure = Structure.from_file("input.cif", primitive=False, sort=False)
cif_text = structure.to(fmt="cif")
poscar_text = structure.to(fmt="poscar")
molecule = Molecule.from_file("molecule.xyz")
xyz_text = molecule.to(fmt="xyz")
Use explicit fmt when a filename or extension is ambiguous. Never assume
automatic detection means the detected interpretation was scientifically
correct.
Use the current parser method and retain all warnings:
import warnings
from pymatgen.io.cif import CifParser
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
parser = CifParser(
"input.cif",
occupancy_tolerance=1.0,
site_tolerance=1e-4,
frac_tolerance=1e-4,
check_cif=True,
comp_tol=0.01,
)
structures = parser.parse_structures(
primitive=False,
symmetrized=False,
check_occu=True,
on_error="raise",
)
parser_warnings = list(parser.warnings)
python_warnings = [str(item.message) for item in caught]
Important behavior:
site_tolerance can be merged.occupancy_tolerance; increasing that tolerance is a scientific decision,
not a generic repair.frac_tolerance can round coordinates near common fractions.check_cif compares parsed structure composition against CIF composition and
may warn about omissions such as difficult-to-locate hydrogens.parse_structures(primitive=False) is the current explicit behavior. Do not
rely on historical defaults.Writing:
from pymatgen.io.cif import CifWriter
writer = CifWriter(
structure,
symprec=None,
significant_figures=8,
write_site_properties=False,
)
cif_text = str(writer)
Setting symprec asks the writer to find symmetry and can refine to a
conventional representation depending on refine_struct; that changes the
representation. Report symprec, angle_tolerance, and refine_struct.
A critical arbitrary-code-execution vulnerability in magnetic CIF transformation parsing affected pymatgen through 2024.2.8 and was fixed in 2024.2.20. Use a current pinned release, but still parse attacker-controlled files only in a low-privilege isolated process with byte, CPU, memory, disk, site-count, and wall-time limits.
from pymatgen.io.vasp import Poscar
poscar = Poscar.from_file(
"POSCAR",
check_for_potcar=False,
read_velocities=True,
)
structure = poscar.structure
direct_text = poscar.get_str(direct=True, significant_figures=16)
cartesian_text = poscar.get_str(direct=False, significant_figures=16)
Record:
POSCAR cannot faithfully represent partial occupancies. Oxidation states and arbitrary site properties generally do not round-trip. Never "fix" a POSCAR by searching nearby directories for POTCAR files without explicit approval.
XYZ is a Cartesian, non-periodic coordinate format. Converting a periodic structure to XYZ drops lattice and periodicity. Basic XYZ also does not define oxidation states, partial occupancies, bonds, charge, spin multiplicity, or arbitrary site properties.
CSSR and XSF have their own representational limits. Treat support in
Structure.to() as syntactic capability, not proof of losslessness.
Pymatgen core objects implement as_dict()/from_dict():
import json
from pymatgen.core import Structure
text = json.dumps(structure.as_dict(), allow_nan=False, sort_keys=True)
payload = json.loads(text)
restored = Structure.from_dict(payload)
For untrusted input, use a bounded strict JSON parser, reject duplicate keys and
non-finite values, validate the expected schema, and call a specific
constructor. Do not use pickle. Do not pass attacker-controlled @module or
@class metadata to a general dynamic object decoder.
YAML is not used by the bundled CLIs. If a workflow truly needs YAML, use a safe loader plus schema validation; YAML safety does not solve object-schema or resource-exhaustion risks.
from pymatgen.io.vasp import Incar, Kpoints, Poscar
incar = Incar({"ENCUT": 520, "ISMEAR": 0, "SIGMA": 0.05})
kpoints = Kpoints.automatic_density(structure, 1000)
poscar = Poscar(structure)
Input sets encode versioned methodological choices:
from pymatgen.io.vasp.sets import MPNonSCFSet, MPRelaxSet, MPStaticSet
relax = MPRelaxSet(structure)
static = MPStaticSet(structure)
bands = MPNonSCFSet(structure, mode="line")
Before writing:
POTCAR datasets are VASP-licensed and not distributed by pymatgen. A
POTCAR.spec is not a POTCAR. Do not redistribute pseudopotential contents or
silently use files from an unrelated installation.
from pymatgen.io.vasp import Vasprun
run = Vasprun(
"vasprun.xml",
ionic_step_skip=None,
parse_dos=True,
parse_eigen=True,
parse_projected_eigen=False,
parse_potcar_file=False,
exception_on_bad_xml=True,
)
final_structure = run.final_structure
final_energy_eV = float(run.final_energy)
band_structure = run.get_band_structure(line_mode=True)
complete_dos = run.complete_dos
Use BSVasprun when only eigenvalue/band-structure information is needed.
Projected eigenvalues can take extreme time and memory; leave
parse_projected_eigen=False unless they are required and resources are
bounded.
Parser success does not establish:
Preserve source-file checksums and parsing options. Large XML, HDF5, CHGCAR, LOCPOT, WAVECAR, and trajectory files need explicit byte and memory limits.
Vasprun.get_band_structure() returns a BandStructure or
BandStructureSymmLine depending on inputs. Relevant methods include
is_metal(), get_band_gap(), get_vbm(), and get_cbm().
run.complete_dos is a CompleteDos; current analyses include total,
element-, site-, and orbital-projected DOS. Verify energy reference, Fermi
level, normalization, smearing, spin channels, projection completeness, and
whether the DOS and band run correspond to the same structure/method.
Current imports:
from pymatgen.io.qchem.inputs import QCInput
from pymatgen.io.qchem.outputs import QCOutput
job = QCInput(
molecule,
rem={
"job_type": "sp",
"method": "wb97x-v",
"basis": "def2-svpd",
},
)
text = str(job)
parsed = QCOutput("qchem.out")
data = parsed.data
QCInput accepts explicit sections such as rem, opt, pcm, solvent,
smx, scan, plots, nbo, geom_opt, and others. Validate each setting
against the licensed Q-Chem version and manual. Preserve molecule atom order,
charge, spin multiplicity, method/basis, solvent model, job type, and input
text.
QCOutput parses a file into structured data; inspect parser errors,
completion, SCF/geometry convergence, imaginary frequencies, and units before
using a result.
Pymatgen interfaces can call or depend on optional external tools, including:
enum.x, makestr.x) for derivative-structure enumerationDo not invoke them automatically. Verify official source, version, hash, license, native build scripts, executable path, exact argv, working directory, input/output paths, and CPU/RAM/disk/time bounds. Never interpolate untrusted text into a shell command.