- Class structure of the mpdf package
- Simple example: Calculating the mPDF from a spin dimer
- Creating a magnetic structure from a CIF file using diffpy.Structure
- Creating a magnetic structure by defining your own unit cell
- Creating a magnetic structure with multiple species: Simple ferrimagnet
- Creating more complex magnetic structures: One-dimensional spin helix
- Exploring some of the additional parameters in the mPDF
- Simple example of an mPDF fit

_{min} and q_{max}. It requires a MagStructure object as input, from which it extracts the atomic positions and spin vectors to be used in calculating the mPDF. The mPDFcalculator class has options for calculating both the properly normalized and the unnormalized mPDF (the unnormalized mPDF is frequently what we obtain experimentally).

In [1]:

```
### Import the necessary libraries
import numpy as np
import matplotlib.pyplot as plt
from diffpy.mpdf import *
### Set all plots to be inline
%matplotlib notebook
### Create a MagStructure object
mstr = MagStructure()
### Create two atoms in the structure
mstr.atoms = np.array([[0,0,0],[4,0,0]])
### Create two spin vectors corresponding to the atoms. Let's make them antiferromagnetic.
S=np.array([0,0,1])
mstr.spins = np.array([S,-S])
### Create the MPDFcalculator object and load the magnetic structure into it
mc = MPDFcalculator(mstr)
### Calculate and plot the mPDF!
r,fr = mc.calc() # Use calc() if you want to extract the numerical results of the calculation
mc.plot() # Use plot() if you just want to plot the mPDF without extracting the numerical arrays.
```

Note that the negative peak at 4 Angstroms is due to the antiferromagnetic orientation of the spins. The sloping baseline at low-r is real and is a unique feature of the mPDF that is not found in the atomic PDF.

Just for fun, let's now make a copy of this structure but make the dimer ferromagnetic instead of antiferromagnetic.

In [2]:

```
### make a copy of the magnetic structure
mstr2 = mstr.copy()
### Set the spins to be ferromagnetic
mstr2.spins = np.array([S,S])
### Create another mPDF calculator
mc2 = MPDFcalculator(mstr2)
### Calculate the mPDF
r2, fr2 = mc2.calc()
### Compare the antiferromagnetic and ferromagnetic mPDFs
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(r,fr,'b-',label='Antiferro')
ax.plot(r2,fr2,'r-',label='Ferro')
ax.set_xlabel(r'r ($\AA$)')
ax.set_ylabel(r'f ($\AA^{-2}$)')
plt.legend()
plt.show()
```

In [3]:

```
### First we have to import the loadStructure function from diffpy.Structure
from diffpy.Structure import loadStructure
### Now let's create a diffpy.Structure object from a CIF file for MnO. This has all the atomic information,
### but none of the magnetic information.
mno = loadStructure('MnO_cubic.cif')
### As a refresher, let's take a look at the mno structure object.
mno
```

Out[3]:

In [4]:

```
### We will now create a MagSpecies object to extend the mno structure object.
mnoMag = MagSpecies()
### Load the mno structure and give the magnetic species a label
mnoMag.struc = mno
mnoMag.label = 'Mn2+'
### Now we need to tell it which atoms in MnO are magnetic. From the printed list in the previous cell,
### the magnetic Mn ions are the first four positions in the structure, so we provide the corresponding indices.
mnoMag.magIdxs = [0,1,2,3]
### Now we provide the magnetic propagation and basis vectors, which are known from previous neutron
### diffraction studies of MnO.
k = np.array([0.5,0.5,0.5])
s = np.array([1,-1,0]) # we won't worry about the magnitude of the basis vector for now
mnoMag.kvecs = np.array([k])
mnoMag.basisvecs = np.array([s])
### Now we provide information about the magnetic form factor. We tell the MagSpecies object the type of magnetic
### ion, and it looks up magnetic form factor in a table.
mnoMag.ffparamkey = 'Mn2'
```

