Simultaneous fit of two datasets

In this example we demonstrate how to fit two datasets simultaneously.

Suppose that we have a sample measured twice for two different incident angles. We are going to fit both datasets simultaneously to find the unknown sample parameters.

To do this, we define one dataset (a pair of real data and corresponding simulation model) for the first incidence angle and another pair of (real data, simulation model) for the second incidence angle. We add both pairs to the FitSuite and run the fitting as usual.

  • In the given example we simulate hemi ellipsoids on top of substrate without interference. At lines 167-169 we define 3 fitting parameters: radius_a and height are parameters to find, radius_b is fixed.
  • At lines 152-160 we define two fixed incident alpha angles equal to 0.1 and 0.4 degree and generate two datasets. These datasets are then used to initialize FitSuite via addSimulationAndRealData method.
  • The majority of the code is located in custom DrawObserver class defined at Line 75, which plots the fit progress for both datasets simultaneously every 10th iteration.
Intensity Image: 
Python Script: 
Fitting example: demonstrates how to fit two datasets simultaneously.

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

def get_sample(radius_a=4.0*nm, radius_b=4.0*nm, height=4.0*nm):
    Returns a sample with uncorrelated cylinders and pyramids.
    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)

    formFactor = ba.FormFactorHemiEllipsoid(radius_a, radius_b, height)
    hemiEllipsoid = ba.Particle(m_particle, formFactor)

    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(incident_alpha=0.2):
    Returns a GISAXS simulation with beam and detector defined.
    simulation = ba.GISASSimulation()
    simulation.setDetectorParameters(50, -1.5*deg, 1.5*deg,
                                     50, 0.0*deg, 2.0*deg)
    simulation.setBeamParameters(1.0*angstrom, incident_alpha, 0.0*deg)
    return simulation

def create_real_data(incident_alpha):
    Generating "real" data by adding noise to the simulated data.
    sample = get_sample(
        radius_a=5.0*nm, radius_b=6.0*nm, height=8.0*nm)

    simulation = get_simulation(incident_alpha)

    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