skills/pymatgen/references/transformations_workflows.md
Transformations create scientific hypotheses and derived structures. They are not harmless cleanup. Always retain the original, make assumptions explicit, bound candidate growth, and record parent/child checksums.
A pymatgen transformation exposes apply_transformation(structure, ...).
One-to-one transformations return a structure; one-to-many transformations can
return ranked dictionaries when explicitly requested.
from pymatgen.transformations.standard_transformations import (
SubstitutionTransformation,
SupercellTransformation,
)
parent = structure.copy()
supercell = SupercellTransformation([2, 2, 2]).apply_transformation(parent)
substituted = SubstitutionTransformation({"Na": "K"}).apply_transformation(
parent
)
Even if a transformation currently returns a new object, keep parent
unchanged and assert it against a pre-operation checksum.
For every transformation, record:
from pymatgen.transformations.standard_transformations import (
SupercellTransformation,
)
transformation = SupercellTransformation(
[[2, 0, 0], [0, 2, 0], [0, 0, 2]]
)
child = transformation.apply_transformation(parent)
Check:
Reject a matrix or candidate whose determinant/site count exceeds the reviewed bound. A non-diagonal matrix changes the cell basis and site ordering.
Complete substitution:
from pymatgen.transformations.standard_transformations import (
SubstitutionTransformation,
)
child = SubstitutionTransformation({"Fe": "Mn"}).apply_transformation(parent)
Partial substitution creates disorder:
disordered = SubstitutionTransformation(
{"Fe": {"Fe": 0.5, "Mn": 0.5}}
).apply_transformation(parent)
Verify composition, charge model, oxidation states, occupancy sums, and whether all intended sites—not just a selected sublattice—were transformed. Partial occupancy is an average/disordered representation, not one ordered atomic configuration.
from pymatgen.transformations.standard_transformations import (
RemoveSpeciesTransformation,
)
child = RemoveSpeciesTransformation(["H"]).apply_transformation(parent)
Removing species changes composition, charge, and possibly connectivity. Never use it as silent parser cleanup. Record removed site indices/species and validate charge/stoichiometry afterward.
from pymatgen.transformations.standard_transformations import (
ConventionalCellTransformation,
PrimitiveCellTransformation,
)
primitive = PrimitiveCellTransformation(
tolerance=0.5,
).apply_transformation(parent)
conventional = ConventionalCellTransformation(
symprec=0.01,
angle_tolerance=5,
).apply_transformation(parent)
These results depend on symmetry tolerances and can change site order or site properties. Compare formula and volume per atom, preserve exact tolerances, and do not treat standardized cells from different conventions as byte-identical.
from pymatgen.transformations.standard_transformations import (
DeformStructureTransformation,
)
deformed = DeformStructureTransformation(
[[1.01, 0, 0], [0, 1.0, 0], [0, 0, 1.0]]
).apply_transformation(parent)
State whether a matrix is a deformation gradient, strain-like approximation, or lattice transform. Bound determinant, condition number, minimum distances, and strain magnitude. Generate positive and negative strains under one manifest for tensor fitting.
Oxidation states are inputs to several transformations and electrostatic rankings. Decoration can be explicit or guessed. Prefer explicit mappings grounded in chemistry:
decorated = parent.copy()
decorated.add_oxidation_state_by_element({"Li": 1, "O": -2})
If a guesser is explicitly approved, bound complexity and preserve all candidate assignments and assumptions. Do not present the first guess as a measured charge state.
from pymatgen.transformations.standard_transformations import (
OrderDisorderedStructureTransformation,
)
transformation = OrderDisorderedStructureTransformation()
ranked = transformation.apply_transformation(
disordered,
return_ranked_list=20,
)
Ordering can grow combinatorially. Before running:
The top-ranked ordering is model-dependent, not a unique ground state.
EnumerateStructureTransformation generates symmetrically distinct orderings
and requires the external enumlib executables (enum.x and makestr.x).
Several advanced transformations, including magnetic ordering, can rely on
enumeration.
Treat enumlib as a separate native-code execution:
Do not install or invoke enumlib automatically.
Advanced doping and charge-balance transformations encode chemical and electrostatic assumptions. A requested dopant does not uniquely define:
Require those choices before execution and report every generated candidate. Validate composition and net formal charge after each transformation.
SlabTransformation and SlabGenerator require explicit Miller index, slab
thickness, vacuum thickness, shift/termination, and cell-reduction choices.
Bound the number of terminations and generated structures. Record whether
sizes are in Å or unit planes.
Never overwrite the bulk parent. Surface energies additionally require consistent bulk/slab methods, atom/reference accounting, surface area, and whether one or two equivalent surfaces are present.
MagOrderingTransformation uses proposed magnetic moments and can enumerate
orderings. Record:
Generated magnetic arrangements are calculation inputs, not converged magnetic ground states.
from pymatgen.alchemy.materials import TransformedStructure
from pymatgen.transformations.standard_transformations import (
SubstitutionTransformation,
SupercellTransformation,
)
tracked = TransformedStructure(parent.copy(), [])
tracked.append_transformation(SupercellTransformation([2, 2, 2]))
tracked.append_transformation(SubstitutionTransformation({"Na": "K"}))
child = tracked.final_structure
history = tracked.history
The history is useful but not sufficient provenance. Also store:
Use strict JSON after schema validation; do not use pickle.
Bundled helpers:
python scripts/composition_structure_validator.py structure input.cif
python scripts/artifact_manifest.py \
--artifact input.cif --artifact transformed.json \
--workflow "reviewed supercell derivation" --output manifest.json
energy_basis: total_per_entry.python scripts/phase_diagram_generator.py entries.json --analyze Li2O
from pymatgen.io.vasp.sets import MPRelaxSet
input_set = MPRelaxSet(
child,
user_incar_settings={"ENCUT": 600},
)
Before write_input():
Pymatgen does not run VASP or establish convergence.
Each stage must link to the exact previous output checksum. Do not reuse a stale structure or charge density silently.
--execute and the named MP_API_KEY.The database object is computed input with provenance, not experimental truth.
Every automated workflow should cap:
Stop on bound exhaustion and report partial progress; never silently truncate a candidate set and present it as exhaustive.