This tutorial describes how to use the muesr API to obtained the local magnetic field at a specific interstitial muon site in a magnetic sample.

To proficiently use muesr a basic knowledge of python is strongly suggested. There are plenty of tutorial out there in the web, pick one and get familiar with the basic python syntax before going forward.

An interactive shell like ipython or jupyter can help a lot but it is not needed.

To be pedantic, you can

  1. Run the commands listed below in an ipython console
  2. Write these commands in a example.py file and run it with python
  3. Use Mantid, that contains a muesr library

First steps with muesr

Find below a tutorial description of the main functions. An alternative way to familiarise with muer is to run directly the Examples first and then come back here for a more systematic introduction.

Defining the sample

The fundamental component of Muesr is the Sample object. You can import and initialize it like this:

>>> from muesr.core import Sample
>>> mysample = Sample()

Specifying the lattice structure

The first thing that must be defined in a sample object is the lattice structure and the atomic positions. Muesr uses a custom version of the ASE Atoms class to do so. The following code defines and add the the lattice structure of simple cubic Iron to the sample object:

>>> import numpy
>>> from muesr.core.atoms import Atoms
>>> atms = Atoms(symbols=['Fe'], scaled_positions=[[0.,0.,0.]], cell=numpy.diag([3.,3.,3.]), pbc=True)
>>> mysample.cell = atms

However this procedure is quite tedious and error prone so it is much better to use builtin functions to parse crystallographic files.

At the moment muesr can parse crystallographic data from CIF (cif) files and XCrysDen (xsf) files. Here’s an example:

>>> # load data from XCrysden *.xsf file
>>> from muesr.i_o import load_xsf
>>> load_xsf(mysample, "/path/to/file.xsf")
>>> # load data from *.cif file
>>> from muesr.i_o import load_cif
>>> load_cif(mysample, "/path/to/file.cif")

The load_cif() function will also load symmetry information. Please note that only a single lattice structure at a time can be defined so each load function will remove the previous lattice structure definition.

Setting muon positions

When the lattice structure is defined it is possible to specify the muon position and the magnetic orders.

To specify the muon position, just do:

>>> mysample.add_muon([0.1,0,0])

positions are assumed to be in fractional coordinates. If Cartesian coordinates are needed, they can be specified as

>>> mysample.add_muon([0.3,0,0], cartesian=True)

You can verify that the two positions are equivalent by printing them with the command

>>> print(mysample.muons)
[array([ 0.1,  0. ,  0. ]), array([ 0.1,  0. ,  0. ])]

If symmetry information are present in the sample definition, it symmetry equivalent muon sites can be obtained. This can be done with the utility function muon_find_equiv(). In our case we did not load any symmetry information so the following command will raise an error. You can check that by doing

>>> from muesr.utilities import muon_find_equiv
>>> muon_find_equiv(mysample)
SymmetryError: Symmetry is not defined.

Defining a magnetic structure

The next step is the definition of a magnetic structure. To do so one must specify the propagation vector and the Fourier components and, optionally, the phases. A quick way to do that is using the helper function mago_add() from ms.

>>> from muesr.utilities.ms import mago_add
>>> mago_add(mysample)

You will be asked the propagation vector and the Fourier coefficients for the specified atomic symbol. By default the Fourier components are specified in Cartesian coordinates. You can use the keyword argument inputConvention to change this behavior (see mago_add() documentation for more info). Here’s an example:

>>> mago_add(a)
   Propagation vector (w.r.t. conv. rec. cell): 0 0 0
   Magnetic moments in Bohr magnetons and Cartesian coordinates.
   Which atom? (enter for all)Fe
   Lattice vectors:
       a    3.000000000000000    0.000000000000000    0.000000000000000
       b    0.000000000000000    3.000000000000000    0.000000000000000
       c    0.000000000000000    0.000000000000000    3.000000000000000
   Atomic positions (fractional):
       1 Fe  0.00000000000000  0.00000000000000  0.00000000000000  63.546
   FC for atom 1 Fe (3 real, [3 imag]): 0 0 1

The same can be achieved without interactive input like this:

>>> mysample.new_mm()
>>> mysample.mm.k = numpy.array([ 0.,  0.,  0.])
>>> mysample.mm.fc = numpy.array([[ 0.+0.j,  0.+0.j,  1.+0.j]])
>>> mysample.mm.desc = "FM m//c"


In this method each atom must have a Fourier component! For a 8 atoms unit cell the numpy array specifying the value must be a 8 x 3 complex array!

It is possible to specify multiple magnetic structure for the same lattice structure. Each time a new magnetic structure is added to the sample object it is immediately selected for the later operations. The currently selected magnetic order can be checked with the following command:

>>> print(mysample)
Sample status:

Crystal structure:           Yes
Magnetic structure:          Yes
Muon position(s):            2 site(s)
Symmetry data:               No

Magnetic orders available ('*' means selected)

 Idx | Sel | Desc.
  0  |     | No title
  1  |  *  | FM m//c

Checking the magnetic structure

The magnetic structures already defined can be visualized with the XCrysDen software.

>>> from muesr.utilities import show_structure
>>> show_structure(mysample)

the interactive session will block until XCrysDen is in execution. To show the local moments on Iron atoms press the ‘f’ key or ‘Display -> Forces’.

XCrysden window showing Fe moments

To procede with the tutorial close the XCrysDen Window.

Evaluating the local field

Once you are done with the definition of the sample details it’s time to crunch some numbers! To evaluate the local fields at the muon site muesr uses a python extension written in C in order to get decent performances. You can load a simple wrapper to the extension as providing local fields with the following command

>>> from muesr.engines.clfc import locfield

A detailed description of the possible computations is given in the muLFC documentation.

Let’s go straight to the local field evaluation which is obtained by running the command:

>>> results = locfield(mysample, 'sum', [30, 30, 30] , 40)

The first argument is just the sample object that was just defined. The second and third argument respectively specify that a simple sum of all magnetic moments should be performed using a supercell obtained replicating 30x30x30 times the unit cell along the lattice vectors. The fourth argument is the radius of the Lorentz sphere considered. All magnetic moments outside the Lorentz sphere are ignored and the muon is automatically placed in the center of the supercell.


To get an estimate of the largest radius that you can use to avoid sampling outside the supercell size you can use the python function find_largest_sphere in the LFC python package.


If the Lorentz sphere does not fit into the supercell, the results obtained with this function are not accurate!

The results variable now contains a list of LocalField objects. However, if you print the results variable you’ll see something that looks like a numpy array:

>>> print(results)
[array([  3.83028907e-18,  -3.37919319e-18,  -3.42111893e+01]),
 array([  3.83028907e-18,  -3.37919319e-18,  -3.42111893e+01])]

these are the total field for the muon positions and the magnetic structure defined above. To access the various components you do:

>>> results[0].Lorentz
array([ 0.        ,  0.        ,  0.14355877])

>>> results[0].Dipolar
array([  3.83028907e-18,  -3.37919319e-18,  -3.43547481e+01])

>>> results[0].Contact
array([ 0.,  0.,  0.])

And you are done! Remember that all results are in Tesla units.

Saving for later use

The current sample definition can be stored in a file with the following command:

>>> from muesr.i_o import save_sample
>>> save_sample(mysample, '/path/to/mysample.yaml')

and later loaded with

>>> from muesr.i_o import load_sample
>>> mysample_again = load_sample('/path/to/mysample.yaml')