Joint Refinement: Si, Bragg + PDF¶
This example demonstrates a joint refinement of the Si crystal structure combining Bragg diffraction and pair distribution function (PDF) analysis. The Bragg experiment uses time-of-flight neutron powder diffraction data from SEPD at Argonne, while the PDF experiment uses data from NOMAD at SNS. A single shared Si structure is refined simultaneously against both datasets.
🛠️ Import Library¶
In [2]:
Copied!
from easydiffraction import ExperimentFactory
from easydiffraction import Project
from easydiffraction import StructureFactory
from easydiffraction import download_data
from easydiffraction import ExperimentFactory
from easydiffraction import Project
from easydiffraction import StructureFactory
from easydiffraction import download_data
In [3]:
Copied!
structure = StructureFactory.from_scratch(name='si')
structure = StructureFactory.from_scratch(name='si')
Set Space Group¶
In [4]:
Copied!
structure.space_group.name_h_m = 'F d -3 m'
structure.space_group.it_coordinate_system_code = '1'
structure.space_group.name_h_m = 'F d -3 m'
structure.space_group.it_coordinate_system_code = '1'
Set Unit Cell¶
In [5]:
Copied!
structure.cell.length_a = 5.42
structure.cell.length_a = 5.42
Set Atom Sites¶
In [6]:
Copied!
structure.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0,
fract_y=0,
fract_z=0,
adp_iso=0.2,
)
structure.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0,
fract_y=0,
fract_z=0,
adp_iso=0.2,
)
In [7]:
Copied!
bragg_data_path = download_data(id=7, destination='data')
bragg_data_path = download_data(id=7, destination='data')
Getting data...
Data #7: Si, SEPD (Argonne)
✅ Data #7 downloaded to '../../../data/ed-7.xye'
Create Experiment¶
In [8]:
Copied!
bragg_expt = ExperimentFactory.from_data_path(
name='sepd', data_path=bragg_data_path, beam_mode='time-of-flight'
)
bragg_expt = ExperimentFactory.from_data_path(
name='sepd', data_path=bragg_data_path, beam_mode='time-of-flight'
)
Set Instrument¶
In [9]:
Copied!
bragg_expt.instrument.setup_twotheta_bank = 144.845
bragg_expt.instrument.calib_d_to_tof_offset = -9.2
bragg_expt.instrument.calib_d_to_tof_linear = 7476.91
bragg_expt.instrument.calib_d_to_tof_quad = -1.54
bragg_expt.instrument.setup_twotheta_bank = 144.845
bragg_expt.instrument.calib_d_to_tof_offset = -9.2
bragg_expt.instrument.calib_d_to_tof_linear = 7476.91
bragg_expt.instrument.calib_d_to_tof_quad = -1.54
Set Peak Profile¶
In [10]:
Copied!
bragg_expt.peak.type = 'jorgensen'
bragg_expt.peak.broad_gauss_sigma_0 = 5.0
bragg_expt.peak.broad_gauss_sigma_1 = 45.0
bragg_expt.peak.broad_gauss_sigma_2 = 1.0
bragg_expt.peak.exp_decay_beta_0 = 0.04221
bragg_expt.peak.exp_decay_beta_1 = 0.00946
bragg_expt.peak.exp_rise_alpha_0 = 0.0
bragg_expt.peak.exp_rise_alpha_1 = 0.5971
bragg_expt.peak.type = 'jorgensen'
bragg_expt.peak.broad_gauss_sigma_0 = 5.0
bragg_expt.peak.broad_gauss_sigma_1 = 45.0
bragg_expt.peak.broad_gauss_sigma_2 = 1.0
bragg_expt.peak.exp_decay_beta_0 = 0.04221
bragg_expt.peak.exp_decay_beta_1 = 0.00946
bragg_expt.peak.exp_rise_alpha_0 = 0.0
bragg_expt.peak.exp_rise_alpha_1 = 0.5971
Peak profile type for experiment 'sepd' changed to
jorgensen
Set Background¶
In [11]:
Copied!
bragg_expt.background.type = 'line-segment'
for x in range(0, 35000, 5000):
bragg_expt.background.create(id=str(x), x=x, y=200)
bragg_expt.background.type = 'line-segment'
for x in range(0, 35000, 5000):
bragg_expt.background.create(id=str(x), x=x, y=200)
Background type for experiment 'sepd' already set to
line-segment
Set Linked Phases¶
In [12]:
Copied!
bragg_expt.linked_phases.create(id='si', scale=13.0)
bragg_expt.linked_phases.create(id='si', scale=13.0)
In [13]:
Copied!
pdf_data_path = download_data(id=5, destination='data')
pdf_data_path = download_data(id=5, destination='data')
Getting data...
Data #5: NOM_9999_Si_640g_PAC_50_ff_ftfrgr_up-to-50.gr
✅ Data #5 already present at '../../../data/ed-5.gr'. Keeping existing.
Create Experiment¶
In [14]:
Copied!
pdf_expt = ExperimentFactory.from_data_path(
name='nomad',
data_path=pdf_data_path,
beam_mode='time-of-flight',
scattering_type='total',
)
pdf_expt = ExperimentFactory.from_data_path(
name='nomad',
data_path=pdf_data_path,
beam_mode='time-of-flight',
scattering_type='total',
)
⚠️ No uncertainty (sy) column provided. Defaulting to 0.03.
Set Peak Profile (PDF Parameters)¶
In [15]:
Copied!
pdf_expt.peak.damp_q = 0.02
pdf_expt.peak.broad_q = 0.02
pdf_expt.peak.cutoff_q = 35.0
pdf_expt.peak.sharp_delta_1 = 0.001
pdf_expt.peak.sharp_delta_2 = 4.0
pdf_expt.peak.damp_particle_diameter = 0
pdf_expt.peak.damp_q = 0.02
pdf_expt.peak.broad_q = 0.02
pdf_expt.peak.cutoff_q = 35.0
pdf_expt.peak.sharp_delta_1 = 0.001
pdf_expt.peak.sharp_delta_2 = 4.0
pdf_expt.peak.damp_particle_diameter = 0
Set Linked Phases¶
In [16]:
Copied!
pdf_expt.linked_phases.create(id='si', scale=1.0)
pdf_expt.linked_phases.create(id='si', scale=1.0)
In [17]:
Copied!
project = Project(name='si_bragg_pdf')
project = Project(name='si_bragg_pdf')
Add Structure¶
In [18]:
Copied!
project.structures.add(structure)
project.structures.add(structure)
Add Experiments¶
In [19]:
Copied!
project.experiments.add(bragg_expt)
project.experiments.add(pdf_expt)
project.experiments.add(bragg_expt)
project.experiments.add(pdf_expt)
In [20]:
Copied!
project.analysis.fitting_mode.type = 'joint'
project.analysis.joint_fit.create(experiment_id='sepd', weight=0.7)
project.analysis.joint_fit.create(experiment_id='nomad', weight=0.3)
project.analysis.fitting_mode.type = 'joint'
project.analysis.joint_fit.create(experiment_id='sepd', weight=0.7)
project.analysis.joint_fit.create(experiment_id='nomad', weight=0.3)
Fitting mode changed to
joint
Display Structure¶
In [21]:
Copied!
project.display.structure(struct_name='si')
project.display.structure(struct_name='si')
Structure 🧩 'si' (Atom view type: 'covalent')
Loading plot…
drag = rotate
wheel = zoom
right-drag = pan
wheel = zoom
right-drag = pan
Display Pattern (Before Fit)¶
In [22]:
Copied!
project.display.pattern(expt_name='sepd')
project.display.pattern(expt_name='sepd')
Loading plot…
In [23]:
Copied!
project.display.pattern(expt_name='nomad')
project.display.pattern(expt_name='nomad')
Loading plot…
Set Free Parameters¶
Shared structural parameters are refined against both datasets simultaneously.
In [24]:
Copied!
structure.cell.length_a.free = True
structure.atom_sites['Si'].adp_iso.free = True
structure.cell.length_a.free = True
structure.atom_sites['Si'].adp_iso.free = True
Bragg experiment parameters.
In [25]:
Copied!
bragg_expt.linked_phases['si'].scale.free = True
bragg_expt.instrument.calib_d_to_tof_offset.free = True
bragg_expt.peak.broad_gauss_sigma_0.free = True
bragg_expt.peak.broad_gauss_sigma_1.free = True
bragg_expt.peak.broad_gauss_sigma_2.free = True
for point in bragg_expt.background:
point.y.free = True
bragg_expt.linked_phases['si'].scale.free = True
bragg_expt.instrument.calib_d_to_tof_offset.free = True
bragg_expt.peak.broad_gauss_sigma_0.free = True
bragg_expt.peak.broad_gauss_sigma_1.free = True
bragg_expt.peak.broad_gauss_sigma_2.free = True
for point in bragg_expt.background:
point.y.free = True
PDF experiment parameters.
In [26]:
Copied!
pdf_expt.linked_phases['si'].scale.free = True
pdf_expt.peak.damp_q.free = True
pdf_expt.peak.broad_q.free = True
pdf_expt.peak.sharp_delta_1.free = True
pdf_expt.peak.sharp_delta_2.free = True
pdf_expt.linked_phases['si'].scale.free = True
pdf_expt.peak.damp_q.free = True
pdf_expt.peak.broad_q.free = True
pdf_expt.peak.sharp_delta_1.free = True
pdf_expt.peak.sharp_delta_2.free = True
Display Free Parameters¶
In [27]:
Copied!
project.display.parameters.free()
project.display.parameters.free()
Free parameters for both structures (🧩 data blocks) and experiments (🔬 data blocks)
| datablock | category | entry | parameter | value | uncertainty | min | max | units | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | si | cell | length_a | 5.42000 | -inf | inf | Å | ||
| 2 | si | atom_site | Si | adp_iso | 0.20000 | -inf | inf | Ų | |
| 3 | sepd | linked_phases | si | scale | 13.00000 | -inf | inf | ||
| 4 | sepd | peak | gauss_sigma_0 | 5.00000 | -inf | inf | μs² | ||
| 5 | sepd | peak | gauss_sigma_1 | 45.00000 | -inf | inf | μs/Å | ||
| 6 | sepd | peak | gauss_sigma_2 | 1.00000 | -inf | inf | μs²/Ų | ||
| 7 | sepd | instrument | d_to_tof_offset | -9.20000 | -inf | inf | μs | ||
| 8 | sepd | background | 0 | y | 200.00000 | -inf | inf | ||
| 9 | sepd | background | 5000 | y | 200.00000 | -inf | inf | ||
| 10 | sepd | background | 10000 | y | 200.00000 | -inf | inf | ||
| 11 | sepd | background | 15000 | y | 200.00000 | -inf | inf | ||
| 12 | sepd | background | 20000 | y | 200.00000 | -inf | inf | ||
| 13 | sepd | background | 25000 | y | 200.00000 | -inf | inf | ||
| 14 | sepd | background | 30000 | y | 200.00000 | -inf | inf | ||
| 15 | nomad | linked_phases | si | scale | 1.00000 | -inf | inf | ||
| 16 | nomad | peak | damp_q | 0.02000 | -inf | inf | Å⁻¹ | ||
| 17 | nomad | peak | broad_q | 0.02000 | -inf | inf | Å⁻² | ||
| 18 | nomad | peak | sharp_delta_1 | 0.00100 | -inf | inf | Å | ||
| 19 | nomad | peak | sharp_delta_2 | 4.00000 | -inf | inf | Ų |
Run Fitting¶
In [28]:
Copied!
project.analysis.fit()
project.display.fit.results()
project.display.fit.correlations()
project.analysis.fit()
project.display.fit.results()
project.display.fit.correlations()
Using all experiments 🔬 ['sepd', 'nomad'] for 'joint' fitting
🚀 Starting fit process with 'lmfit (leastsq)'...
📈 Goodness-of-fit progress:
| iteration | time (s) | χ² | change / status | |
|---|---|---|---|---|
| 1 | 1 | 0.51 | 3378.13 | |
| 2 | 11 | 6.01 | 3378.13 | |
| 3 | 20 | 11.04 | 3378.11 | |
| 4 | 23 | 12.58 | 844.33 | 75.0% ↓ |
| 5 | 32 | 17.77 | 844.33 | |
| 6 | 41 | 22.87 | 844.33 | |
| 7 | 43 | 23.99 | 267.01 | 68.4% ↓ |
| 8 | 51 | 29.09 | 267.01 | |
| 9 | 60 | 34.83 | 267.02 | |
| 10 | 63 | 36.57 | 59.98 | 77.5% ↓ |
| 11 | 71 | 41.66 | 59.98 | |
| 12 | 80 | 46.73 | 59.98 | |
| 13 | 83 | 48.90 | 52.58 | 12.3% ↓ |
| 14 | 91 | 53.95 | 52.58 | |
| 15 | 100 | 59.04 | 52.58 | |
| 16 | 103 | 61.22 | 51.89 | 1.3% ↓ |
| 17 | 111 | 66.26 | 51.89 | |
| 18 | 120 | 71.41 | 51.89 | |
| 19 | 129 | 76.97 | 51.87 | |
| 20 | 135 | 82.28 | 51.87 | |
| 21 | 141 | 87.91 | 51.87 | |
| 22 | 147 | 93.00 | 51.87 | |
| 23 | 153 | 98.72 | 51.87 | |
| 24 | 159 | 103.82 | 51.87 | |
| 25 | 165 | 109.55 | 51.87 | |
| 26 | 170 | 114.02 | 51.87 |
🏆 Best goodness-of-fit (reduced χ²) is 51.87 at iteration 153
✅ Fitting complete.
⚙️ Settings used:
| Name | Value | Description | |
|---|---|---|---|
| 1 | max_iterations | 1000 | Maximum solver iterations. |
📋 Least-squares fit results:
| Metric | Value | |
|---|---|---|
| 1 | 🧪 Minimizer | lmfit (leastsq) |
| 2 | ✅ Overall status | success |
| 3 | ⏱️ Fitting time (seconds) | 114.02 |
| 4 | 🔁 Iterations | 167 |
| 5 | 📏 Goodness-of-fit (reduced χ²) | 51.87 |
| 6 | 📏 R-factor (Rf, %) | 10.50 |
| 7 | 📏 R-factor squared (Rf², %) | 9.32 |
| 8 | 📏 Weighted R-factor (wR, %) | 8.30 |
📈 Refined parameters:
| datablock | category | entry | parameter | units | start | value | s.u. | change | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | si | cell | length_a | Å | 5.4200 | 5.4306 | 0.0000 | 0.20 % ↑ | |
| 2 | si | atom_site | Si | adp_iso | Ų | 0.2000 | 0.7041 | 0.0037 | 252.05 % ↑ |
| 3 | sepd | linked_phases | si | scale | 13.0000 | 16.0569 | 0.1008 | 23.51 % ↑ | |
| 4 | sepd | peak | gauss_sigma_0 | μs² | 5.0000 | -1.9630 | 1.2495 | 139.26 % ↓ | |
| 5 | sepd | peak | gauss_sigma_1 | μs/Å | 45.0000 | 50.3913 | 2.4601 | 11.98 % ↑ | |
| 6 | sepd | peak | gauss_sigma_2 | μs²/Ų | 1.0000 | 0.2155 | 0.4974 | 78.45 % ↓ | |
| 7 | sepd | instrument | d_to_tof_offset | μs | -9.2000 | -8.2424 | 0.0950 | 10.41 % ↓ | |
| 8 | sepd | background | 0 | y | 200.0000 | 280.6064 | 3.2291 | 40.30 % ↑ | |
| 9 | sepd | background | 5000 | y | 200.0000 | 148.7236 | 1.3525 | 25.64 % ↓ | |
| 10 | sepd | background | 10000 | y | 200.0000 | 118.2995 | 1.4191 | 40.85 % ↓ | |
| 11 | sepd | background | 15000 | y | 200.0000 | 135.8529 | 2.7036 | 32.07 % ↓ | |
| 12 | sepd | background | 20000 | y | 200.0000 | 132.6849 | 4.7369 | 33.66 % ↓ | |
| 13 | sepd | background | 25000 | y | 200.0000 | 174.9850 | 9.5201 | 12.51 % ↓ | |
| 14 | sepd | background | 30000 | y | 200.0000 | 180.5821 | 19.3648 | 9.71 % ↓ | |
| 15 | nomad | linked_phases | si | scale | 1.0000 | 1.2713 | 0.0010 | 27.13 % ↑ | |
| 16 | nomad | peak | damp_q | Å⁻¹ | 0.0200 | 0.0250 | 0.0001 | 25.16 % ↑ | |
| 17 | nomad | peak | broad_q | Å⁻² | 0.0200 | 0.0188 | 0.0002 | 5.92 % ↓ | |
| 18 | nomad | peak | sharp_delta_1 | Å | 0.0010 | 2.4399 | 0.0382 | 243887.89 % ↑ | |
| 19 | nomad | peak | sharp_delta_2 | Ų | 4.0000 | -1.5282 | 0.0898 | 138.21 % ↓ |
• start = parameter value before refinement
• value = refined value from least-squares minimization
• s.u. = standard uncertainty (one sigma), from the covariance matrix
• change = relative change from start, in %; ↑ = increase, ↓ = decrease
• value = refined value from least-squares minimization
• s.u. = standard uncertainty (one sigma), from the covariance matrix
• change = relative change from start, in %; ↑ = increase, ↓ = decrease
⚠️ Red s.u.: exceeds the refined value (consider adding constraints)
Loading plot…
Display Pattern (After Fit)¶
In [29]:
Copied!
project.display.pattern(expt_name='sepd')
project.display.pattern(expt_name='sepd')
Loading plot…
In [30]:
Copied!
project.display.pattern(expt_name='nomad')
project.display.pattern(expt_name='nomad')
Loading plot…
💾 Save Project¶
In [31]:
Copied!
project.save_as(dir_path='projects/ed_16_si_bragg_pdf')
project.save_as(dir_path='projects/ed_16_si_bragg_pdf')
Saving project 📦 'si_bragg_pdf' to '../../../projects/ed_16_si_bragg_pdf'
├── 📄 project.cif
├── 📁 structures/
│ └── 📄 si.cif
├── 📁 experiments/
│ └── 📄 sepd.cif
│ └── 📄 nomad.cif
├── 📁 analysis/
│ └── 📄 analysis.cif
└── 📁 reports/
└── 📄 si_bragg_pdf.html