Beam divergence in specular simulations

This example demonstrates beam divergence effects in reflectivity computations. All simulation parameters (except for ones related to beam divergence itself) coincide with those defined in reflectometry simulation tutorial.

In application to specular simulation, the observed intensity picture can be affected by divergence either in beam wavelength or incident angle.

Intensity image

In this example the following parameters related to the beam divergence were set to the simulation:

  • Gaussian distributions both in wavelength and incident angle
  • The mean value for beam wavelength $\lambda_0 = 1.54 \, \unicode{x212B}$
  • Standard deviation in the wavelength $\sigma_{\lambda} = 0.01 \cdot \lambda_0$
  • Standard deviation in the incident angle $\sigma_{\alpha} = 0.01^{\circ}$

As one can see from the python script, the definitions of beam parameter distributions match ones described in similar example for GISAS simulations. However, in the case of the incident angle one should always use a distribution with zero mean, since the actual mean value is substituted by SpecularSimulation in dependence on the defined inclination angle range. If the distribution of the incident angle has non-zero mean value, an exception is thrown:

terminate called after throwing an instance of 'std::runtime_error'
  what():  Error in SpecularSimulation: parameter distribution of beam inclination angle should have zero mean.
  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
117
118
119
120
121
122
"""
Example of taking into account
beam divergence effects in specular simulations
with BornAgain.

"""
from matplotlib import pyplot as plt
import bornagain as ba
from bornagain import deg, angstrom


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

    # creating materials
    m_ambient = ba.MaterialBySLD("Ambient", 0.0, 0.0)
    m_ti = ba.MaterialBySLD("Ti", -1.9493e-06, 0.0)
    m_ni = ba.MaterialBySLD("Ni", 9.4245e-06, 0.0)
    m_substrate = ba.MaterialBySLD("SiSubstrate", 2.0704e-06, 0.0)

    # creating layers
    ambient_layer = ba.Layer(m_ambient)
    ti_layer = ba.Layer(m_ti, 30 * angstrom)
    ni_layer = ba.Layer(m_ni, 70 * angstrom)
    substrate_layer = ba.Layer(m_substrate)

    # creating multilayer
    multi_layer = ba.MultiLayer()
    multi_layer.addLayer(ambient_layer)
    for i in range(10):
        multi_layer.addLayer(ti_layer)
        multi_layer.addLayer(ni_layer)
    multi_layer.addLayer(substrate_layer)

    return multi_layer


def get_simulation(divergent_beam):
    """
    Defines and returns a specular simulation.
    """

    # simulation parameters definition
    wavelength = 1.54 * angstrom
    n_bins = 500
    alpha_min = 0.0 * deg
    alpha_max = 2.0 * deg

    simulation = ba.SpecularSimulation()
    simulation.setBeamParameters(
        wavelength, n_bins, alpha_min, alpha_max)

    # adding beam divergence
    if divergent_beam is True:
        add_beam_divergence(simulation, wavelength)

    return simulation


def add_beam_divergence(simulation, wavelength):
    """
    Adds beam divergence to the simulation
    """
    # beam divergence parameters
    d_wl = 0.01 * wavelength  # spread width for wavelength
    d_ang = 0.01 * ba.deg  # spread width for incident angle
    n_sig = 3  # number of standard deviations to take into account
    n_points = 25  # number of points to take in parameter distribution

    # creating beam parameter distributions
    alpha_distr = ba.DistributionGaussian(0.0, d_ang)
    wavelength_distr = ba.DistributionGaussian(wavelength, d_wl)

    # adding distributions to the simulation
    simulation.addParameterDistribution("*/Beam/InclinationAngle",
                                        alpha_distr, n_points, n_sig)
    simulation.addParameterDistribution("*/Beam/Wavelength",
                                        wavelength_distr, n_points, n_sig)


def run_simulation(divergent_beam):
    """
    Runs simulation and returns its result.
    """
    sample = get_sample()
    simulation = get_simulation(divergent_beam)
    simulation.setSample(sample)
    simulation.runSimulation()
    return simulation.result()


def get_plot_data(sim_result):
    data = sim_result.data()
    intensity = data.getArray()
    x_axis = data.getAxis(0).getBinCenters()
    return x_axis, intensity


def plot(sim_result_1, sim_result_2):
    """
    Plots results from two different simulations
    """

    plt.semilogy(*get_plot_data(sim_result_1), *get_plot_data(sim_result_2))

    plt.xlabel(r'$\alpha_i \; (deg)$', fontsize=16)
    plt.ylabel(r'Intensity', fontsize=16)

    plt.legend(['Divergent beam',
                'Ideal beam'],
                loc='upper right')

    plt.show()


if __name__ == '__main__':
    result_div_beam = run_simulation(divergent_beam=True)
    result_ideal_beam = run_simulation(divergent_beam=False)
    plot(result_div_beam, result_ideal_beam)
BeamDivergence.py