Electronic-State Participation Ratio¶
The defectpl.participation_ratio module computes how much of each
Kohn–Sham wavefunction is spatially concentrated on the defect neighbourhood.
It reads site-projected wavefunction data from a VASP PROCAR file and
pairs it with defect geometry from defect_entry.json.
Physical Background¶
Site-projected wavefunction character¶
For a Kohn–Sham state |ψ_{n,k,σ}⟩, VASP decomposes the wavefunction onto spherical harmonics centred at each atom i:
This is exactly the "tot" column for atom i in the PROCAR file.
Participation Ratio (P-ratio)¶
The P-ratio measures the fraction of the total wavefunction weight residing on the defect neighbourhood atoms:
where \(\mathcal{N}\) is the set of neighbouring-atom indices.
| P-ratio | Physical meaning |
|---|---|
| ≈ 0 | Delocalized — bulk host state |
| ≈ 1 | Fully localized on defect neighbours — deep defect level |
States with P-ratio > 0.2 are typically classified as defect-localized (Kumagai et al., PRB 2021).
Inverse Participation Ratio (IPR)¶
The site-based IPR is a code-agnostic localization metric:
| IPR value | Physical meaning |
|---|---|
| \(1/N\) | Perfectly delocalized over N atoms |
| 1 | Fully localized on a single atom |
Required Files¶
| File | Required? | Description |
|---|---|---|
PROCAR |
Yes | VASP projected wavefunction output. Needs LORBIT = 11 or 12 in INCAR. |
defect_entry.json |
Yes | Provides defect_name and defect_center. Generated by defectpl pr make-entry. |
defect_structure_info.json |
Recommended | Neighbour atom indices. Generated by defectpl pr make-dsi. |
CONTCAR / POSCAR |
Fallback | Used for distance-based neighbour search when DSI file is absent. |
No pydefect needed
All prerequisite files can be generated directly with defectpl:
Workflow¶
1. Prepare VASP inputs¶
Add LORBIT = 11 (or 12) to your INCAR before running VASP:
This ensures the PROCAR file contains full site projections.
2. Generate defect_entry.json¶
Option A — Manual (provide the defect centre directly)
Option B — Auto-detect from perfect vs defect structure
The command compares the two structures, finds the removed/added atom, and uses its position as the defect centre. Works for vacancies, interstitials, and substitutions.
3. Generate defect_structure_info.json (recommended)¶
This writes a defect_structure_info.json containing the indices of all
atoms within 3.5 Å of the defect centre.
4. Run the calculation¶
All defaults are auto-detected. Explicit paths can be supplied:
defectpl pr calc \
--procar Va_O1_2/PROCAR \
--entry Va_O1_2/defect_entry.json \
--dsi Va_O1_2/defect_structure_info.json \
--out Va_O1_2/
Output files:
participation_ratio.json— full nested resultsparticipation_ratio_summary.csv— flat table
Python API¶
High-level: ParticipationRatioCalculator¶
from defectpl.participation_ratio import ParticipationRatioCalculator
calc = ParticipationRatioCalculator(
procar = "PROCAR",
defect_entry = "defect_entry.json",
defect_structure_info = "defect_structure_info.json", # optional
poscar = "CONTCAR", # fallback
cutoff_radius = 3.5, # Å
)
result = calc.run()
# Serialise results
calc.to_json("participation_ratio.json")
calc.to_csv("participation_ratio_summary.csv")
# Query top-10 most localised states
for row in calc.top_localized(n=10, metric="p_ratio"):
print(row["spin"], row["kpt"], row["band"],
row["energy"], row["p_ratio"], row["ipr"])
Generating prerequisite files from Python¶
from defectpl.defect_utils import make_defect_entry, make_defect_structure_info
# Create defect_entry.json manually
make_defect_entry(
name = "Va_O1_2",
center = [0.5, 0.5, 0.5],
)
# Auto-detect from structures
make_defect_entry(
name = "Va_O1_2",
perfect_poscar = "../perfect/POSCAR",
defect_poscar = "CONTCAR",
)
# Create defect_structure_info.json
make_defect_structure_info(
poscar = "CONTCAR",
defect_center_frac = [0.5, 0.5, 0.5],
cutoff_radius = 3.5,
)
Low-level functions¶
from defectpl.participation_ratio import (
read_procar,
compute_participation_ratios,
neighbors_from_defect_structure_info,
resolve_neighbors,
)
# Parse PROCAR (native parser — no extra dependencies)
procar_data = read_procar("PROCAR", use_pymatgen=False)
# Resolve neighbours from the best available source
indices, source = resolve_neighbors(
dsi_path = "defect_structure_info.json",
poscar_path = "CONTCAR",
defect_center_frac = [0.5, 0.5, 0.5],
cutoff_radius = 3.5,
)
# Compute P-ratio and IPR
result = compute_participation_ratios(
procar_data = procar_data,
neighbor_indices = indices,
defect_name = "Va_O1_2",
defect_center = [0.5, 0.5, 0.5],
)
Output Schema¶
participation_ratio.json:
{
"defect_name": "Va_O1_2",
"defect_center": [0.86, 0.86, 0.86],
"neighbor_atom_indices": [3, 7, 11, 15],
"n_atoms": 56,
"n_spins": 1,
"n_kpoints": 1,
"n_bands": 224,
"neighbor_source": "defect_structure_info.json (4 atoms)",
"data": {
"spin_1": {
"kpt_1": {
"band_112": {
"energy": -0.231,
"occupation": 1.0,
"p_ratio": 0.7812,
"ipr": 0.031200,
"p_neighbors": 0.768,
"p_total": 0.983
}
}
}
}
}
participation_ratio_summary.csv columns:
spin, kpt, band, energy, occ, p_ratio, ipr, p_neighbors, p_total
Neighbour Resolution Strategy¶
The module uses the best available source for neighbour indices, in order:
-
defect_structure_info.json— reads keysneighbor_atom_indices,neighboring_atom_indices, or aneighborslist withindexfields. Compatible with both defectpl-generated and pydefect-generated files. -
Distance-based search on POSCAR/CONTCAR — finds all atoms within
cutoff_radiusÅ using the minimum-image convention. -
Empty list — if no structural information is available. All P-ratios are reported as 0 (only IPR remains meaningful).
CLI Reference¶
See the Command Line Interface page.
Quick reference:
# Single directory (auto-detect all paths)
defectpl pr calc
# Batch across charge-state subdirectories
defectpl pr batch --dir defects/Va_O1/
# Print summary of existing results
defectpl pr summary Va_O1_2/participation_ratio.json
# List top-10 most localised states by IPR
defectpl pr top Va_O1_2/participation_ratio.json --n 10 --metric ipr
Plotting¶
defectpl provides three dedicated plot functions for visualising P-ratio and IPR
results. All functions return a matplotlib.Axes and work without a display
(they use whatever matplotlib backend is active; the CLI forces Agg).
P-ratio / IPR vs energy¶
from defectpl.participation_ratio import plot_pr_vs_energy
import json
with open("participation_ratio.json") as fh:
result = json.load(fh)
# Simple scatter — P-ratio vs energy
ax = plot_pr_vs_energy(result)
# With VBM/CBM markers and custom window
ax = plot_pr_vs_energy(
result,
metric = "p_ratio", # or "ipr"
threshold = 0.2, # dashed horizontal line
vbm = 5.20, # orange vertical line
cbm = 8.10, # green vertical line
emin = 4.0, # filter: only bands with energy ≥ 4 eV
emax = 9.5,
out = "pr_energy.pdf",
)
| Parameter | Default | Description |
|---|---|---|
metric |
"p_ratio" |
Y-axis: "p_ratio" or "ipr". |
threshold |
0.2 |
Dashed horizontal reference line value. |
vbm |
None |
Orange dotted vertical line at VBM. |
cbm |
None |
Green dotted vertical line at CBM. |
emin / emax |
None |
Energy window filter (eV). |
kpt_idx |
0 |
0-based k-point to plot. |
out |
None |
Save file path. When None the axes is returned without saving. |
ax |
None |
Inject into an existing matplotlib.Axes. |
P-ratio / IPR vs band index¶
from defectpl.participation_ratio import plot_pr_vs_band_index
ax = plot_pr_vs_band_index(
result,
metric = "p_ratio",
threshold = 0.2,
emin = 4.0, # restrict to bands with energy ≥ 4 eV
emax = 9.5,
out = "pr_band.png",
)
The band index (1-based, matching PROCAR band numbering) appears on the X-axis.
Use emin/emax to show only the bands that fall within an energy window —
useful for focusing on the gap region.
Kohn-Sham level plot with P-ratio colour code¶
from defectpl.vasp import read_eigenval_file
from defectpl.ks_analysis import extract_ksplot_data, plot_ks_with_pr
import json
eigenval_data = read_eigenval_file("EIGENVAL", k_idx=0)
ks_data = extract_ksplot_data(eigenval_data, vbm=5.20, cbm=8.10, espan=1.5)
with open("participation_ratio.json") as fh:
pr_result = json.load(fh)
plot_ks_with_pr(
ks_data, pr_result,
metric = "p_ratio", # or "ipr"
cmap = "RdYlGn_r", # green = low, red = high localization
vmin = 0.0,
vmax = 1.0,
output_filename = "ks_pr_plot.png",
)
Each horizontal bar in the level diagram is coloured by the P-ratio (or IPR) of that Kohn-Sham state. A continuous colorbar is added on the right.
| Parameter | Default | Description |
|---|---|---|
metric |
"p_ratio" |
Localization metric for colouring. |
cmap |
"RdYlGn_r" |
Matplotlib colormap name. |
vmin / vmax |
0.0 / 1.0 |
Colormap bounds. |
kpt_idx |
0 |
0-based k-point index. |
output_filename |
"ks_pr_plot.png" |
Destination file. |
figsize |
(7, 6) |
Figure size in inches. |
dpi |
300 |
Image resolution. |
Tips¶
- Always use
LORBIT = 11(or12) in INCAR so PROCAR contains full site projections. - For Gamma-point-only calculations (
KPOINTSwith a single k-point),n_kpoints = 1and the output always showskpt_1. - For spin-polarized calculations (
ISPIN = 2) the output contains bothspin_1(majority) andspin_2(minority) blocks. - The native PROCAR parser (
--native-procarflag oruse_pymatgen=False) works without a pymatgen installation and is faster for large files. - Use
defectpl pr batchto process all charge states of a defect in one command.