Rectangular detector

In this example we demonstrate the difference between GISAS simulation using default spherical detector and using special rectangular detector. The later provides more accurate representation of real experimental detectors.

See this tutorial for detailed explanations about various detector types in BornAgain.

 

  • As an example we take typical PILATUS detector (981x1043 pixels) placed at the distance 2000 mm from sample origin. The detector is perpendicular to the x-axis of sample reference frame, as shown on the plot.
  • Scattering from monodisperce distribution of cylindrical particles in DWBA is simulated.
  • Two detectors are defined in the code: a spherical detector (line 42) and rectangular detector (line 58). They parameters are selected to represent a real PILATUS detector as close as possible.
  • We run two simulations for two different detectors independently, and then compare results.
  • Both simulations looks very much alike. The relative difference plot indicates the difference on the level 10^-1 - 10^-3.
  • The difference is coming from the fact, that detector pixel shapes, as well as coordinates of pixel centers in phi_f, alpha_f space, are slighly different in the case of spherical and rectangular detectors.
  • Please have in mind, that Simulation::getIntensityData() returns a Histogram2D object with axes defined in native detector coordinates (radians for SphericalDetector and millimeters for RectangularDetector). in the case of SphericalDetector radians are converted during the plotting into degrees for convenience.
Real-space model: 
Intensity Image: 
Python Script: 
"""
Simulation with rectangular detector. Pilatus3-1M detector is used as an example.
Results will be compared against simulation with spherical detector.
"""
import numpy
import bornagain as ba
from bornagain import deg, angstrom, nm
import matplotlib
from matplotlib import pyplot as plt

detector_distance = 1000.0  # in mm
pilatus_pixel_size = 0.172  # in mm
pilatus_npx, pilatus_npy = 981, 1043  # number of pixels


def get_sample():
    """
    Returns a sample with cylindrical particles on a substrate.
    """
    # defining materials
    m_ambience = ba.HomogeneousMaterial("Air", 0.0, 0.0)
    m_substrate = ba.HomogeneousMaterial("Substrate", 6e-6, 2e-8)
    m_particle = ba.HomogeneousMaterial("Particle", 6e-4, 2e-8)

    # collection of particles
    edge = 40*nm
    ff = ba.FormFactorBox(edge, edge, edge)
    cylinder = ba.Particle(m_particle, ff)
    particle_layout = ba.ParticleLayout()
    particle_layout.addParticle(cylinder, 1.0)

    air_layer = ba.Layer(m_ambience)
    air_layer.addLayout(particle_layout)
    substrate_layer = ba.Layer(m_substrate)

    multi_layer = ba.MultiLayer()
    multi_layer.addLayer(air_layer)
    multi_layer.addLayer(substrate_layer)
    return multi_layer


def get_spherical_detector():
    """
    Returns a spherical detector roughly approximating our PILATUS detector
    """
    n_phi = pilatus_npx
    n_alpha = pilatus_npy
    width = pilatus_npx*pilatus_pixel_size
    height = pilatus_npy*pilatus_pixel_size
    phi_min = numpy.arctan(-width/2./detector_distance)
    phi_max = numpy.arctan(width/2./detector_distance)
    alpha_min = 0.0
    alpha_max = numpy.arctan(height/detector_distance)
    return ba.SphericalDetector(
        n_phi, phi_min, phi_max, n_alpha, alpha_min, alpha_max)


def get_rectangular_detector():
    """
    Returns a rectangular detector representing our PILATUS detector
    """
    width = pilatus_npx*pilatus_pixel_size
    height = pilatus_npy*pilatus_pixel_size
    detector = ba.RectangularDetector(pilatus_npx, width, pilatus_npy, height)
    detector.setPerpendicularToSampleX(detector_distance, width/2., 0.0)
    return detector


def get_simulation():
    """
    Return a GISAXS simulation with defined beam
    """
    simulation = ba.GISASSimulation()
    simulation.setBeamParameters(10*angstrom, 0.2*deg, 0.0*deg)
    return simulation


def plot_as_colormap(hist, Title, xLabel, yLabel, zmin=None, zmax=None):
    """
    Simple plot of intensity data as color map
    """
    if not zmin:
        zmin = 1.0

    if not zmax:
        zmax = hist.getMaximum()

    im = plt.imshow(
        hist.getArray(),
        norm=matplotlib.colors.LogNorm(zmin, zmax),
        extent=[hist.getXmin(), hist.getXmax(),
                hist.getYmin(), hist.getYmax()],
        aspect='auto')
    cb = plt.colorbar(im, pad=0.025)
    plt.xlabel(xLabel, fontsize=16)
    plt.ylabel(yLabel, fontsize=16)
    plt.title(Title)


def plot(results):
    """
    Plots results of two simulations and their relative difference on one canvas
    """
    fig = plt.figure(figsize=(13.6, 5.12))

    # showing  result of spherical detector simulation
    plt.subplot(1, 3, 1)
    plot_as_colormap(results['spherical'], "Spherical detector",
                     r'$\phi_f ^{\circ}$', r'$\alpha_f ^{\circ}$')

    # showing  result of rectangular detector simulation
    plt.subplot(1, 3, 2)
    plot_as_colormap(results['rectangular'], "Rectangular detector",
                     'X, mm', 'Y, mm')

    # show relative difference between two plots (sph[i]-rect[i])/rect[i]
    # for every detector pixel
    plt.subplot(1, 3, 3)
    plot_as_colormap(results['difference'], "Relative difference",
                     'X, mm', 'Y, mm', 1e-06, 1.0)

    plt.subplots_adjust(left=0.05, right=0.95, top=0.88, bottom=0.12)
    plt.show()


def run_simulation():
    """
    Run two simulations for two different detectors and plot results
    """
    results = {}

    sample = get_sample()
    simulation = get_simulation()
    simulation.setSample(sample)

    # runs simulation for spherical detector
    simulation.setDetector(get_spherical_detector())
    simulation.runSimulation()
    results['spherical'] = simulation.getIntensityData(ba.IDetector2D.DEGREES)

    # runs simulation for rectangular detector
    simulation.setDetector(get_rectangular_detector())
    simulation.runSimulation()
    results['rectangular'] = simulation.getIntensityData()

    results['difference'] = results['spherical'].relativeDifferenceHistogram(
        results['rectangular'])

    return results


if __name__ == '__main__':
    results = run_simulation()
    plot(results)