← All writing

· 8 min read

From raw XRD data to answers: a step-by-step guide for beginners

The structured workflow I could not find when I first opened a raw diffractogram: what the machine actually gives you, how to clean it, identify phases, and calculate d-spacing, crystallite size and strain, with a worked example and a Python script.

In my instrumentation course I learned how X-ray diffraction works: X-rays scatter off planes of atoms, and Bragg's law says where the reflections appear. It made sense on the whiteboard. Then I got a raw file from a real diffractometer and had no idea where to start. There were thousands of noisy points, a sloping background, peaks that looked doubled, and a dozen software tools, each assuming I already knew the workflow.

I searched for a structured, beginner-level path from "raw file" to "what is in my sample and how big are the crystals". I didn't find one, so I worked it out the long way. This guide is that path, written for juniors and anyone opening their first diffractogram.

What the machine actually gives you

A powder diffractometer measures X-ray intensity (counts) at each detector angle, written $2\theta$. The raw file is just those two columns, wrapped in a vendor format: .raw from Bruker, .xrdml from Malvern Panalytical, .ras from Rigaku. Your first job is to get a plain two-column .xy or .csv file, using the instrument's own software or a free converter such as PowDLL.

Before anything else, write down three things from the instrument report:

  • The X-ray source. Almost always Cu Kα, wavelength $\lambda = 1.5406$ Å. Every number below depends on it.
  • The step size and scan range, for example 0.02° steps from 5° to 80°.
  • The sample form: powder, film or pressed pellet. It affects which corrections matter later.

The one equation: Bragg's law

$$ n\lambda = 2d\sin\theta $$

A peak at angle $2\theta$ means there is a family of atomic planes spaced $d$ apart. The most common beginner error in all of XRD is using $2\theta$ where the equation needs $\theta$. The x-axis is $2\theta$, so halve it before you take the sine or cosine.

Step 1: Look before you calculate

Plot the raw data, intensity against $2\theta$, and just look:

  • Sharp, narrow peaks: crystalline material.
  • A broad hump (often between 15° and 30°): amorphous content. Polymers like chitosan show one, and so does a glass sample holder.
  • Peaks on top of a hump: a semi-crystalline material or a composite. Common, and fine.
  • A very noisy pattern: too little sample or too fast a scan. No processing will rescue it; re-measure.

Step 2: Clean the data, gently

Three corrections, applied lightly:

  1. Background subtraction. Remove the slowly varying baseline so peak heights and widths mean something. Fit the baseline through the regions between peaks. Never cut into a peak.
  2. Smoothing, only if needed. A light Savitzky–Golay filter reduces noise. Heavy smoothing broadens peaks and will make your crystallites look smaller than they are.
  3. Kα2 stripping. Copper emits two close wavelengths, Kα1 and Kα2 (1.5444 Å). At high angles each peak splits into a doublet. Most software can remove the Kα2 part; otherwise remember the shoulder is not a second phase.

Step 3: Find the peaks

For each peak, record its position ($2\theta$ at the maximum), its intensity (height above background), and its FWHM, the full width at half maximum, in degrees. Fitting each peak with a pseudo-Voigt function gives the most reliable position and width. Reading them off the plot is fine for a first look.

Step 4: Identify the phases

This is the question most people actually care about: what is in my sample?

Compare your peak list with reference patterns. Each crystalline compound has a fingerprint: a set of peak positions with characteristic relative intensities. Sources:

  • Crystallography Open Database (COD): free, with CIF files.
  • Materials Project: free, with a calculated pattern for every compound.
  • ICDD PDF: the paid standard, used by HighScore and many instrument packages.
  • VESTA: open any CIF and use Utilities → Powder Diffraction Pattern to simulate its pattern for your wavelength.

A match means peak positions agree (within about 0.1–0.2°) and relative intensities roughly agree. Positions matter most. Intensities can be distorted by preferred orientation (see the pitfalls below). Start with the phases you expect from your synthesis, then explain every peak that remains. An unexplained peak is a finding: an impurity, an unreacted precursor or a second phase.

Step 5: Calculate d-spacing and lattice parameters

From each peak position:

$$ d = \frac{\lambda}{2\sin\theta} $$

For a cubic crystal, once you know the peak's Miller indices $(hkl)$:

$$ a = d\sqrt{h^2 + k^2 + l^2} $$

Other crystal systems have their own formulas, and refinement software handles them. The same idea is what tracks MXene's interlayer spacing. The (002) peak of the Ti3AlC2 MAX phase sits near 9.5° ($d \approx 9.3$ Å). After etching and delamination it typically moves to lower angles, around 6–7° ($d \approx 13$–14 Å), because the layers have been pushed apart.

Step 6: Crystallite size with the Scherrer equation

Small crystallites broaden peaks. The Scherrer equation turns broadening into size:

$$ D = \frac{K\lambda}{\beta\cos\theta} $$
  • $D$ is the crystallite size (the size of coherent crystal domains, not particle size).
  • $K \approx 0.9$ is a shape factor.
  • $\lambda$ is the wavelength.
  • $\beta$ is the peak broadening in radians, after removing the instrument's own broadening.
  • $\theta$ is half the peak position.

