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
#!/usr/bin/env python3
"""
An example of computing splin-flip reflectivity from
a magnetized sample.
"""
from math import sqrt
import matplotlib.pyplot as plt
import bornagain as ba
from bornagain import angstrom, ba_plot as bp, deg, nm, R3


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

    # Define materials
    material_Ambient = ba.MaterialBySLD("Ambient", 0, 0)
    h = 1e8
    magnetic_field = R3(1/2*h, sqrt(3)/2*h, 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*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):
    """
    Defines and returns a specular simulation.
    """


def run_simulation(polarizer_dir=R3(0, 1, 0), analyzer_dir=None):
    """
    Runs simulation and returns its result.
    """
    sample = get_sample()

    n = bp.simargs['n']
    scan = ba.AlphaScan(1.54*angstrom, n, 0, 5*deg)

    # adding polarizer and analyzer operator
    scan.setPolarization(polarizer_dir)
    if analyzer_dir:
        scan.setAnalyzer(analyzer_dir, 1, 0.5)

    simulation = ba.SpecularSimulation(scan, sample)

    result = simulation.simulate()

    return result.convertedBinCenters(), result.array()


def plot(axis, data, labels):

    plt.figure()
    plt.yscale('log')
    for d, l in zip(data, labels):
        plt.plot(axis, d, label=l, linewidth=1)

    plt.legend(loc='upper right')
    bp.inside_ticks()

    plt.xlabel(r"$\alpha_{\rm i} \;(^\circ)$")
    plt.ylabel("Reflectivity")

    plt.tight_layout()


if __name__ == '__main__':
    bp.parse_args(sim_n=500)

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

    q, results_pm = run_simulation(ba.R3(0, +1, 0), ba.R3(0, -1, 0))
    q, results_mp = run_simulation(ba.R3(0, -1, 0), ba.R3(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.R3(0, +1, 0))
    q, results_m = run_simulation(ba.R3(0, -1, 0))

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

    bp.show_or_export()
Examples/specular/PolarizedNoAnalyzer.py