DiffractScout’s offline powder engine is built on Gemmi. CIF2Peaks used
pymatgen XRDCalculator for theoretical powder lines. The two implementations
are designed for workflow parity, not bit-identical intensities.
Both tools aim to produce, from a structure-bearing CIF and an X-ray wavelength:
- indexed reflections with Miller indices, (d), (\theta), (2\theta), (q), (g);
- structure-factor-related intensities with and without a laboratory-style Lorentz–polarization (LP) factor;
- phase-internal relative intensities (strongest line scaled to 100);
- volume-normalized intensity channels
(J = I / V_{\mathrm{cell}}^2) (legacy names still contain
R_hkl); - optional plane-normal Young’s modulus when a valid (C_{ij}) is paired;
- a continuous pseudo-Voigt display profile for plotting.
Users can move the same scientific questions—candidate peaks, LP vs no-LP
channels, elasticity on hkl normals—between the two codebases with the column
aliases in SCHEMA_ALIASES.md.
| Quantity | Expectation |
|---|---|
| Absolute (I) or (J) values | May differ between Gemmi and pymatgen |
| Peak-by-peak intensity ordering near ties | May swap when values are close |
| Multiplicity of a given representative | Same physical idea; counting of symmetry/Friedel mates can differ in edge cases |
Representative hkl of a family |
Both pick a deterministic member; the choice rule may differ |
| Multi-family coincidence at one (2\theta) | CIF2Peaks could merge pymatgen families into one peak row; DiffractScout emits one row per unique family |
| Floating-point (2\theta), (d), (q) | Agree to crystallographic precision for clean cells; not bit-identical |
Do not use bit-identical intensity regression between CIF2Peaks exports and DiffractScout as a release gate. Prefer analytic structure-factor checks (e.g. monoatomic FCC (\lvert F_{111}\rvert^2)), space-group absences, and internal invariants ((J = I / V^2), LP ratio consistency).
| Topic | CIF2Peaks (pymatgen) | DiffractScout (Gemmi) |
|---|---|---|
| Structure I/O | pymatgen structure from CIF | Gemmi small structure; dedicated occupancy conversion for SF |
| Powder intensities | XRDCalculator.get_pattern(scaled=False) |
Enumerate Miller candidates, absences, (\lvert F\rvert^2), multiplicity, LP |
| Atomic form factors / SF | pymatgen calculator defaults | Gemmi StructureFactorCalculatorX |
| Debye–Waller | Assumed 1 when absent | Same practical boundary; missing (B) not invented |
| LP factor | Same laboratory-style form ((1+\cos^2 2\theta)/(\sin^2\theta\cos\theta)) | Same formula in diffraction.py |
| Volume-normalized (J) | I_unscaled / V^2 and (I_unscaled/LP)/V^2 |
I_with_LP / V^2 and I_no_LP / V^2 |
| Offline base install | Required pymatgen for local XRD | Core analysis uses Gemmi; pymatgen optional via mp-api |
| Provenance | Export notes | SHA-256 inputs, manifest, scientific boundary string |
Conceptually:
I_with_LP ≈ theoretical_intensity_unscaled (CIF2Peaks)
I_no_LP ≈ multiplicity_structure_factor_sq (CIF2Peaks)
J_with_LP = I_with_LP / V_cell^2 ↔ material_scattering_factor_R_hkl
J_no_LP = I_no_LP / V_cell^2 ↔ material_scattering_factor_R_hkl_no_lp
The definitions of the (J) channels match. The numerators come from different structure-factor stacks, so (J) values are workflow-comparable, not byte-equal.
Both engines respect crystallographic absences for the resolved space group. DiffractScout records space-group resolution order and optional spglib cross-checks in diagnostics. A mismatch between declared and detected symmetry is a user-review item in both ecosystems; it can change which lines appear.
CIF2Peaks often retained four-index Miller–Bravais labels when pymatgen supplied
them. DiffractScout stores three-index (h,k,l) on ReflectionRecord and can
format four-index display labels with label_hkl_for_crystal_system in
hkl.py when the crystal system string indicates hexagonal or trigonal families.
Plane-normal elasticity always uses the three-index plane
plane_hkl_for_normal (requiring (i = -(h+k)) for four-index input).
The optional independent-engine comparison runs the packaged synthetic CIFs through both DiffractScout and pymatgen at the same wavelength and cell setting. It compares coincident peak groups by spacing, checks reflection selection rules, and compares cubic directional moduli with pymatgen's tensor implementation. Versioned reports record input hashes, tolerances, software versions, and normalized-intensity differences. Intensity differences are reported separately from the peak-position and elasticity acceptance checks because the two scattering-factor models differ.
python -m pip install -e ".[mp]"
python scripts/compare_reference_engines.py --output outputs/reference_engines- Contract tests: (J = I / V^2), ranks consistent with channels, LP ratio (I_{\mathrm{with,LP}} / I_{\mathrm{no,LP}}).
- Analytic fixtures: known monoatomic cells and expected (\lvert F\rvert^2).
- Cross-engine comparison: compare (d) and (2\theta) to a tight tolerance; compare intensity ratios or top-(N) peak sets, not raw floats.
- Never treat legacy
R_hklcolumns as Rietveld residuals (seeSCHEMA_ALIASES.mdandSCIENTIFIC_CONTRACTS.md).
DiffractScout preserves the CIF2Peaks scientific workflow (indexed peaks, LP split, volume-normalized channels, optional (E(n_{hkl})), plottable profile) while moving crystallographic computation to Gemmi for an offline-first, provenance-oriented package. Intensity parity is semantic, not bitwise.