Structure Refinement: LBCO, HRPT¶
This basic example is designed to show how Rietveld refinement can be performed when both the crystal structure and experiment parameters are defined using CIF files.
For this example, constant-wavelength neutron powder diffraction data for La0.5Ba0.5CoO3 from HRPT at PSI is used.
The example is intended for users who are already familiar with the EasyDiffraction library and want to quickly get started with a basic refinement.
It is also useful for those who want to see how constraints can be applied to highly correlated parameters. For a more detailed explanation of the code, please refer to the other tutorials.
Import Library¶
In [2]:
Copied!
import easydiffraction as ed
import easydiffraction as ed
Step 1: Define Project¶
In [3]:
Copied!
# Create minimal project without name and description
project = ed.Project()
# Create minimal project without name and description
project = ed.Project()
Step 2: Define Crystal Structure¶
In [4]:
Copied!
# Download CIF file from repository
structure_path = ed.download_data(id=1, destination='data')
# Download CIF file from repository
structure_path = ed.download_data(id=1, destination='data')
Getting data...
Data #1: La0.5Ba0.5CoO3 (crystal structure)
✅ Data #1 downloaded to 'data/ed-1.cif'
In [5]:
Copied!
# Add structure from downloaded CIF
project.structures.add_from_cif_path(structure_path)
# Add structure from downloaded CIF
project.structures.add_from_cif_path(structure_path)
Step 3: Define Experiment¶
In [6]:
Copied!
# Download CIF file from repository
expt_path = ed.download_data(id=2, destination='data')
# Download CIF file from repository
expt_path = ed.download_data(id=2, destination='data')
Getting data...
Data #2: La0.5Ba0.5CoO3, HRPT (PSI), 300 K
✅ Data #2 downloaded to 'data/ed-2.cif'
In [7]:
Copied!
# Add experiment from downloaded CIF
project.experiments.add_from_cif_path(expt_path)
# Add experiment from downloaded CIF
project.experiments.add_from_cif_path(expt_path)
Step 4: Perform Analysis (no constraints)¶
In [8]:
Copied!
# Start refinement. All parameters, which have standard uncertainties
# in the input CIF files, are refined by default.
project.analysis.fit()
# Start refinement. All parameters, which have standard uncertainties
# in the input CIF files, are refined by default.
project.analysis.fit()
Standard fitting
📋 Using experiment 🔬 'hrpt' for 'single' fitting
🚀 Starting fit process with 'lmfit (leastsq)'...
📈 Goodness-of-fit (reduced χ²) change:
| iteration | χ² | improvement [%] | |
|---|---|---|---|
| 1 | 1 | 165.50 | |
| 2 | 28 | 33.67 | 79.7% ↓ |
| 3 | 45 | 10.85 | 67.8% ↓ |
| 4 | 63 | 6.43 | 40.7% ↓ |
| 5 | 81 | 3.33 | 48.2% ↓ |
| 6 | 98 | 2.23 | 33.2% ↓ |
| 7 | 116 | 1.91 | 14.5% ↓ |
| 8 | 133 | 1.50 | 21.1% ↓ |
| 9 | 150 | 1.45 | 3.6% ↓ |
| 10 | 167 | 1.34 | 7.7% ↓ |
| 11 | 185 | 1.29 | 3.4% ↓ |
| 12 | 276 | 1.29 |
🏆 Best goodness-of-fit (reduced χ²) is 1.29 at iteration 261
✅ Fitting complete.
In [9]:
Copied!
# Show fit results summary
project.analysis.display.fit_results()
# Show fit results summary
project.analysis.display.fit_results()
Fit results
✅ Success: True
⏱️ Fitting time: 16.34 seconds
📏 Goodness-of-fit (reduced χ²): 1.29
📏 R-factor (Rf): 5.62%
📏 R-factor squared (Rf²): 5.25%
📏 Weighted R-factor (wR): 4.40%
📈 Fitted parameters:
| datablock | category | entry | parameter | start | fitted | uncertainty | units | change | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | lbco | cell | length_a | 3.8800 | 3.8909 | 0.0000 | Å | 0.28 % ↑ | |
| 2 | lbco | atom_site | La | adp_iso | 0.5000 | 0.5051 | 6011.8674 | Ų | 1.02 % ↑ |
| 3 | lbco | atom_site | Ba | adp_iso | 0.5000 | 0.5052 | 9770.9689 | Ų | 1.04 % ↑ |
| 4 | lbco | atom_site | Co | adp_iso | 0.5000 | 0.2371 | 0.0611 | Ų | 52.59 % ↓ |
| 5 | lbco | atom_site | O | adp_iso | 0.5000 | 1.3935 | 0.0168 | Ų | 178.70 % ↑ |
| 6 | hrpt | linked_phases | lbco | scale | 10.0000 | 9.1349 | 0.0643 | 8.65 % ↓ | |
| 7 | hrpt | peak | broad_gauss_u | 0.1000 | 0.0816 | 0.0031 | deg² | 18.44 % ↓ | |
| 8 | hrpt | peak | broad_gauss_v | -0.1000 | -0.1159 | 0.0067 | deg² | 15.90 % ↑ | |
| 9 | hrpt | peak | broad_gauss_w | 0.1000 | 0.1204 | 0.0033 | deg² | 20.45 % ↑ | |
| 10 | hrpt | peak | broad_lorentz_y | 0.1000 | 0.0844 | 0.0021 | deg | 15.57 % ↓ | |
| 11 | hrpt | instrument | twotheta_offset | 0.6000 | 0.6226 | 0.0010 | deg | 3.76 % ↑ | |
| 12 | hrpt | background | 1 | y | 170.0000 | 168.4242 | 1.3977 | 0.93 % ↓ | |
| 13 | hrpt | background | 2 | y | 170.0000 | 164.3715 | 1.0025 | 3.31 % ↓ | |
| 14 | hrpt | background | 3 | y | 170.0000 | 166.8870 | 0.7391 | 1.83 % ↓ | |
| 15 | hrpt | background | 4 | y | 170.0000 | 175.3977 | 0.6586 | 3.18 % ↑ | |
| 16 | hrpt | background | 5 | y | 170.0000 | 174.3027 | 0.9113 | 2.53 % ↑ |
⚠️ Red uncertainty: exceeds the fitted value (consider adding constraints)
In [10]:
Copied!
# Show parameter correlations
project.plotter.plot_param_correlations()
# Show parameter correlations
project.plotter.plot_param_correlations()
Step 5: Perform Analysis (with constraints)¶
In [11]:
Copied!
# As can be seen from the parameter-correlation plot, the isotropic
# displacement parameters of La and Ba are highly correlated. Because
# La and Ba share the same mixed-occupancy site, their contributions to
# the neutron diffraction pattern are difficult to separate, especially
# since their coherent scattering lengths are not very different.
# Therefore, it is necessary to constrain them to be equal. First we
# define aliases and then use them to create a constraint.
project.analysis.aliases.create(
label='biso_La',
param=project.structures['lbco'].atom_sites['La'].adp_iso,
)
project.analysis.aliases.create(
label='biso_Ba',
param=project.structures['lbco'].atom_sites['Ba'].adp_iso,
)
project.analysis.constraints.create(expression='biso_Ba = biso_La')
# As can be seen from the parameter-correlation plot, the isotropic
# displacement parameters of La and Ba are highly correlated. Because
# La and Ba share the same mixed-occupancy site, their contributions to
# the neutron diffraction pattern are difficult to separate, especially
# since their coherent scattering lengths are not very different.
# Therefore, it is necessary to constrain them to be equal. First we
# define aliases and then use them to create a constraint.
project.analysis.aliases.create(
label='biso_La',
param=project.structures['lbco'].atom_sites['La'].adp_iso,
)
project.analysis.aliases.create(
label='biso_Ba',
param=project.structures['lbco'].atom_sites['Ba'].adp_iso,
)
project.analysis.constraints.create(expression='biso_Ba = biso_La')
In [12]:
Copied!
# Start refinement. All parameters, which have standard uncertainties
# in the input CIF files, are refined by default.
project.analysis.fit()
# Start refinement. All parameters, which have standard uncertainties
# in the input CIF files, are refined by default.
project.analysis.fit()
Standard fitting
📋 Using experiment 🔬 'hrpt' for 'single' fitting
🚀 Starting fit process with 'lmfit (leastsq)'...
📈 Goodness-of-fit (reduced χ²) change:
| iteration | χ² | improvement [%] | |
|---|---|---|---|
| 1 | 1 | 1.29 | |
| 2 | 20 | 1.29 |
🏆 Best goodness-of-fit (reduced χ²) is 1.29 at iteration 19
✅ Fitting complete.
In [13]:
Copied!
# Show fit results summary
project.analysis.display.fit_results()
# Show fit results summary
project.analysis.display.fit_results()
Fit results
✅ Success: True
⏱️ Fitting time: 1.12 seconds
📏 Goodness-of-fit (reduced χ²): 1.29
📏 R-factor (Rf): 5.62%
📏 R-factor squared (Rf²): 5.25%
📏 Weighted R-factor (wR): 4.40%
📈 Fitted parameters:
| datablock | category | entry | parameter | start | fitted | uncertainty | units | change | |
|---|---|---|---|---|---|---|---|---|---|
| 1 | lbco | cell | length_a | 3.8909 | 3.8909 | 0.0000 | Å | 0.00 % ↑ | |
| 2 | lbco | atom_site | La | adp_iso | 0.5051 | 0.5051 | 0.0278 | Ų | 0.00 % ↑ |
| 3 | lbco | atom_site | Co | adp_iso | 0.2371 | 0.2371 | 0.0564 | Ų | 0.00 % ↓ |
| 4 | lbco | atom_site | O | adp_iso | 1.3935 | 1.3935 | 0.0160 | Ų | 0.00 % ↑ |
| 5 | hrpt | linked_phases | lbco | scale | 9.1349 | 9.1349 | 0.0538 | 0.00 % ↓ | |
| 6 | hrpt | peak | broad_gauss_u | 0.0816 | 0.0816 | 0.0031 | deg² | 0.00 % ↑ | |
| 7 | hrpt | peak | broad_gauss_v | -0.1159 | -0.1159 | 0.0066 | deg² | 0.01 % ↑ | |
| 8 | hrpt | peak | broad_gauss_w | 0.1204 | 0.1204 | 0.0032 | deg² | 0.00 % ↑ | |
| 9 | hrpt | peak | broad_lorentz_y | 0.0844 | 0.0844 | 0.0021 | deg | 0.00 % ↓ | |
| 10 | hrpt | instrument | twotheta_offset | 0.6226 | 0.6226 | 0.0010 | deg | 0.00 % ↑ | |
| 11 | hrpt | background | 1 | y | 168.4242 | 168.4242 | 1.3974 | 0.00 % ↑ | |
| 12 | hrpt | background | 2 | y | 164.3715 | 164.3715 | 1.0023 | 0.00 % ↑ | |
| 13 | hrpt | background | 3 | y | 166.8870 | 166.8871 | 0.7388 | 0.00 % ↑ | |
| 14 | hrpt | background | 4 | y | 175.3977 | 175.3978 | 0.6490 | 0.00 % ↑ | |
| 15 | hrpt | background | 5 | y | 174.3027 | 174.3026 | 0.8956 | 0.00 % ↓ |
In [14]:
Copied!
# Show parameter correlations
project.plotter.plot_param_correlations()
# Show parameter correlations
project.plotter.plot_param_correlations()
In [15]:
Copied!
# Show defined experiment names
project.experiments.show_names()
# Show defined experiment names
project.experiments.show_names()
Defined experiments 🔬
['hrpt']
In [16]:
Copied!
# Plot measured vs. calculated diffraction patterns
project.plotter.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)
# Plot measured vs. calculated diffraction patterns
project.plotter.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)