Skill Tracer
Skills › Scientific Agent Skills › pymatgen › analysis_modules.md

analysis_modules.md

367 lines · skills/pymatgen/references/analysis_modules.md at 5f2bc03

Open on GitHub
1# Analysis: tolerances, computed entries, bands, DOS, and model limits
2
3This reference targets `pymatgen==2026.9.24` with
4`pymatgen-core==2026.9.23`. An analysis object returning a value does not
5establish convergence, uncertainty, experimental agreement, or suitability of
6the underlying model.
7

Analysis: tolerances, computed entries, bands, DOS, and model limits

This reference targets pymatgen==2026.9.24 with pymatgen-core==2026.9.23. An analysis object returning a value does not establish convergence, uncertainty, experimental agreement, or suitability of the underlying model.

Raw lines 1 to 7
8## Symmetry
9
10```python
11from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
13analyzer = SpacegroupAnalyzer(
14 structure,
15 symprec=0.01, # Å
16 angle_tolerance=5.0, # degrees
19result = {
20 "symbol": analyzer.get_space_group_symbol(),
21 "number": analyzer.get_space_group_number(),
22 "crystal_system": str(analyzer.get_crystal_system()),
23 "point_group": analyzer.get_point_group_symbol(),
24 "operation_count": len(analyzer.get_symmetry_operations()),
26symmetrized = analyzer.get_symmetrized_structure()
27equivalent_indices = symmetrized.equivalent_indices
28wyckoff_symbols = symmetrized.wyckoff_symbols
29```
31The documented pymatgen default is `symprec=0.01 Å`; a looser value such as
32`0.1 Å` is often used for relaxed structures and by the Materials Project
33pipeline. Results can change with:
35- coordinate precision and relaxation noise
36- occupancy/disorder model
37- oxidation/spin/site properties used or ignored
38- primitive/conventional representation
39- `symprec`, `angle_tolerance`, and spglib version
41Always sweep justified tolerances and report the entire sensitivity grid. Do
42not choose a tolerance solely because it gives a desired group.
44Standardized or primitive structures are new representations:
46```python
47conventional = analyzer.get_conventional_standard_structure(
48 keep_site_properties=False
50primitive = analyzer.get_primitive_standard_structure(
51 keep_site_properties=False
53```
55Site properties can be lost or propagated without symmetry-aware adjustment.
56Preserve the parent and compare composition, volume per atom, magnetic order,
57and property semantics.

Symmetry

from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

analyzer = SpacegroupAnalyzer(
    structure,
    symprec=0.01,          # Å
    angle_tolerance=5.0,   # degrees
)

result = {
    "symbol": analyzer.get_space_group_symbol(),
    "number": analyzer.get_space_group_number(),
    "crystal_system": str(analyzer.get_crystal_system()),
    "point_group": analyzer.get_point_group_symbol(),
    "operation_count": len(analyzer.get_symmetry_operations()),
}
symmetrized = analyzer.get_symmetrized_structure()
equivalent_indices = symmetrized.equivalent_indices
wyckoff_symbols = symmetrized.wyckoff_symbols

The documented pymatgen default is symprec=0.01 Å; a looser value such as 0.1 Å is often used for relaxed structures and by the Materials Project pipeline. Results can change with:

  • coordinate precision and relaxation noise
  • occupancy/disorder model
  • oxidation/spin/site properties used or ignored
  • primitive/conventional representation
  • symprec, angle_tolerance, and spglib version

Always sweep justified tolerances and report the entire sensitivity grid. Do not choose a tolerance solely because it gives a desired group.

Standardized or primitive structures are new representations:

conventional = analyzer.get_conventional_standard_structure(
    keep_site_properties=False
)
primitive = analyzer.get_primitive_standard_structure(
    keep_site_properties=False
)

Site properties can be lost or propagated without symmetry-aware adjustment. Preserve the parent and compare composition, volume per atom, magnetic order, and property semantics.

