Structure Refinement: Co2SiO4, D20 (T-scan)¶
This example demonstrates a Rietveld refinement of the Co2SiO4 crystal structure using constant-wavelength neutron powder diffraction data from D20 at ILL. A sequential refinement of the same structure against a temperature scan is performed to show how to manage multiple experiments in a project.
Import Library¶
import easydiffraction as ed
Step 1: Define Project¶
The project object manages structures, experiments, and analysis.
project = ed.Project()
Set output verbosity level to "short" to show only one-line status messages during the analysis process.
project.verbosity = 'short'
project.structures.create(name='cosio')
structure = project.structures['cosio']
Set Space Group¶
structure.space_group.name_h_m = 'P n m a'
structure.space_group.it_coordinate_system_code = 'abc'
Set Unit Cell¶
structure.cell.length_a = 10.31
structure.cell.length_b = 6.0
structure.cell.length_c = 4.79
Set Atom Sites¶
structure.atom_sites.create(
label='Co1',
type_symbol='Co',
fract_x=0,
fract_y=0,
fract_z=0,
wyckoff_letter='a',
b_iso=0.3,
)
structure.atom_sites.create(
label='Co2',
type_symbol='Co',
fract_x=0.279,
fract_y=0.25,
fract_z=0.985,
wyckoff_letter='c',
b_iso=0.3,
)
structure.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0.094,
fract_y=0.25,
fract_z=0.429,
wyckoff_letter='c',
b_iso=0.34,
)
structure.atom_sites.create(
label='O1',
type_symbol='O',
fract_x=0.091,
fract_y=0.25,
fract_z=0.771,
wyckoff_letter='c',
b_iso=0.63,
)
structure.atom_sites.create(
label='O2',
type_symbol='O',
fract_x=0.448,
fract_y=0.25,
fract_z=0.217,
wyckoff_letter='c',
b_iso=0.59,
)
structure.atom_sites.create(
label='O3',
type_symbol='O',
fract_x=0.164,
fract_y=0.032,
fract_z=0.28,
wyckoff_letter='d',
b_iso=0.83,
)
file_path = ed.download_data(id=27, destination='data')
Getting data...
Data #27: Co2SiO4, D20 (ILL), 23 files, T from ~60K to ~500K
✅ Data #27 downloaded to 'data/ed-27.zip'
Create Experiments and Set Temperature¶
data_paths = ed.extract_data_paths_from_zip(file_path)
for i, data_path in enumerate(data_paths, start=1):
name = f'd20_{i}'
project.experiments.add_from_data_path(
name=name,
data_path=data_path,
)
expt = project.experiments[name]
expt.diffrn.ambient_temperature = ed.extract_metadata(
file_path=data_path,
pattern=r'^TEMP\s+([0-9.]+)',
)
✅ Data loaded: Experiment 🔬 'd20_1'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_2'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_3'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_4'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_5'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_6'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_7'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_8'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_9'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_10'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_11'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_12'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_13'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_14'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_15'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_16'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_17'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_18'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_19'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_20'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_21'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_22'. 1507 points.
✅ Data loaded: Experiment 🔬 'd20_23'. 1507 points.
Set Instrument¶
for expt in project.experiments:
expt.instrument.setup_wavelength = 1.87
expt.instrument.calib_twotheta_offset = 0.29
Set Peak Profile¶
for expt in project.experiments:
expt.peak.broad_gauss_u = 0.24
expt.peak.broad_gauss_v = -0.53
expt.peak.broad_gauss_w = 0.38
expt.peak.broad_lorentz_y = 0.02
Set Excluded Regions¶
for expt in project.experiments:
expt.excluded_regions.create(id='1', start=0, end=8)
expt.excluded_regions.create(id='2', start=150, end=180)
Set Background¶
for expt in project.experiments:
expt.background.create(id='1', x=8, y=609)
expt.background.create(id='2', x=9, y=581)
expt.background.create(id='3', x=10, y=563)
expt.background.create(id='4', x=11, y=540)
expt.background.create(id='5', x=12, y=520)
expt.background.create(id='6', x=15, y=507)
expt.background.create(id='7', x=25, y=463)
expt.background.create(id='8', x=30, y=434)
expt.background.create(id='9', x=50, y=451)
expt.background.create(id='10', x=70, y=431)
expt.background.create(id='11', x=90, y=414)
expt.background.create(id='12', x=110, y=361)
expt.background.create(id='13', x=130, y=292)
expt.background.create(id='14', x=150, y=241)
Set Linked Phases¶
for expt in project.experiments:
expt.linked_phases.create(id='cosio', scale=1.2)
Step 4: Perform Analysis¶
This section shows how to set free parameters, define constraints, and run the refinement.
Set Free Parameters¶
structure.cell.length_a.free = True
structure.cell.length_b.free = True
structure.cell.length_c.free = True
structure.atom_sites['Co2'].fract_x.free = True
structure.atom_sites['Co2'].fract_z.free = True
structure.atom_sites['Si'].fract_x.free = True
structure.atom_sites['Si'].fract_z.free = True
structure.atom_sites['O1'].fract_x.free = True
structure.atom_sites['O1'].fract_z.free = True
structure.atom_sites['O2'].fract_x.free = True
structure.atom_sites['O2'].fract_z.free = True
structure.atom_sites['O3'].fract_x.free = True
structure.atom_sites['O3'].fract_y.free = True
structure.atom_sites['O3'].fract_z.free = True
structure.atom_sites['Co1'].b_iso.free = True
structure.atom_sites['Co2'].b_iso.free = True
structure.atom_sites['Si'].b_iso.free = True
structure.atom_sites['O1'].b_iso.free = True
structure.atom_sites['O2'].b_iso.free = True
structure.atom_sites['O3'].b_iso.free = True
for expt in project.experiments:
expt.linked_phases['cosio'].scale.free = True
expt.instrument.calib_twotheta_offset.free = True
expt.peak.broad_gauss_u.free = True
expt.peak.broad_gauss_v.free = True
expt.peak.broad_gauss_w.free = True
expt.peak.broad_lorentz_y.free = True
for point in expt.background:
point.y.free = True
Set Constraints¶
Set aliases for parameters.
project.analysis.aliases.create(
label='biso_Co1',
param_uid=structure.atom_sites['Co1'].b_iso.uid,
)
project.analysis.aliases.create(
label='biso_Co2',
param_uid=structure.atom_sites['Co2'].b_iso.uid,
)
Set constraints.
project.analysis.constraints.create(
expression='biso_Co2 = biso_Co1',
)
Apply constraints.
project.analysis.apply_constraints()
Set Fit Mode¶
project.analysis.fit_mode.mode = 'single'
Run Fitting¶
project.analysis.fit()
Using 23 experiments 🔬 from 'd20_1' to 'd20_23' for 'single' fitting
🚀 Starting fit process with 'lmfit'...
📈 Goodness-of-fit (reduced χ²) per experiment:
| experiment | χ² | iterations | status | |
|---|---|---|---|---|
| 1 | d20_1 | 4.74 | 323 | ✅ |
| 2 | d20_2 | 4.59 | 187 | ✅ |
| 3 | d20_3 | 4.84 | 187 | ✅ |
| 4 | d20_4 | 4.71 | 184 | ✅ |
| 5 | d20_5 | 4.66 | 203 | ✅ |
| 6 | d20_6 | 4.84 | 187 | ✅ |
| 7 | d20_7 | 4.79 | 187 | ✅ |
| 8 | d20_8 | 4.56 | 187 | ✅ |
| 9 | d20_9 | 4.75 | 187 | ✅ |
| 10 | d20_10 | 4.81 | 243 | ✅ |
| 11 | d20_11 | 4.66 | 283 | ✅ |
| 12 | d20_12 | 4.56 | 283 | ✅ |
| 13 | d20_13 | 4.80 | 283 | ✅ |
| 14 | d20_14 | 4.61 | 203 | ✅ |
| 15 | d20_15 | 4.62 | 323 | ✅ |
| 16 | d20_16 | 4.60 | 283 | ✅ |
| 17 | d20_17 | 4.81 | 323 | ✅ |
| 18 | d20_18 | 4.66 | 323 | ✅ |
| 19 | d20_19 | 4.64 | 283 | ✅ |
| 20 | d20_20 | 4.74 | 323 | ✅ |
| 21 | d20_21 | 4.69 | 323 | ✅ |
| 22 | d20_22 | 4.85 | 323 | ✅ |
| 23 | d20_23 | 3.87 | 323 | ✅ |
Plot Measured vs Calculated¶
last_expt_name = project.experiments.names[-1]
project.plot_meas_vs_calc(expt_name=last_expt_name, show_residual=True)
Plot Parameter Evolution¶
Define the quantity to use as the x-axis in the following plots.
temperature = project.experiments[0].diffrn.ambient_temperature
Plot unit cell parameters vs. temperature.
project.plot_param_series(structure.cell.length_a, versus=temperature)
project.plot_param_series(structure.cell.length_b, versus=temperature)
project.plot_param_series(structure.cell.length_c, versus=temperature)
Plot isotropic displacement parameters vs. temperature.
project.plot_param_series(structure.atom_sites['Co1'].b_iso, versus=temperature)
project.plot_param_series(structure.atom_sites['Si'].b_iso, versus=temperature)
project.plot_param_series(structure.atom_sites['O1'].b_iso, versus=temperature)
project.plot_param_series(structure.atom_sites['O2'].b_iso, versus=temperature)
project.plot_param_series(structure.atom_sites['O3'].b_iso, versus=temperature)
Plot selected fractional coordinates vs. temperature.
project.plot_param_series(structure.atom_sites['Co2'].fract_x, versus=temperature)
project.plot_param_series(structure.atom_sites['Co2'].fract_z, versus=temperature)
project.plot_param_series(structure.atom_sites['O1'].fract_z, versus=temperature)
project.plot_param_series(structure.atom_sites['O2'].fract_z, versus=temperature)
project.plot_param_series(structure.atom_sites['O3'].fract_z, versus=temperature)