Every instrument broadens peaks a little. Measure a standard with very large crystals (LaB6 or silicon) on the same instrument to get the instrumental FWHM. For roughly Gaussian peaks, subtract it in quadrature:

$$ \beta = \sqrt{\beta_\text{obs}^2 - \beta_\text{inst}^2} $$

Worked example

Anatase TiO2 has its strongest peak, (101), near $2\theta = 25.30°$. Say you measure an FWHM of 0.50°, and your standard shows an instrumental FWHM of 0.08°.

StepCalculationResult
θ25.30° / 212.65°
d-spacing1.5406 / (2 × sin 12.65°)3.517 Å (anatase (101) is 3.52 Å ✓)
Corrected β√(0.50² − 0.08²)0.494° = 0.00861 rad
Size D0.9 × 1.5406 / (0.00861 × cos 12.65°)165 Å = 16.5 nm

Two classic mistakes, using the same numbers. Leaving β in degrees gives 0.28 nm, smaller than a single unit cell. Using cos 2θ instead of cos θ gives 17.6 nm, wrong but believable, which makes it the more dangerous mistake.

Scherrer works best for crystallites below about 100 nm. Above that, the broadening becomes too small to measure reliably.

Step 7: Separate size from strain (Williamson–Hall)

Lattice strain also broadens peaks, and Scherrer blames all broadening on size. With four or more peaks you can separate the two using the Williamson–Hall equation:

$$ \beta\cos\theta = \frac{K\lambda}{D} + 4\varepsilon\sin\theta $$

Plot $\beta\cos\theta$ (y-axis) against $4\sin\theta$ (x-axis) for each peak and fit a straight line. The slope is the strain $\varepsilon$, and the intercept is $K\lambda/D$, which gives the size. A negative slope usually means non-uniform peaks or poorly fitted widths, not negative strain.

Step 8: Know when to go further

Everything above is single-peak analysis. Its natural next step is Rietveld refinement, which fits the whole pattern with a crystal structure model to get lattice parameters, phase fractions, size and strain together. Free tools such as GSAS-II, FullProf and Profex do it. Learn single-peak analysis first, though: Rietveld is far easier when you already understand what each peak is telling you.

Pitfalls that fooled me

  • All peaks shifted the same way: usually the sample surface was not at the right height (sample displacement), not a change in the material. An internal standard such as silicon powder mixed in settles the question.
  • Wrong relative intensities: preferred orientation. Plate-like crystals and films, MXene included, lie flat and exaggerate their (00l) peaks.
  • A broad hump called a phase: amorphous content or the sample holder. Measure the empty holder once.
  • Too many peaks "identified": if you need five phases to explain a simple synthesis, check your references before you check your chemistry.

A script to do steps 2, 3, 5 and 6

pythonimport numpy as np
from scipy.ndimage import minimum_filter1d, uniform_filter1d
from scipy.signal import savgol_filter, find_peaks, peak_widths

LAMBDA = 1.5406          # Å, Cu Kα1
K = 0.9                  # Scherrer shape factor
INSTRUMENT_FWHM = 0.08   # degrees 2θ, measured on a LaB6 or Si standard

two_theta, counts = np.loadtxt("sample.xy", unpack=True)   # columns: 2θ (°), intensity
step = np.mean(np.diff(two_theta))

smooth = savgol_filter(counts, window_length=11, polyorder=3)    # light smoothing only
window = int(3 / step)                                           # ~3° rolling window
background = uniform_filter1d(minimum_filter1d(smooth, window), window)
signal = smooth - background

peaks, _ = find_peaks(signal, prominence=0.05 * signal.max())
fwhm = peak_widths(signal, peaks, rel_height=0.5)[0] * step      # in degrees

print(f"{'2θ (°)':>8} {'d (Å)':>8} {'FWHM (°)':>9} {'D (nm)':>8}")
for p, w in zip(peaks, fwhm):
    theta = np.radians(two_theta[p] / 2)                         # θ, not 2θ
    d = LAMBDA / (2 * np.sin(theta))
    beta = np.radians(np.sqrt(max(w**2 - INSTRUMENT_FWHM**2, 0)))
    size = K * LAMBDA / (beta * np.cos(theta)) / 10 if beta else float("nan")
    print(f"{two_theta[p]:8.2f} {d:8.3f} {w:9.3f} {size:8.1f}")

Treat its output as a first pass and check every peak by eye. For quick plots and peak tables without writing code, I built Plot Bhai. It runs in the browser, and your data never leaves your computer.

The workflow on one line

Convert → look → clean gently → find peaks → match phases → d-spacing → Scherrer → Williamson–Hall → Rietveld when you are ready. Follow it in that order and XRD stops being overwhelming.

If you are stuck on a pattern, send it my way. Helping a beginner read one is the reason this post exists.

Reply by email

Open to graduate positions and collaboration

Let's build materials that matter.