Raw lines 8 to 58
59## Structure matching
61```python
62from pymatgen.analysis.structure_matcher import StructureMatcher
64matcher = StructureMatcher(
65 ltol=0.2,
66 stol=0.3,
67 angle_tol=5,
68 primitive_cell=True,
69 scale=True,
71matches = matcher.fit(first, second)
72```
74Record every tolerance and option. A match is equivalence under the chosen
75algorithm, reductions, scaling, and species comparator—not identity of files,
76provenance, defects, magnetic states, or experimental phases.
78`fit()` is directional. For order-independent grouping use
79`matcher.group_structures(structures, symmetric=True)` in this snapshot;
80the new option is ignored for anonymous matching. Current mixed-functional
81entry processing also sorts inputs and uses symmetric grouping, fixing earlier
82hash-seed-dependent hulls. Recompute old mixed hulls when comparing versions.

Structure matching

from pymatgen.analysis.structure_matcher import StructureMatcher

matcher = StructureMatcher(
    ltol=0.2,
    stol=0.3,
    angle_tol=5,
    primitive_cell=True,
    scale=True,
)
matches = matcher.fit(first, second)

Record every tolerance and option. A match is equivalence under the chosen algorithm, reductions, scaling, and species comparator—not identity of files, provenance, defects, magnetic states, or experimental phases.

fit() is directional. For order-independent grouping use matcher.group_structures(structures, symmetric=True) in this snapshot; the new option is ignored for anonymous matching. Current mixed-functional entry processing also sorts inputs and uses symmetric grouping, fixing earlier hash-seed-dependent hulls. Recompute old mixed hulls when comparing versions.

Raw lines 59 to 83
84## Local environments
86```python
87from pymatgen.analysis.local_env import CrystalNN, VoronoiNN
89crystal_nn = CrystalNN()
90neighbors = crystal_nn.get_nn_info(structure, 0)
92voronoi_nn = VoronoiNN()
93voronoi_neighbors = voronoi_nn.get_nn_info(structure, 0)
94```
96Coordination depends on the method, radii/oxidation information, weights,
97cutoffs, disorder, and geometry. Preserve:
99- algorithm and pymatgen version
100- all constructor settings
101- oxidation-state decoration
102- site index/label mapping
103- warnings and failures
104- whether weighted or integer coordination was reported
106Bound the number of sites and neighbors emitted. Cross-check model-sensitive
107conclusions with more than one justified definition.

Local environments

from pymatgen.analysis.local_env import CrystalNN, VoronoiNN

crystal_nn = CrystalNN()
neighbors = crystal_nn.get_nn_info(structure, 0)

voronoi_nn = VoronoiNN()
voronoi_neighbors = voronoi_nn.get_nn_info(structure, 0)

Coordination depends on the method, radii/oxidation information, weights, cutoffs, disorder, and geometry. Preserve:

  • algorithm and pymatgen version
  • all constructor settings
  • oxidation-state decoration
  • site index/label mapping
  • warnings and failures
  • whether weighted or integer coordination was reported

Bound the number of sites and neighbors emitted. Cross-check model-sensitive conclusions with more than one justified definition.

Raw lines 84 to 108
109## Phase diagrams
111An `Entry` contains a composition and a total energy:
113```python
114from pymatgen.analysis.phase_diagram import PhaseDiagram
115from pymatgen.entries.computed_entries import ComputedEntry
117entries = [
118 ComputedEntry("Li", -1.0, entry_id="local-Li"),
119 ComputedEntry("O2", -2.0, entry_id="local-O2"),
120 ComputedEntry("Li2O", -4.0, entry_id="local-Li2O"),
122diagram = PhaseDiagram(entries)
124for entry in entries:
125 print(
126 entry.entry_id,
127 diagram.get_form_energy_per_atom(entry),
128 diagram.get_e_above_hull(entry),
129 )
130```
132`ComputedEntry.energy` is total eV for the represented composition, not
133eV/atom. `energy_per_atom`, formation energy, and hull distance are normalized
134values.

