Polarized reflectometry without analyzer

Starting from the spin-flip tutorial, we want to remove the analyzer and perform a reflectivity simulation with incident polarized neutrons and no polarization analysis of the reflected beam. All other parameters of the simulation remain unchanged.

Omitting the polarization analysis corresponds to summing up the spin-flip and non-spin-flip channels for both incident polarization states: $$I^+ = I^{++} + I^{+-} \hspace{2em} I^- = I^{- -} + I^{-+}$$

In a script, these two polarization measurements can be computed according to:

r_plus  = results_pp + results_pm
r_minus = results_mm + results_mp

Here, results_pp, results_mm are the reflectivities in the non-spin-flip channels computed by calling

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

and the spin-flip channels are computed by

q, results_pm = run_simulation(ba.kvector_t(0,  1, 0),
                               ba.kvector_t(0, -1, 0))
q, results_mp = run_simulation(ba.kvector_t(0, -1, 0),
                               ba.kvector_t(0,  1, 0))

However, this approach has the disadvantage that it requires to perform four simulations in order to compute the two reflectivity curves. Instead, BornAgain supports the direct computation of the two channels for incident polarized neutrons without polarization analysis. This can simply be achieved by not specifying any analyzer when setting up the simulation object and hence omitting the line

simulation.setAnalyzerProperties(analyzer, 1.0, 0.5)

In this example, this is achieved by calling the run_simulation function for up and down polarization of the incident neutron beam as follows:

q, results_p = run_simulation(ba.kvector_t(0,  1, 0))
q, results_m = run_simulation(ba.kvector_t(0, -1, 0))

The attached script computes the two channels with both approaches and plots the results. It is then easy to check that they are identical. This is the resulting plot that shows the computed reflectivity for up and down polarization of the incident neutrons:

Reflectivity

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
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
"""
An example of computing splin-flip reflectivity from
a magnetized sample.
"""
import numpy
import matplotlib.pyplot as plt

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


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

    # Define materials
    material_Ambient = ba.MaterialBySLD("Ambient", 0.0, 0.0)
    magnetic_field = kvector_t(50000000, 86602540.3784, 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.0, 1.0, 0.0), analyzer=None):
    """
    Runs simulation and returns its result.
    """
    sample = get_sample()
    simulation = get_simulation(sample)

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

    simulation.runSimulation()
    result = simulation.result()
    return result.axis(), result.array()


def plot(axis, data, labels):

    plt.figure()
    for d, l in zip(data, labels):
        plt.semilogy(axis, d, 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()


if __name__ == '__main__':
    q, results_pp = run_simulation(ba.kvector_t(0, 1, 0), ba.kvector_t(0, 1, 0))
    q, results_mm = run_simulation(ba.kvector_t(0, -1, 0),
                                   ba.kvector_t(0, -1, 0))

    q, results_pm = run_simulation(ba.kvector_t(0, 1, 0),
                                   ba.kvector_t(0, -1, 0))
    q, results_mp = run_simulation(ba.kvector_t(0, -1, 0),
                                   ba.kvector_t(0, 1, 0))

    r_plus = results_pp + results_pm
    r_minus = results_mm + results_mp
    plot(q, [r_plus, r_minus], ["$+$", "$-$"])

    # same result, but need half the computational time
    q, results_p = run_simulation(ba.kvector_t(0, 1, 0))
    q, results_m = run_simulation(ba.kvector_t(0, -1, 0))

    plot(q, [results_p, results_m], ["$+$", "$-$"])

    plt.show()
PolarizedNoAnalyzer.py