In [5]:

```
### Now we can create a MagStructure object and load mnoMag into it.
mnoMagStruc = MagStructure()
mnoMagStruc.loadSpecies(mnoMag)
### Now we will generate the atomic positions and spins. It is important to do the atoms first, since the
### spins are generated by applying the propagation and basis vectors to the atomic positions. These methods
### use the information contained in the atomic and magnetic structures to generate arrays of atomic positions
### and spin vectors.
mnoMagStruc.makeAtoms()
mnoMagStruc.makeSpins()
### And we make the magnetic form factor:
mnoMagStruc.makeFF()
```

In [6]:

```
### Now we can create the mPDF calculator and load the magnetic structure
mc = MPDFcalculator()
mc.magstruc = mnoMagStruc
### Plot the mPDf
mc.plot()
```

In [7]:

```
### Create the magnetic species and turn off the diffpy.structure option.
mspec = MagSpecies(useDiffpyStruc=False)
### Define the lattice vectors of the unit cell. Let's make a cubic unit cell.
a = 4.0
mspec.latVecs = np.array([[a,0,0], [0,a,0], [0,0,a]])
### Define the positions of the magnetic atoms in the unit cell (in fractional coordinates). Let's make
### a body-centered structure.
mspec.atomBasis = np.array([[0,0,0], [0.5,0.5,0.5]])
### Define the magnetic moments in the same order as the list of atoms. Let's make an antiferromagnet.
mspec.spinBasis = np.array([[0,0,1], [0,0,-1]])
### Create the magnetic structure object and load mspec.
mstruc = MagStructure()
mstruc.loadSpecies(mspec)
mstruc.makeAtoms()
mstruc.makeSpins()
### Let's visualize the first unit cell to make sure we have what we expect.
visAtoms = np.array([[0,0,0],[a,0,0],[0,a,0],[0,0,a],[a,a,0],
[a,0,a],[0,a,a],[a,a,a],[0.5*a,0.5*a,0.5*a]])
visSpins = mstruc.spinsFromAtoms(visAtoms, fractional=False)
mstruc.visualize(visAtoms, visSpins)
```

In [8]:

```
### Now we can set up the MPDFcalculator and plot the mPDF.
mc = MPDFcalculator(mstruc)
mc.plot()
```

In [9]:

```
### Create the first magnetic species and turn off the diffpy.structure option.
mspec1 = MagSpecies(useDiffpyStruc=False)
### Define the lattice vectors of the unit cell. Let's make a cubic unit cell.
a = 4.0
mspec1.latVecs = np.array([[a,0,0],[0,a,0],[0,0,a]])
### Define the atomic position and magnetic moment.
mspec1.atomBasis = np.array([0,0,0])
mspec1.spinBasis = np.array([0,0,1])
mspec1.label = 'big' ### it is necessary to define unique identifying labels when you have multiple species
### Now make the other species, starting with mspec1 as a template
mspec2 = mspec1.copy()
mspec2.atomBasis = np.array([0.5,0.5,0.5])
mspec2.spinBasis = np.array([0,0,-0.5])
mspec2.label = 'small'
### Create the magnetic structure object and load the species.
mstruc = MagStructure()
mstruc.loadSpecies(mspec1)
mstruc.loadSpecies(mspec2)
mstruc.makeAll()
### Again, let's visualize the first unit cell to make sure we have what we expect.
visAtoms = np.array([[0,0,0],[a,0,0],[0,a,0],[0,0,a],[a,a,0],[a,0,a],[0,a,a],[a,a,a],[0.5*a,0.5*a,0.5*a]])
visSpins = mstruc.spinsFromAtoms(visAtoms, fractional=False)
mstruc.visualize(visAtoms, visSpins, showcrystalaxes=True, axesorigin=np.array([-1,-1,-1]))
```

In [10]:

