Specular reflectometry with polarized neutrons and a magnetic sample

This example shows how to perform a specular reflectometry simulation with polarized neutrons. We show this for a sample that contains only a single magnetic layer on top of a substrate. The magnetization and polarization are chosen to be (anti-)parallel, and hence, no spin-flip is observed. By rotation of either the magnetization or the polarization vector, one can generalize this example to observe also the spin-flip channels.

The construction of the sample is almost identical to the unpolarized cases, where the only difference is that the magnetization of a material can be specified:

m_layer_mat = ba.MaterialBySLD("Layer", 1e-4, 1e-8,
                               ba.kvector_t(0, 1e8, 0))
Here, we create a material with the usual specification of a name and the real and complex parts of the SLD. In addition, the last argument defines the magnetization vector. For this example, we chose a magnetization of magnitude $10^8 \text{A/m}$ along the positive $y$-axis.

In contrast to a normal unpolarized computation, the incoming beam polarization as well as the analyzer must be specified when creating the simulation. The polarization state of the incoming beam can be specified as follows:

polarization = ba.kvector_t(0, 1, 0)
simulation.setBeamPolarization(polarization)
Its polarization direction is specified as a regular Bloch vector on or inside the unit sphere. Magnitudes of the Bloch vector smaller than one denote non-perfect polarization.

The analyzer properties are similarly set:

analyzer = ba.kvector_t(0, 1, 0)
simulation.setAnalyzerProperties(analyzer, 1.0, 0.5)
The first argument specifies the direction of the analyzer, again by providing a Bloch vector with unit length. The second argument specifies the efficiency (magnitude of the Bloch vector). The third argument is the transmission of the analyzer, here $0.5$ corresponds to a perfect analyzer: half of an unpolarized incoming beam is not transmitted. Therefore, this example corresponds to a perfect analyzer that is configured to probe the up-up channel.

In this example, setting up the beam polarization and the analyzer is done in the run_simulation function. In order to simulate the two non-spin-flip channels (up-up and down-down), we have to perform the two calls

results_pp = run_simulation(ba.kvector_t(0,  1, 0),
                            ba.kvector_t(0,  1, 0))
results_mm = run_simulation(ba.kvector_t(0, -1, 0),
                            ba.kvector_t(0, -1, 0))

The resulting reflectivities are shown in this plot:

Reflectivity

As expected, we find the birefringent behavior and two different critical angles. Since the magnetization of the layer is parallel to the neutron polarization, no spin-flip is observed. It is a good exercise to verify this claim by a short computation.

Here is the complete example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python3
"""
An example of computing reflectivity on a
magnetized sample.
"""

import bornagain as ba
from bornagain import angstrom, deg, nm, nm2, kvector_t

import matplotlib.pyplot as plt


def get_sample():
    """
    Defines sample and returns it
    """

    # Define materials
    material_Ambient = ba.MaterialBySLD("Ambient", 0.0, 0.0)
    magnetic_field = kvector_t(0, 100000000, 0)
    material_Layer = ba.MaterialBySLD("Layer", 0.0001, 1e-08, magnetic_field)
    material_Substrate = ba.MaterialBySLD("Substrate", 7e-05, 2e-06)

    # Define layers
    layer_1 = ba.Layer(material_Ambient)
    layer_2 = ba.Layer(material_Layer, 10.0*nm)
    layer_3 = ba.Layer(material_Substrate)

    # Define sample
    sample = ba.MultiLayer()
    sample.addLayer(layer_1)
    sample.addLayer(layer_2)
    sample.addLayer(layer_3)

    return sample


def get_simulation(sample, scan_size=500):
    """
    Defines and returns a specular simulation.
    """
    simulation = ba.SpecularSimulation()
    scan = ba.AngularSpecScan(1.54*angstrom, scan_size, 0.0*deg, 5.0*deg)
    simulation.setScan(scan)
    simulation.setSample(sample)
    return simulation


def run_simulation(polarization=ba.kvector_t(0, 1, 0),
                   analyzer=ba.kvector_t(0, 1, 0)):
    """
    Runs simulation and returns its result.
    """
    sample = get_sample()
    simulation = get_simulation(sample)

    # adding polarization and analyzer operator
    simulation.beam().setPolarization(polarization)
    simulation.setAnalyzerProperties(analyzer, 1.0, 0.5)

    simulation.runSimulation()
    return simulation.result()


def plot(data, labels):

    plt.figure()
    for d, l in zip(data, labels):
        plt.semilogy(d.axis(), d.array(), label=l, linewidth=1)

    plt.legend(loc='upper right')
    plt.gca().yaxis.set_ticks_position('both')
    plt.gca().xaxis.set_ticks_position('both')

    plt.xlabel(r"$\alpha_i$ [deg]")
    plt.ylabel("Reflectivity")

    plt.tight_layout()
    plt.show()


if __name__ == '__main__':
    results_pp = run_simulation(ba.kvector_t(0, 1, 0), ba.kvector_t(0, 1, 0))
    results_mm = run_simulation(ba.kvector_t(0, -1, 0), ba.kvector_t(0, -1, 0))
    plot([results_pp, results_mm], ["$++$", "$--$"])
BasicPolarizedReflectometry.py