Phase diagrams

An Entry contains a composition and a total energy:

from pymatgen.analysis.phase_diagram import PhaseDiagram
from pymatgen.entries.computed_entries import ComputedEntry

entries = [
    ComputedEntry("Li", -1.0, entry_id="local-Li"),
    ComputedEntry("O2", -2.0, entry_id="local-O2"),
    ComputedEntry("Li2O", -4.0, entry_id="local-Li2O"),
]
diagram = PhaseDiagram(entries)

for entry in entries:
    print(
        entry.entry_id,
        diagram.get_form_energy_per_atom(entry),
        diagram.get_e_above_hull(entry),
    )

ComputedEntry.energy is total eV for the represented composition, not eV/atom. energy_per_atom, formation energy, and hull distance are normalized values.

Raw lines 109 to 135
136### Comparability gate
138Before constructing a hull, verify that entries share a compatible:
140- functional and correction/mixing scheme
141- pseudopotential family and valence configuration
142- magnetic, spin, and SOC treatment
143- reference-state convention
144- numerical convergence level
145- temperature/pressure model
147Include elemental endpoints and all relevant competing phases. Missing phases
148can make unstable entries appear stable. Duplicate compositions are allowed as
149polymorphs only when their energies are comparable and provenance is distinct.
151`diagram.stable_entries` means on the computed zero-temperature convex hull for
152that exact entry set. It is not experimental stability or synthesizability.

Comparability gate

Before constructing a hull, verify that entries share a compatible:

  • functional and correction/mixing scheme
  • pseudopotential family and valence configuration
  • magnetic, spin, and SOC treatment
  • reference-state convention
  • numerical convergence level
  • temperature/pressure model

Include elemental endpoints and all relevant competing phases. Missing phases can make unstable entries appear stable. Duplicate compositions are allowed as polymorphs only when their energies are comparable and provenance is distinct.

diagram.stable_entries means on the computed zero-temperature convex hull for that exact entry set. It is not experimental stability or synthesizability.

Raw lines 136 to 153
154### Decomposition
156```python
157from pymatgen.core import Composition
159target = Composition("Li2O", strict=True)
160decomposition = diagram.get_decomposition(target)
161```
163For an existing entry, use `get_e_above_hull(entry)`. A bare composition has no
164candidate energy, so it has a hull decomposition but no intrinsic energy above
165hull.

Decomposition

from pymatgen.core import Composition

target = Composition("Li2O", strict=True)
decomposition = diagram.get_decomposition(target)

For an existing entry, use get_e_above_hull(entry). A bare composition has no candidate energy, so it has a hull decomposition but no intrinsic energy above hull.

Raw lines 154 to 166
167### Plotting
169```python
170from pymatgen.analysis.phase_diagram import PDPlotter
172plotter = PDPlotter(diagram, show_unstable=0.2, backend="matplotlib")
173plotter.write_image("phase.new.svg", image_format="svg")
174```
176Plot to a new path, bound unstable points and output size, and preserve the
177machine-readable entry table. Plotting backends and image export can introduce
178optional dependencies. The default Plotly backend needs its image-export stack;
179the bundled CLI selects Matplotlib so PNG/PDF/SVG export works with the tested
180package set.

Plotting

from pymatgen.analysis.phase_diagram import PDPlotter

plotter = PDPlotter(diagram, show_unstable=0.2, backend="matplotlib")
plotter.write_image("phase.new.svg", image_format="svg")

Plot to a new path, bound unstable points and output size, and preserve the machine-readable entry table. Plotting backends and image export can introduce optional dependencies. The default Plotly backend needs its image-export stack; the bundled CLI selects Matplotlib so PNG/PDF/SVG export works with the tested package set.