```
### Now we can set up the MPDFcalculator and plot the mPDF.
mc = MPDFcalculator(mstruc)
### Important: since we have two different magnetic species, we must be sure that the calculation
### uses an equivalent number of spins from each species as the "center" of the calculation.
### We do this by changing the calcList attribute of the MPDFcalculator, which is a list of the
### indices of the atoms/spins to be used as the centers for the calculation. To find the starting
### index of each species, use the getSpeciesIdxs method on the magnetic structure.
mc.calcList = mstruc.getSpeciesIdxs().values()
print mc.calcList
### Now we can plot.
mc.plot()
```

In [11]:

```
### We will load in a structure from a CIF file and modify it to simulate a 1-D material.
astruc = loadStructure("MnO_cubic.cif")
astruc.lattice.a = 3.0
astruc.lattice.b = 150.0
astruc.lattice.c = 150.0
### Create the magnetic species object.
helix = MagSpecies(astruc)
### Set up the magnetic propagation and basis vectors for a helical spin configuration.
k = np.array([np.sqrt(2)/10,0,0]) # make the period incommensurate with the lattice
helix.kvecs=np.array([k, -k])
Sk = 0.5*(np.array([0,0,1])+0.5j*np.array([0,1,0])) # j is the numpy symbol for the imaginary unit
helix.basisvecs = np.array([Sk,Sk.conj()])
### Populate with atoms and spins.
helix.rmaxAtoms = 70.0
helix.makeAtoms()
helix.makeSpins()
helix.label = 'helix'
### Create the magnetic structure object.
mstruc = MagStructure()
mstruc.loadSpecies(helix)
#### Visualize the spins.
x,y,z = mstruc.atoms.transpose()
mask = np.logical_and(z==0,np.logical_and(y==0,np.abs(x)<30))
visatoms = mstruc.atoms[mask]
visspins = spinsFromAtoms(mstruc, visatoms, fractional=False)
mstruc.visualize(visatoms, visspins)
```

In [12]:

```
### Now plot the mPDF.
mc = MPDFcalculator(mstruc)
mc.rmax=70.0
mc.plot()
```

Now we will take a look at some of the other mPDF parameters that can be adjusted, including:

- Qmin and Qmax to simulate experimental conditions.
- Parameters that dampen and/or broaden the mPDF, simulating the effects of thermal motion and instrumental resolution.
- "Ordered" scale factor corresponding to the magnitude of the locally ordered moment, and "paramagnetic" scale factor which depends only on the magnitude and spatial extent of a single localized moment, not any correlations between moments.
- The r-range for the calculation.

For this, we will create the antiferromagnetic MnO structure again.

In [13]:

```
### Create the diffpy structure, MagSpecies, and MagStructure
mno = loadStructure('MnO_cubic.cif')
mnoMag = MagSpecies(mno, magIdxs=[0,1,2,3], label='Mn2+', ffparamkey='Mn2')
mnoMag.kvecs = np.array([0.5,0.5,0.5])
mnoMag.basisvecs = np.array([1,-1,0])
mnoMagStruc = MagStructure()
mnoMagStruc.loadSpecies(mnoMag)
mnoMagStruc.makeAll()
```

In [14]:

```
### Default mPDF calculator
mc = MPDFcalculator(mnoMagStruc)
r, fr, dr = mc.calc(both=True)
### Adjust qmin and qmax to simulate termination ripples
mcQ = mc.copy()
mcQ.qmin, mcQ.qmax = 0.1,25
rq,frq = mcQ.calc()
sclfactor = fr.max()/frq.max()
frq *= sclfactor
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(r, fr, 'b-', label='Default')
ax.plot(rq, frq, 'r-', label='With qmin/qmax')
ax.plot(r, frq-fr-7, 'g-', label='Difference')
ax.set_xlabel(r'r ($\AA$)')
ax.set_ylabel(r'f ($\AA^{-2}$)')
plt.legend(loc='best')
plt.show()
```