Approximations

This example compares three size-distribution treatments for two cylinder sizes in radial-paracrystal order. The local monodisperse approximation uses independent domains and sums their intensities incoherently. The decoupling approximation places both particle variants in one common structure without a size-dependent phase shift. The size-spacing coupling approximation adds that phase shift through the paracrystal’s kappa parameter.

The three panels use the same particle fractions and instrument, making the effect of the approximation visible directly in the scattering patterns.

Result

Approximations result

Sample

Approximations sample

Python script

  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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = ["bornagain>=25,<26"]
# ///
"""
Cylinders of two different sizes in Decoupling Approximation,
Local Monodisperse Approximation and Size-Spacing Coupling Approximation
"""
import bornagain as ba
ba.require_versions("bornagain>=25,<26")
from bornagain import deg, nm


def get_sample(approximation):
    """
    A sample with cylinders of two different sizes on a substrate
    in radial paracrystal ordering.
    """

    # Materials
    particle_color = (0.86, 0.24, 0.18)
    particle_mat = ba.RefractiveMaterial("Particle", particle_color, 0.0006, 2e-08)
    substrate_color = (0.28, 0.57, 0.82)
    substrate_mat = ba.RefractiveMaterial("Substrate", substrate_color, 6e-06, 2e-08)
    vacuum = ba.Vacuum()

    # Particles
    ff_1 = ba.Cylinder(5*nm, 5*nm)
    ff_2 = ba.Cylinder(8*nm, 8*nm)
    particle_1 = ba.Particle(particle_mat, ff_1)
    particle_2 = ba.Particle(particle_mat, ff_2)

    # Layers
    layer_1 = ba.Layer(vacuum)
    layer_2 = ba.Layer(substrate_mat)

    # Distribution function
    profile = ba.Profile1DGauss(3*nm)

    if (not approximation=='LMA' and
        not approximation=='DA' and
        not approximation=='SSCA'):
        raise Exception('Unknown approximation')

    if approximation == 'LMA':
        """
        Local Monodisperse Approximation: two independent layouts
        with their own distinct interference functions
        """
        # Interference functions
        layout_1 = ba.RadialParacrystal(particle_1, 16.8*nm, 1000*nm)
        layout_2 = ba.RadialParacrystal(particle_2, 22.8*nm, 1000*nm)
        layout_1.setProbabilityDistribution(profile)
        layout_2.setProbabilityDistribution(profile)

        # Populate layer with two independent layouts
        layer_1.addDeposit2D(0.5, layout_1)
        layer_1.addDeposit2D(0.5, layout_2)

    else: # 'DA/SSCA'
        # Particle mixture
        mix = ba.Mixture()
        mix.addParticle(particle_1, 4)
        mix.addParticle(particle_2, 1)

        layout = ba.RadialParacrystal(mix, 18*nm, 1000*nm)
        profile = ba.Profile1DGauss(3*nm)
        layout.setProbabilityDistribution(profile)

        """
        The only difference between Decoupling Approximation and
        Size-Spacing Coupling Approximation is that in SSCA there is
        a position phase offset between fractions defined by 'kappa' parameter:
        DA: kappa = 0 (default value)
        SSCA: kappa = 1
        """
        if approximation == 'SSCA':
            layout.setKappa(1)

        # Populate layer with the layout
        layer_1.deposit2D(layout)

    # Sample
    sample = ba.Sample()
    sample.addLayer(layer_1)
    sample.addLayer(layer_2)

    return sample


def get_simulation(sample):
    beam = ba.Beam(1e9, 0.1*nm, 0.2*deg)
    n = 200
    detector = ba.SphericalDetector(n, 0., 2*deg, n, 0., 2*deg)
    simulation = ba.ScatteringSimulation(beam, sample, detector)
    return simulation


def simulate(approximation):
    sample = get_sample(approximation)
    simulation = get_simulation(sample)
    result = simulation.simulate()
    result.setTitle(approximation)
    return result


if __name__ == '__main__':
    ba.showSample3D(get_sample('LMA'), sample_size=240*nm, seed=0)
    results = [
        simulate('LMA'),
        simulate('DA'),
        simulate('SSCA')
    ]
    ba.plot2d_to_row(results, unit_aspect=1)
    ba.plt.show()
auto/Examples/gisas/methods/Approximations.py