skills/pymatgen/references/core_classes.md
This reference targets the verified pymatgen==2026.5.4 wrapper with
pymatgen-core==2026.7.16. Core objects now ship from pymatgen-core but keep
the public pymatgen.core namespace.
Pymatgen does not make every quantity "atomic units." Common contracts include:
Read the specific method contract before combining quantities. Record units in every artifact.
Structure is periodic and owns a Lattice; Molecule is non-periodic.
Structure coordinates are fractional by default. Molecule coordinates are
Cartesian. Never infer which object or coordinate mode the user intended.
from pymatgen.core import DummySpecies, Element, Species
iron = Element("Fe")
silicon = Element.from_Z(14)
oxygen = Element.from_name("oxygen")
fe2 = Species("Fe", oxidation_state=2)
vacancy_label = DummySpecies("X")
Important distinctions:
Element.symbol is the chemical symbol.Element.Z is atomic number.Element.X is Pauling electronegativity, not the symbol.Species adds oxidation state and optional properties. Oxidation state is
formal chemical annotation, not an automatically validated charge model.Current API documentation also exposes predicates and data such as
is_metal, is_noble_gas, atomic_mass, oxidation-state sets, and electronic
configuration. Check for None/missing values and retain property provenance.
Use strict parsing at external boundaries:
from pymatgen.core import Composition
composition = Composition("LiFePO4", strict=True)
formula = composition.formula
reduced = composition.reduced_formula
chemical_system = composition.chemical_system
mass_amu = float(composition.weight)
Construction from a mapping is explicit:
composition = Composition({"Fe": 2, "O": 3}, strict=True)
Safety rules:
oxi_state_guesses() is heuristic and can be combinatorial. Call it only
after explicit user approval and bound elements, formula size, runtime, and
returned guesses.Element and oxidized Species keys without intentionally
defining how charge decoration should behave.The bundled validator does not guess by default:
python scripts/composition_structure_validator.py composition "Fe2O3"
python scripts/composition_structure_validator.py composition "Fe2O3" \
--guess-oxidation-states
from pymatgen.core import Lattice
cubic = Lattice.cubic(5.64)
triclinic = Lattice.from_parameters(
a=4.0,
b=5.0,
c=6.0,
alpha=80,
beta=90,
gamma=100,
)
matrix_lattice = Lattice(
[
[4.0, 0.0, 0.0],
[0.5, 5.0, 0.0],
[0.2, 0.3, 6.0],
],
pbc=(True, True, True),
)
The matrix rows are lattice vectors. Preserve:
(a, b, c)(alpha, beta, gamma)Niggli/LLL reduction and crystallographic standardization can change the cell basis and site coordinates without changing intended periodic geometry. They still produce new representations and require provenance.
The verified constructor includes explicit safety-relevant switches:
from pymatgen.core import Lattice, Structure
structure = Structure(
lattice=Lattice.cubic(5.64),
species=["Na", "Cl"],
coords=[[0, 0, 0], [0.5, 0.5, 0.5]],
coords_are_cartesian=False,
validate_proximity=True,
to_unit_cell=False,
)
Structure is mutable. IStructure is immutable/hashable. Prefer:
original = Structure.from_file("input.cif", primitive=False, sort=False)
derived = original.copy()
derived.make_supercell([2, 2, 2])
Do not mutate original in a provenance-sensitive workflow.
Each periodic site's species is a composition-like mapping. An ordered site
has one species with occupancy 1. A disordered site can contain multiple
species and fractional occupancies.
for index, site in enumerate(structure):
occupancy_sum = sum(float(value) for value in site.species.values())
print(index, site.species, occupancy_sum)
Before downstream analysis:
structure.is_orderedOrdering a disordered structure changes the model and may create many candidates. It is never a format cleanup.
site.frac_coords and site.coords are fractional and Cartesian,
respectively. Fractional coordinates outside [0, 1) can be valid periodic
images; wrapping them is a transformation, not an automatic fix.
Record whether to_unit_cell, sorting, merging, primitive reduction, or
standardization occurred. Check minimum periodic distances under a site-count
bound; an all-pairs matrix is quadratic.
Oxidation states can be attached to species:
decorated = structure.copy()
decorated.add_oxidation_state_by_element({"Na": 1, "Cl": -1})
This mutates the copied structure. Preserve the undecorated parent, mapping,
method, and any charge-balance assumptions. add_oxidation_state_by_guess() is
heuristic; do not invoke it implicitly.
Current public operations include:
Structure.from_file(path, primitive=False, sort=False, merge_tol=0.0)Structure.from_str(text, fmt=...)structure.to(filename=..., fmt=...)get_distance(i, j) and bounded neighbor methodsget_primitive_structure()copy(), make_supercell(), apply_strain(), and site editinginterpolate() for compatible endpointsEvery operation has assumptions. Interpolation does not establish a physical path; primitive/standard cells can alter site order and properties.
from pymatgen.core import Molecule
water = Molecule(
["O", "H", "H"],
[[0.0, 0.0, 0.0], [0.758, 0.0, 0.504], [-0.758, 0.0, 0.504]],
charge=0,
spin_multiplicity=1,
)
Molecule coordinates are Cartesian Å. Record:
File formats often omit charge, multiplicity, bonding, isotope, or atom-label
semantics. Molecule.from_file() parsing success does not prove those fields
were present or preserved.
Core objects expose as_dict() and from_dict():
import json
from pymatgen.core import Structure
payload = structure.as_dict()
text = json.dumps(payload, allow_nan=False, sort_keys=True)
decoded = json.loads(text)
restored = Structure.from_dict(decoded)
For untrusted JSON:
Structure schemaDo not use pickle. Do not feed attacker-controlled MSON metadata to a general decoder that dynamically imports classes. JSON is only a syntax; schema validation is the trust boundary.