Polarized SANS

This example compares all four polarization channels of SANS from concentric magnetic core-shell particles in a solvent.

The homogeneous solvent is represented by two layers of the same material, so their interface has no optical contrast. This gives the Born-approximation limit without reflected-wave effects from a substrate.

The particle magnetization defines the positive spin direction along the detector’s horizontal axis. The first sign in each panel denotes the incident polarization and the second sign denotes the analyzed final polarization. Accordingly, ++ and -- are non-spin-flip channels, while +- and -+ are spin-flip channels.

The two non-spin-flip patterns differ because the nuclear-magnetic interference changes sign. The two spin-flip patterns coincide for this non-chiral sample and show the anisotropy of magnetic scattering, including a zero at the detector center.

 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
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = ["bornagain>=25,<26"]
# ///
"""
Polarized SANS from magnetic core-shell particles in four spin channels.
"""
import bornagain as ba
ba.require_versions("bornagain>=25,<26")
from bornagain import ba_plot as bp, deg, nm, nm2, R3


def get_sample():
    """
    A sample with a magnetic core-shell particle in a solvent.
    """

    # Materials
    magnetization = R3(0, 1e6, 0)  # (A/m)
    core_color = (0.86, 0.24, 0.18)
    core_mat = ba.RefractiveMaterial("Core", core_color, 6e-06, 2e-08, magnetization)
    shell_color = (0.25, 0.65, 0.35)
    shell_mat = ba.RefractiveMaterial("Shell", shell_color, 1e-07, 2e-08)
    solvent_color = (0.90, 0.93, 0.97)
    solvent_mat = ba.RefractiveMaterial("Solvent", solvent_color, 5e-06, 0)

    # Core-shell particle
    core_radius = 10*nm
    shell_radius = 12*nm
    core = ba.Particle(core_mat, ba.Sphere(core_radius))
    core.translate(R3(0, 0, shell_radius - core_radius))
    shell = ba.Particle(shell_mat, ba.Sphere(shell_radius))
    particle = ba.CoreAndShell(core, shell)

    # Layers: two solvent layers, particles deposited at their interface
    layer_top = ba.Layer(solvent_mat)
    layer_bottom = ba.Layer(solvent_mat)
    layer_top.deposit2D(ba.Dilute2D(0.001/nm2, particle))

    # Sample
    sample = ba.Sample()
    sample.addLayer(layer_top)
    sample.addLayer(layer_bottom)

    return sample


def get_simulation(sample, polarizer, analyzer):
    """
    A SANS simulation for one polarization channel.
    """
    n = 200

    # Beam nearly parallel to the sample plane:
    beam = ba.Beam(1e9, 0.4*nm, 0.001*deg)

    # Detector opposite to source:
    detector = ba.SphericalDetector(n, -7*deg, 7*deg, n, -7*deg, 7*deg)

    beam.setPolarization(polarizer)
    detector.setAnalyzer(analyzer)

    simulation = ba.ScatteringSimulation(beam, sample, detector)
    # SANS form-factor scattering in solvent, not an averaged decorated layer.
    simulation.options().setUseAvgMaterials(False)
    return simulation


def simulate(polarizer, analyzer, title):
    """
    Runs one polarization channel.
    """
    sample = get_sample()
    result = get_simulation(sample, polarizer, analyzer).simulate()
    result.setTitle(title)
    return result


if __name__ == '__main__':
    spin_up = R3(0, 1, 0)
    spin_down = -spin_up
    channels = [
        ("$++$", spin_up, spin_up),
        ("$+-$", spin_up, spin_down),
        ("$-+$", spin_down, spin_up),
        ("$--$", spin_down, spin_down),
    ]
    results = [
        simulate(polarizer, analyzer, title)
        for title, polarizer, analyzer in channels
    ]

    ba.showSample3D(get_sample(), sample_size=120*nm, seed=0)
    bp.plot2d_to_grid(results, 2, unit_aspect=1)
    bp.plt.show()
auto/Examples/scatter2d/PolarizedSANS.py