Raw lines 167 to 181
182## Chemical-potential and Pourbaix analyses
184`ChemicalPotentialDiagram` and `PourbaixDiagram` add assumptions beyond a
185composition hull. Record reference states, open species, aqueous ion data,
186concentrations, pH, electrochemical potential, temperature, corrections, and
187solvent convention. Do not reuse a solid-state entry set as a valid aqueous
188thermodynamic model without the required transformations and references.

Chemical-potential and Pourbaix analyses

ChemicalPotentialDiagram and PourbaixDiagram add assumptions beyond a composition hull. Record reference states, open species, aqueous ion data, concentrations, pH, electrochemical potential, temperature, corrections, and solvent convention. Do not reuse a solid-state entry set as a valid aqueous thermodynamic model without the required transformations and references.

Raw lines 182 to 189
190## Electronic band structures
192The following VASP snippets are illustrative because they require the user's
193calculation files; the current method signatures were verified.
195```python
196from pymatgen.io.vasp import Vasprun
198run = Vasprun(
199 "vasprun.xml",
200 parse_dos=False,
201 parse_eigen=True,
202 parse_projected_eigen=False,
203 parse_potcar_file=False,
205bands = run.get_band_structure(line_mode=True)
207gap = bands.get_band_gap()
208vbm = bands.get_vbm()
209cbm = bands.get_cbm()
210metal = bands.is_metal()
211```
213Report:
215- source calculation and convergence status
216- structure checksum
217- functional, pseudopotentials, DFT+U, spin, SOC
218- k-point mesh/path and line-mode reconstruction
219- Fermi-energy convention and any override
220- occupation/smearing settings
221- direct/indirect criterion and numerical tolerance
223A DFT band gap is method-dependent. Materials Project documents that its PBE
224band gaps are systematically underestimated.
226`BSPlotter` can plot a `BandStructureSymmLine`; plotting does not validate the
227k-path. High-symmetry paths depend on crystallographic setting and magnetic
228primitive-cell assumptions.

Electronic band structures

The following VASP snippets are illustrative because they require the user's calculation files; the current method signatures were verified.

from pymatgen.io.vasp import Vasprun

run = Vasprun(
    "vasprun.xml",
    parse_dos=False,
    parse_eigen=True,
    parse_projected_eigen=False,
    parse_potcar_file=False,
)
bands = run.get_band_structure(line_mode=True)

gap = bands.get_band_gap()
vbm = bands.get_vbm()
cbm = bands.get_cbm()
metal = bands.is_metal()

Report:

  • source calculation and convergence status
  • structure checksum
  • functional, pseudopotentials, DFT+U, spin, SOC
  • k-point mesh/path and line-mode reconstruction
  • Fermi-energy convention and any override
  • occupation/smearing settings
  • direct/indirect criterion and numerical tolerance

A DFT band gap is method-dependent. Materials Project documents that its PBE band gaps are systematically underestimated.

BSPlotter can plot a BandStructureSymmLine; plotting does not validate the k-path. High-symmetry paths depend on crystallographic setting and magnetic primitive-cell assumptions.

Raw lines 190 to 229
230## Density of states
232```python
233from pymatgen.io.vasp import Vasprun
235run = Vasprun(
236 "vasprun.xml",
237 parse_dos=True,
238 parse_eigen=False,
239 parse_projected_eigen=False,
240 parse_potcar_file=False,
242dos = run.complete_dos
243element_dos = dos.get_element_dos()
244site_dos = dos.get_site_dos(run.final_structure[0])
245orbital_dos = dos.get_spd_dos()
246```
248Check:
250- energy grid and reference/Fermi level
251- density units and normalization
252- spin channels and SOC
253- smearing and integration method
254- projection basis and completeness
255- consistency between DOS sites and final structure
257Do not compare integrated/projected DOS across calculations until these
258conventions match.
260For individual d orbitals, use `Orbital.dx2_y2`. `Orbital.dx2` remains a
261deprecated alias until 2027-08-17; this snapshot still serializes the legacy
262`dx2` key while accepting both spellings on read.

