Structure Refinement: PbSO4, NPD + XRD¶
This example demonstrates a more advanced use of the EasyDiffraction library by explicitly creating and configuring structures and experiments before adding them to a project. It could be more suitable for users who are interested in creating custom workflows. This tutorial provides minimal explanation and is intended for users already familiar with EasyDiffraction.
The tutorial covers a Rietveld refinement of PbSO4 crystal structure based on the joint fit of both X-ray and neutron diffraction data.
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='pbso4')
structure = StructureFactory.from_scratch(name='pbso4')
Set Space Group¶
In [4]:
Copied!
structure.space_group.name_h_m = 'P n m a'
structure.space_group.name_h_m = 'P n m a'
Set Unit Cell¶
In [5]:
Copied!
structure.cell.length_a = 8.47
structure.cell.length_b = 5.39
structure.cell.length_c = 6.95
structure.cell.length_a = 8.47
structure.cell.length_b = 5.39
structure.cell.length_c = 6.95
Set Atom Sites¶
In [6]:
Copied!
structure.atom_sites.create(
label='Pb',
type_symbol='Pb',
fract_x=0.1876,
fract_y=0.25,
fract_z=0.167,
wyckoff_letter='c',
adp_iso=1.37,
)
structure.atom_sites.create(
label='S',
type_symbol='S',
fract_x=0.0654,
fract_y=0.25,
fract_z=0.684,
wyckoff_letter='c',
adp_iso=0.3777,
)
structure.atom_sites.create(
label='O1',
type_symbol='O',
fract_x=0.9082,
fract_y=0.25,
fract_z=0.5954,
wyckoff_letter='c',
adp_iso=1.9764,
)
structure.atom_sites.create(
label='O2',
type_symbol='O',
fract_x=0.1935,
fract_y=0.25,
fract_z=0.5432,
wyckoff_letter='c',
adp_iso=1.4456,
)
structure.atom_sites.create(
label='O3',
type_symbol='O',
fract_x=0.0811,
fract_y=0.0272,
fract_z=0.8086,
wyckoff_letter='d',
adp_iso=1.2822,
)
structure.atom_sites.create(
label='Pb',
type_symbol='Pb',
fract_x=0.1876,
fract_y=0.25,
fract_z=0.167,
wyckoff_letter='c',
adp_iso=1.37,
)
structure.atom_sites.create(
label='S',
type_symbol='S',
fract_x=0.0654,
fract_y=0.25,
fract_z=0.684,
wyckoff_letter='c',
adp_iso=0.3777,
)
structure.atom_sites.create(
label='O1',
type_symbol='O',
fract_x=0.9082,
fract_y=0.25,
fract_z=0.5954,
wyckoff_letter='c',
adp_iso=1.9764,
)
structure.atom_sites.create(
label='O2',
type_symbol='O',
fract_x=0.1935,
fract_y=0.25,
fract_z=0.5432,
wyckoff_letter='c',
adp_iso=1.4456,
)
structure.atom_sites.create(
label='O3',
type_symbol='O',
fract_x=0.0811,
fract_y=0.0272,
fract_z=0.8086,
wyckoff_letter='d',
adp_iso=1.2822,
)
In [7]:
Copied!
data_path1 = download_data(id=13, destination='data')
data_path1 = download_data(id=13, destination='data')
Getting data...
Data #13: PbSO4, D1A (ILL)
✅ Data #13 downloaded to 'data/ed-13.dat'
Create Experiment¶
In [8]:
Copied!
expt1 = ExperimentFactory.from_data_path(
name='npd',
data_path=data_path1,
radiation_probe='neutron',
)
expt1 = ExperimentFactory.from_data_path(
name='npd',
data_path=data_path1,
radiation_probe='neutron',
)
Set Instrument¶
In [9]:
Copied!
expt1.instrument.setup_wavelength = 1.91
expt1.instrument.calib_twotheta_offset = -0.1406
expt1.instrument.setup_wavelength = 1.91
expt1.instrument.calib_twotheta_offset = -0.1406
Set Peak Profile¶
In [10]:
Copied!
expt1.peak.broad_gauss_u = 0.139
expt1.peak.broad_gauss_v = -0.412
expt1.peak.broad_gauss_w = 0.386
expt1.peak.broad_lorentz_x = 0
expt1.peak.broad_lorentz_y = 0.088
expt1.peak.broad_gauss_u = 0.139
expt1.peak.broad_gauss_v = -0.412
expt1.peak.broad_gauss_w = 0.386
expt1.peak.broad_lorentz_x = 0
expt1.peak.broad_lorentz_y = 0.088
Set Background¶
Select the background type.
In [11]:
Copied!
expt1.background_type = 'line-segment'
expt1.background_type = 'line-segment'
Background type for experiment 'npd' already set to
line-segment
Add background points.
In [12]:
Copied!
for id, x, y in [
('1', 11.0, 206.1624),
('2', 15.0, 194.75),
('3', 20.0, 194.505),
('4', 30.0, 188.4375),
('5', 50.0, 207.7633),
('6', 70.0, 201.7002),
('7', 120.0, 244.4525),
('8', 153.0, 226.0595),
]:
expt1.background.create(id=id, x=x, y=y)
for id, x, y in [
('1', 11.0, 206.1624),
('2', 15.0, 194.75),
('3', 20.0, 194.505),
('4', 30.0, 188.4375),
('5', 50.0, 207.7633),
('6', 70.0, 201.7002),
('7', 120.0, 244.4525),
('8', 153.0, 226.0595),
]:
expt1.background.create(id=id, x=x, y=y)
Set Linked Phases¶
In [13]:
Copied!
expt1.linked_phases.create(id='pbso4', scale=1.5)
expt1.linked_phases.create(id='pbso4', scale=1.5)
In [14]:
Copied!
data_path2 = download_data(id=16, destination='data')
data_path2 = download_data(id=16, destination='data')
Getting data...
Data #16: PbSO4, laboratory X-ray
✅ Data #16 downloaded to 'data/ed-16.dat'
Create Experiment¶
In [15]:
Copied!
expt2 = ExperimentFactory.from_data_path(
name='xrd',
data_path=data_path2,
radiation_probe='xray',
)
expt2 = ExperimentFactory.from_data_path(
name='xrd',
data_path=data_path2,
radiation_probe='xray',
)
Set Instrument¶
In [16]:
Copied!
expt2.instrument.setup_wavelength = 1.540567
expt2.instrument.calib_twotheta_offset = -0.05181
expt2.instrument.setup_wavelength = 1.540567
expt2.instrument.calib_twotheta_offset = -0.05181
Set Peak Profile¶
In [17]:
Copied!
expt2.peak.broad_gauss_u = 0.304138
expt2.peak.broad_gauss_v = -0.112622
expt2.peak.broad_gauss_w = 0.021272
expt2.peak.broad_lorentz_x = 0
expt2.peak.broad_lorentz_y = 0.057691
expt2.peak.broad_gauss_u = 0.304138
expt2.peak.broad_gauss_v = -0.112622
expt2.peak.broad_gauss_w = 0.021272
expt2.peak.broad_lorentz_x = 0
expt2.peak.broad_lorentz_y = 0.057691
Set Background¶
Select background type.
In [18]:
Copied!
expt2.background_type = 'chebyshev'
expt2.background_type = 'chebyshev'
Background type for experiment 'xrd' changed to
chebyshev
Add background points.
In [19]:
Copied!
for id, x, y in [
('1', 0, 119.195),
('2', 1, 6.221),
('3', 2, -45.725),
('4', 3, 8.119),
('5', 4, 54.552),
('6', 5, -20.661),
]:
expt2.background.create(id=id, order=x, coef=y)
for id, x, y in [
('1', 0, 119.195),
('2', 1, 6.221),
('3', 2, -45.725),
('4', 3, 8.119),
('5', 4, 54.552),
('6', 5, -20.661),
]:
expt2.background.create(id=id, order=x, coef=y)
Set Linked Phases¶
In [20]:
Copied!
expt2.linked_phases.create(id='pbso4', scale=0.001)
expt2.linked_phases.create(id='pbso4', scale=0.001)
In [21]:
Copied!
project = Project()
project = Project()
Add Structure¶
In [22]:
Copied!
project.structures.add(structure)
project.structures.add(structure)
Add Experiments¶
In [23]:
Copied!
project.experiments.add(expt1)
project.experiments.add(expt2)
project.experiments.add(expt1)
project.experiments.add(expt2)
In [24]:
Copied!
project.analysis.fit_mode.mode = 'joint'
project.analysis.fit_mode.mode = 'joint'
Set Minimizer¶
In [25]:
Copied!
project.analysis.current_minimizer = 'lmfit'
project.analysis.current_minimizer = 'lmfit'
Current minimizer changed to
lmfit
Set Fitting Parameters¶
Set structure parameters to be optimized.
In [26]:
Copied!
structure.cell.length_a.free = True
structure.cell.length_b.free = True
structure.cell.length_c.free = True
structure.cell.length_a.free = True
structure.cell.length_b.free = True
structure.cell.length_c.free = True
Set experiment parameters to be optimized.
In [27]:
Copied!
expt1.linked_phases['pbso4'].scale.free = True
expt1.instrument.calib_twotheta_offset.free = True
expt1.peak.broad_gauss_u.free = True
expt1.peak.broad_gauss_v.free = True
expt1.peak.broad_gauss_w.free = True
expt1.peak.broad_lorentz_y.free = True
expt1.linked_phases['pbso4'].scale.free = True
expt1.instrument.calib_twotheta_offset.free = True
expt1.peak.broad_gauss_u.free = True
expt1.peak.broad_gauss_v.free = True
expt1.peak.broad_gauss_w.free = True
expt1.peak.broad_lorentz_y.free = True
In [28]:
Copied!
expt2.linked_phases['pbso4'].scale.free = True
expt2.instrument.calib_twotheta_offset.free = True
expt2.peak.broad_gauss_u.free = True
expt2.peak.broad_gauss_v.free = True
expt2.peak.broad_gauss_w.free = True
expt2.peak.broad_lorentz_y.free = True
for term in expt2.background:
term.coef.free = True
expt2.linked_phases['pbso4'].scale.free = True
expt2.instrument.calib_twotheta_offset.free = True
expt2.peak.broad_gauss_u.free = True
expt2.peak.broad_gauss_v.free = True
expt2.peak.broad_gauss_w.free = True
expt2.peak.broad_lorentz_y.free = True
for term in expt2.background:
term.coef.free = True
Perform Fit¶
Plot Measured vs Calculated¶
In [29]:
Copied!
project.plotter.plot_meas_vs_calc(expt_name='npd', x_min=35.5, x_max=38.3, show_residual=True)
project.plotter.plot_meas_vs_calc(expt_name='npd', x_min=35.5, x_max=38.3, show_residual=True)
In [30]:
Copied!
project.plotter.plot_meas_vs_calc(expt_name='xrd', x_min=29.0, x_max=30.4, show_residual=True)
project.plotter.plot_meas_vs_calc(expt_name='xrd', x_min=29.0, x_max=30.4, show_residual=True)