Skip to content

Commit 062a1f2

Browse files
committed
doc: add first tutorial to doc
1 parent 0689c55 commit 062a1f2

File tree

1 file changed

+225
-1
lines changed

1 file changed

+225
-1
lines changed

doc/source/tutorials/index.rst

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,228 @@
11
Tutorials
22
=========
33

4-
Examples usage of ``diffpy.cmi`` can be found at `this GitHub repo <https://github.com/diffpy/add2019-diffpy-cmi>`_. Tutorials coming soon...
4+
The atomic pair distribution function (PDF) provides a powerful route to understanding the local atomic structure of materials in real space.
5+
Despite its utility, PDF fitting and analysis can be challenging.
6+
The process of understand PDFs requires time, care, and the development of intuition to connect structural models to experimental data.
7+
Hopefully by the end of this tutorial series, PDF fitting will go from being a mysterious black box to a powerful tool in your structural analysis.
8+
9+
Example usage of ``diffpy.cmi`` can be found at `this GitHub repo <https://github.com/diffpy/pdfttp_data>`_.
10+
11+
.. _structural-parameters:
12+
13+
Structural parameters
14+
---------------------
15+
16+
Before we start fitting PDFs, lets first understand the parameters that will be refined on and what they mean.
17+
It is important to understand these parameters as they will help you gain better insight into the fitting process and
18+
how to interpret the results.
19+
20+
.. include:: ../snippets/structure_params.rst
21+
22+
.. _dataset-parameters:
23+
24+
Dataset parameters
25+
------------------
26+
27+
.. include:: ../snippets/data_params.rst
28+
29+
30+
Bulk Ni PDF fitting
31+
-------------------
32+
Although it is easier (and recommended) to fit PDFs of bulk homogeneous materials in `PDFgui <https://github.com/diffpy/diffpy.pdfgui>`_,
33+
fitting a bulk PDF in ``diffpy.cmi`` will provide you with a deeper insight into the fitting process. Additionally, this pedagogical
34+
example will help you build up confidence in using ``diffpy.cmi``.
35+
36+
For bulk PDFs, we highly recommend using PDFgui. PDFgui is a great stepping stone before using ``diffpy.cmi``.
37+
If you don't know how to use PDFgui, please see this
38+
`PDFgui tutorial <https://www.diffpy.org/diffpy.pdfgui/tutorial.html#lesson-1-creating-simple-fit-of-ni-pdf>`_
39+
to fit the same Ni PDF with PDFgui.
40+
41+
.. admonition:: Data Download
42+
43+
First, visit `this link <https://github.com/Billingegroup/pdfttp_data/raw/main/ch03NiModelling/data/Ni.cif>`_ and download the files for this tutorial:
44+
45+
- ``Ni.cif``
46+
- ``Ni.gr``
47+
48+
1. To start, we must first import all necessary modules and packages.
49+
50+
.. code-block:: python
51+
52+
import matplotlib.pyplot as plt
53+
import numpy as np
54+
55+
from diffpy.utils.parsers.loaddata import loadData
56+
from diffpy.structure import loadStructure
57+
from diffpy.srfit.fitbase import FitContribution, FitRecipe
58+
from diffpy.srfit.fitbase import Profile
59+
from diffpy.srfit.pdf import PDFParser, PDFGenerator
60+
from diffpy.structure.parsers import getParser
61+
from diffpy.srfit.structure import constrainAsSpaceGroup
62+
63+
2. As a sanity check, lets load the cif and PDF data to see what they look like.
64+
65+
.. code-block:: python
66+
67+
stru_path = "~/path/to/data/Ni.cif"
68+
data_path = "~/path/to/data/Ni.gr"
69+
# Load the structure and PDF data
70+
structure = loadStructure(stru_path)
71+
pdf_data = loadData(data_path)
72+
print("Ni cif file:")
73+
print(structure)
74+
75+
r = pdf_data[:, 0]
76+
g = pdf_data[:, 1]
77+
plt.plot(r, g, label="PDF Data")
78+
plt.xlabel("r (Angstrom)")
79+
plt.ylabel("G(r)")
80+
plt.title("Ni PDF Data")
81+
plt.legend()
82+
plt.show()
83+
84+
3. Once you see the data and have a feel for it, we can begin the fitting process. We will first define
85+
the :ref:`dataset-parameters` and :ref:`Structural parameters <structural-parameters>`. These values
86+
will be used to initialize our fit. Given this data, these values are reasonable starting points.
87+
88+
.. code-block:: python
89+
90+
PDF_RMIN = 1.5
91+
PDF_RMAX = 50
92+
PDF_RSTEP = 0.01
93+
QMAX = 25
94+
QMIN = 0.1
95+
CUBICLAT_I = 3.52
96+
SCALE_I = 0.4
97+
UISO_I = 0.005
98+
DELTA2_I = 2
99+
QDAMP_I = 0.04
100+
QBROAD_I = 0.02
101+
102+
4. Now, we will define a function called ``make_recipe``.
103+
This function will essentially tell the fit what to do. It contains all the necessary parameters and
104+
contributions for the PDF fitting process. Because looking at a function with a lot of code is scary,
105+
here is a gist of what the function does.
106+
107+
108+
.. image:: ../img/makerecipe_flow.png
109+
:alt: codecov-in-pr-comment
110+
:width: 210px
111+
:align: right
112+
113+
- **Parses PDF data:** Given the PDF data file it extracts the Q_min and Q_max values.
114+
- **Parses cif file:** Given the cif file it extracts the structure and space group information.
115+
- **Simulates a PDF:** Given the structure, it computes a simulated PDF over predefined r-range.
116+
- **Creates a Fit Contribution:** This will hold the simulated PDF and the experimental PDF.
117+
- **Creates a Fit Recipe:** This is the main recipe that combines all contributions and parameters for the fit.
118+
- **Refines params based on Fit Recipe:** When the function is called, the parameters defined in the fit recipe are refined to best fit the data.
119+
120+
121+
122+
.. code-block:: python
123+
124+
def make_recipe(cif_path, dat_path):
125+
126+
# 1. Get structural info
127+
p_cif = getParser('cif') # Get the parser for CIF files
128+
stru = p_cif.parseFile(cif_path) # Using the parser, load the structure from the CIF
129+
sg = p_cif.spacegroup.short_name # Get the space group to constrain the fit later on
130+
131+
# 2. Get PDF data
132+
profile = Profile() # Create a Profile object to hold PDF data and metadata
133+
parser = PDFParser() # Create a PDFParser to get the appropriate Q_min and Q_max
134+
parser.parseFile(dat_path) # Parse the PDF data file
135+
profile.loadParsedData(parser) # Load the parsed PDF data into the profile
136+
profile.setCalculationRange(
137+
xmin=PDF_RMIN, xmax=PDF_RMAX, dx=PDF_RSTEP
138+
) # Set the calculation range for the PDF fit
139+
140+
# 3. Create a PDFGenerator to generate a simulated PDF from the structure
141+
genpdf = PDFGenerator("generatedPDF") # Give the generator a name
142+
genpdf.setStructure(stru, periodic=True) # Give the generator the structure
143+
144+
# 4. Create a Fit Contribution object
145+
contribution = FitContribution("niPDFfit") # Give the contribution a name
146+
contribution.addProfileGenerator(genpdf) # Add the PDFGenerator to the contribution
147+
148+
# 5. Add the Profile to the contribution
149+
contribution.setProfile(profile, xname="r") # Add the Profile to the contribution
150+
151+
# 6. Set the equation used to combine the simulated PDF with the experimental PDF
152+
contribution.setEquation("s1*generatedPDF") # scaling factor for the simulated PDF
153+
154+
# 7. Create a Fit Recipe which turns our physics model into a mathematical recipe
155+
recipe = FitRecipe()
156+
recipe.addContribution(contribution) # Add the contribution to the recipe
157+
158+
# 8. Initialize the experimental parameters
159+
recipe.niPDFfit.generatedPDF.qdamp.value = QDAMP_I
160+
recipe.niPDFfit.generatedPDF.qbroad.value = QBROAD_I
161+
recipe.niPDFfit.generatedPDF.setQmin(QMIN)
162+
recipe.niPDFfit.generatedPDF.setQmax(QMAX)
163+
164+
# 9. Add scaling factor to the recipe
165+
recipe.addVar(contribution.s1, value=SCALE_I, tag="scale")
166+
167+
# 10. Add structural params to recipe, constraining them to the space group
168+
spacegroupparams = constrainAsSpaceGroup(genpdf.phase, sg) # Extract SG constraints
169+
for par in spacegroupparams.latpars: # Iterate over constrained lattice parameters
170+
recipe.addVar(par,
171+
value=CUBICLAT_I,
172+
name="fcc_lat",
173+
tag="lat")
174+
for par in spacegroupparams.adppars: # Iterate over constrained ADPs
175+
recipe.addVar(par,
176+
value=UISO_I,
177+
fixed=False,
178+
name="fcc_ADP",
179+
tag="adp")
180+
recipe.addVar(genpdf.delta2,
181+
name="Ni_Delta2",
182+
value=DELTA2_I,
183+
tag="d2") # Add delta2 parameter for PDF peak broadening
184+
185+
# 11. Add instrumental Qdamp and Qbroad parameters to the recipe
186+
recipe.addVar(genpdf.qdamp,
187+
fixed=False,
188+
name="Calib_Qdamp",
189+
value=QDAMP_I,
190+
tag="inst")
191+
recipe.addVar(genpdf.qbroad,
192+
fixed=False,
193+
name="Calib_Qbroad",
194+
value=QBROAD_I,
195+
tag="inst")
196+
return recipe
197+
198+
5. Now we can call ``make_recipe`` and plot the fit, experimental, and difference curves to evaluate how well the data fit.
199+
200+
.. code-block:: python
201+
202+
recipe = make_recipe(cif_path, dat_path) # Call the function to create the fit recipe
203+
recipe()
204+
r = recipe.pdfcontrib.profile.x
205+
g = recipe.pdfcontrib.profile.y
206+
gcalc = recipe.pdfcontrib.profile.ycalc
207+
diffzero = -0.65 * max(g) * np.ones_like(g) # offset the difference curve
208+
diff = g - gcalc + diffzero
209+
210+
plt.figure(figsize=(15, 6))
211+
plt.scatter(r, g, label="Experimental PDF")
212+
plt.plot(r, gcalc, label="Fitted PDF")
213+
plt.plot(r, diff, label="Difference")
214+
215+
plt.xlabel("r (Angstrom)")
216+
plt.ylabel("G(r)")
217+
plt.title("Ni PDF Fit")
218+
plt.legend()
219+
plt.show()
220+
221+
Once you run the above code, you should see a plot similar to the one below.
222+
223+
.. image:: ../img/Ni_Fit.png
224+
:alt: codecov-in-pr-comment
225+
:width: 700px
226+
:align: center
227+
228+
Congratulations! You have successfully fit a bulk Ni PDF using ``diffpy.cmi``. You are now one step closer to becoming a PDF fitting expert.

0 commit comments

Comments
 (0)