Fitting along slices

Here we demonstrate how to fit along slices. The idea is that the user defines positions of vertical and horizontal lines crossing the detector plane in regions of most interest (Yoneda wings, Bragg peaks, etc) and then finds sample parameters which fits those regions best.

Such approach uses much less CPU while still giving a chance to find optimal sample parameters. In general, however, it is arguable, whether fitting along slices makes more sence than fitting using the whole detector image. Without going into this discussion we just provide such possibility.

Technically, the idea is to mask the whole detector except thin lines, one vertical and one horizontal, representing slices. This will make the simulation and fitting to calculate only along the indicated slices.

  • In the given example we simulate cylinders on top of substrate without interference. The fitting procedure looks for the cylinder's height and radius.
  • Lines 187, 188, 189 demonstrate the whole code you need to mask the whole detector and then unmask two slices: vertical line at phi=0.0, and horizontal line at alpha=0.2deg.
  • The majority of the code is located in custom DrawObserver class (defined at Line 77, and invoked at Lines 195,196), which plots the fit progress along slices every 5th iteration.
Intensity Image: 
Python Script: 
Fitting example: fit along slices

from __future__ import print_function
import matplotlib
from matplotlib import pyplot as plt
import math
import random
import bornagain as ba
from bornagain import deg, angstrom, nm
import numpy

phi_slice_value = 0.0*deg  # position of vertical slice
alpha_slice_value = 0.2*deg  # position of horizontal slice

def get_sample(radius=5*nm, height=10*nm):
    Returns a sample with uncorrelated cylinders on a substrate.
    m_air = 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)

    cylinder_ff = ba.FormFactorCylinder(radius, height)
    cylinder = ba.Particle(m_particle, cylinder_ff)

    particle_layout = ba.ParticleLayout()

    air_layer = ba.Layer(m_air)

    substrate_layer = ba.Layer(m_substrate, 0)
    multi_layer = ba.MultiLayer()
    return multi_layer

def get_simulation():
    Create and return GISAXS simulation with beam and detector defined
    simulation = ba.GISASSimulation()
    simulation.setDetectorParameters(100, -1.0*deg, 1.0*deg,
                                     100, 0.0*deg, 2.0*deg)
    simulation.setBeamParameters(1.0*angstrom, 0.2*deg, 0.0*deg)
    return simulation

def create_real_data():
    Generating "real" data by adding noise to the simulated data.
    sample = get_sample(5.0*nm, 10.0*nm)

    simulation = get_simulation()

    real_data = simulation.getIntensityData()

    # spoiling simulated data with the noise to produce "real" data
    noise_factor = 1.0
    for i in range(0, real_data.getTotalNumberOfBins()):
        amplitude = real_data.getBinContent(i)
        sigma = noise_factor*math.sqrt(amplitude)
        noisy_amplitude = random.gauss(amplitude, sigma)
        if noisy_amplitude