Density of states

from pymatgen.io.vasp import Vasprun

run = Vasprun(
    "vasprun.xml",
    parse_dos=True,
    parse_eigen=False,
    parse_projected_eigen=False,
    parse_potcar_file=False,
)
dos = run.complete_dos
element_dos = dos.get_element_dos()
site_dos = dos.get_site_dos(run.final_structure[0])
orbital_dos = dos.get_spd_dos()

Check:

  • energy grid and reference/Fermi level
  • density units and normalization
  • spin channels and SOC
  • smearing and integration method
  • projection basis and completeness
  • consistency between DOS sites and final structure

Do not compare integrated/projected DOS across calculations until these conventions match.

For individual d orbitals, use Orbital.dx2_y2. Orbital.dx2 remains a deprecated alias until 2027-08-17; this snapshot still serializes the legacy dx2 key while accepting both spellings on read.

Raw lines 230 to 263
264## VASP parse cost
266`Vasprun(parse_projected_eigen=True)` can require extreme time and memory.
267`BSVasprun` is optimized for eigenvalue-focused band-structure parsing. Large
268XML/HDF5/volumetric files need file-size, array-size, site/k-point/band, memory,
269and wall-time bounds.

VASP parse cost

Vasprun(parse_projected_eigen=True) can require extreme time and memory. BSVasprun is optimized for eigenvalue-focused band-structure parsing. Large XML/HDF5/volumetric files need file-size, array-size, site/k-point/band, memory, and wall-time bounds.

Raw lines 264 to 270
271## Diffraction
273```python
274from pymatgen.analysis.diffraction.xrd import XRDCalculator
276calculator = XRDCalculator(wavelength="CuKa")
277pattern = calculator.get_pattern(
278 structure,
279 scaled=True,
280 two_theta_range=(5, 90),
283for two_theta, intensity, hkls in zip(
284 pattern.x,
285 pattern.y,
286 pattern.hkls,
287 strict=True,
288):
289 print(two_theta, intensity, hkls)
290```
292Peak positions/intensities depend on radiation, occupancies, structure,
293instrumental broadening, preferred orientation, temperature/displacement, and
294the ideal-powder model. A simulated pattern is not a phase-identification
295result by itself.

Diffraction

from pymatgen.analysis.diffraction.xrd import XRDCalculator

calculator = XRDCalculator(wavelength="CuKa")
pattern = calculator.get_pattern(
    structure,
    scaled=True,
    two_theta_range=(5, 90),
)

for two_theta, intensity, hkls in zip(
    pattern.x,
    pattern.y,
    pattern.hkls,
    strict=True,
):
    print(two_theta, intensity, hkls)

Peak positions/intensities depend on radiation, occupancies, structure, instrumental broadening, preferred orientation, temperature/displacement, and the ideal-powder model. A simulated pattern is not a phase-identification result by itself.

Raw lines 271 to 296
297## Surfaces, slabs, and Wulff shapes
299```python
300from pymatgen.core.surface import SlabGenerator
302generator = SlabGenerator(
303 structure,
304 miller_index=(1, 1, 1),
305 min_slab_size=12.0,
306 min_vacuum_size=15.0,
307 center_slab=True,
308 in_unit_planes=False,
310slabs = generator.get_slabs()
311```
313Record bulk parent, Miller-index convention, slab/vacuum units, termination,
314symmetrization, dipole correction, in-plane cell, fixed layers, and candidate
315limit. Slab thickness and vacuum are convergence parameters, not universal
316constants.
318Current `WulffShape` takes parallel Miller-index and surface-energy sequences:
320```python
321from pymatgen.analysis.wulff import WulffShape
323wulff = WulffShape(
324 structure.lattice,
325 [(1, 0, 0), (1, 1, 0), (1, 1, 1)],
326 [1.0, 1.1, 0.9], # one consistent energy unit per area
328```
330Surface energies must share composition/chemical-potential, slab, functional,
331and area conventions. Report their unit explicitly.

Surfaces, slabs, and Wulff shapes

from pymatgen.core.surface import SlabGenerator

generator = SlabGenerator(
    structure,
    miller_index=(1, 1, 1),
    min_slab_size=12.0,
    min_vacuum_size=15.0,
    center_slab=True,
    in_unit_planes=False,
)
slabs = generator.get_slabs()

Record bulk parent, Miller-index convention, slab/vacuum units, termination, symmetrization, dipole correction, in-plane cell, fixed layers, and candidate limit. Slab thickness and vacuum are convergence parameters, not universal constants.

Current WulffShape takes parallel Miller-index and surface-energy sequences:

from pymatgen.analysis.wulff import WulffShape

wulff = WulffShape(
    structure.lattice,
    [(1, 0, 0), (1, 1, 0), (1, 1, 1)],
    [1.0, 1.1, 0.9],  # one consistent energy unit per area
)

Surface energies must share composition/chemical-potential, slab, functional, and area conventions. Report their unit explicitly.

Raw lines 297 to 332
333## Adsorption
335`AdsorbateSiteFinder` produces geometric candidates, not adsorption energies or
336preferred sites. Bound generated structures and preserve slab termination,
337adsorbate geometry/charge/spin, coverage, orientation, and parent mapping.

Adsorption

AdsorbateSiteFinder produces geometric candidates, not adsorption energies or preferred sites. Bound generated structures and preserve slab termination, adsorbate geometry/charge/spin, coverage, orientation, and parent mapping.

Raw lines 333 to 338
339## Elasticity and other tensors
341`pymatgen.analysis.elasticity` represents strain, stress, and elastic tensors.
342Verify Voigt index convention, stress sign, units (typically GPa for reported
343moduli), reference frame, crystal symmetry, finite-strain magnitude, and fit
344quality. Mechanical-stability criteria depend on crystal class and conditions.

Elasticity and other tensors

pymatgen.analysis.elasticity represents strain, stress, and elastic tensors. Verify Voigt index convention, stress sign, units (typically GPa for reported moduli), reference frame, crystal symmetry, finite-strain magnitude, and fit quality. Mechanical-stability criteria depend on crystal class and conditions.

Raw lines 339 to 345
346## Analysis report checklist
348- source checksum and parser warnings
349- exact package versions
350- units and normalization
351- all tolerances/model parameters
352- bounded input/output sizes
353- disorder and oxidation-state handling
354- convergence and uncertainty evidence
355- method-specific caveats
356- no claim of experimental truth from computed output alone

Analysis report checklist

  • source checksum and parser warnings
  • exact package versions
  • units and normalization
  • all tolerances/model parameters
  • bounded input/output sizes
  • disorder and oxidation-state handling
  • convergence and uncertainty evidence
  • method-specific caveats
  • no claim of experimental truth from computed output alone
Raw lines 346 to 357
358## Sources (verified 2026-09-30)
360- [pymatgen analysis API](https://pymatgen.org/pymatgen.analysis.html)
361- [pymatgen symmetry API](https://pymatgen.org/pymatgen.symmetry.html)
362- [pymatgen electronic-structure API](https://pymatgen.org/pymatgen.electronic_structure.html)
363- [pymatgen VASP API](https://pymatgen.org/pymatgen.io.vasp.html)
364- [pymatgen usage guide](https://pymatgen.org/usage.html)
365- [pymatgen changelog](https://pymatgen.org/CHANGES.html)
366- [Materials Project electronic-structure methodology](https://docs.materialsproject.org/methodology/materials-methodology/electronic-structure)
367- [Materials Project computed-data FAQ](https://docs.materialsproject.org/frequently-asked-questions)