### Working with sample parameters

This example shows how to get an access to sample structure information and how to manipulate sample parameters on already constructed sample. This can be useful for debugging and for quick simulations.

In BornAgain a sample is described by a hierarchical tree of objects. For example, the tree representing a multilayer can be printed in a Python session by running.

print(sample.treeToString())

with subsequent output

MultiLayer ('CrossCorrelationLength':0 'ExternalFieldX':0 'ExternalFieldY':0 'ExternalFieldZ':0)
Layer0
ParticleLayout ('TotalParticleDensity':1)
Particle0 ('Abundance':0.5 'PositionX':0 'PositionY':0 'PositionZ':0)
Particle1 ('Abundance':0.5 'PositionX':0 'PositionY':0 'PositionZ':0)
Prism3 ('BaseEdge':5 'Height':5)
InterferenceNone
LayerInterface
Layer1

The top MultiLayer object is composed of three children, namely Layer #0, Layer Interface #0 and Layer #1. The children objects might themselves also be decomposed into tree-like structures. For example, Layer #0 contains a ParticleLayout object, which holds information related to the two types of particles populating the layer. All numerical values used during the sample construction (thickness of layers, size of particles, etc.) are part of the same tree structure. These values are registered in the sample parameter pool using the name composed of the corresponding nodes’ names. A list of the names and values of all registered sample’s parameters can be displayed using the command

 66  print(sample.parametersToString())

The output will be:

'/MultiLayer/CrossCorrelationLength':0
'/MultiLayer/ExternalFieldX':0
'/MultiLayer/ExternalFieldY':0
'/MultiLayer/ExternalFieldZ':0
'/MultiLayer/Layer0/ParticleLayout/TotalParticleDensity':1
'/MultiLayer/Layer0/ParticleLayout/Particle0/Abundance':0.5
'/MultiLayer/Layer0/ParticleLayout/Particle0/PositionX':0
'/MultiLayer/Layer0/ParticleLayout/Particle0/PositionY':0
'/MultiLayer/Layer0/ParticleLayout/Particle0/PositionZ':0
'/MultiLayer/Layer0/ParticleLayout/Particle0/Cylinder/Height':5
'/MultiLayer/Layer0/ParticleLayout/Particle1/Abundance':0.5
'/MultiLayer/Layer0/ParticleLayout/Particle1/PositionX':0
'/MultiLayer/Layer0/ParticleLayout/Particle1/PositionY':0
'/MultiLayer/Layer0/ParticleLayout/Particle1/PositionZ':0
'/MultiLayer/Layer0/ParticleLayout/Particle1/Prism3/BaseEdge':5
'/MultiLayer/Layer0/ParticleLayout/Particle1/Prism3/Height':5

These values can be accessed/changed during run time. For example, the height of the cylinders populating the first layer can be changed from the current value of 5 nm to 10 nm by running the command

 80 81 82  sample.setParameterValue( "/MultiLayer/Layer0/ParticleLayout/Particle0/Cylinder/Height", 10.0*nm)

Wildcards * can be used to reduce typing or to work on a group of parameters. In the example below, the first command will change the height of all cylinders in the same way, as in the previous example.

 97  sample.setParameterValue("*/Cylinder/Height", 10.0*nm)

The line below will change simultaneously both, the height and the half-side length of prisms.

 98  sample.setParameterValue("*/Prism3/*", 10.0*nm)

Example below demonstrates how to create a sample with fixed parameters and then change these parameters on the fly during runtime. Four simulations are performed one after another. Parameters of the sample are adjusted in between using different matching criteria.

  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  """ Working with sample parameters. This example shows how to create a simulation with fixed parameters, and then change these parameters on the fly during runtime. """ from __future__ import print_function import bornagain as ba from bornagain import deg, angstrom, nm def get_sample(): """ Returns a sample with uncorrelated cylinders and prisms on a substrate. Parameter set is fixed. """ # defining materials 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) # collection of particles cylinder_ff = ba.FormFactorCylinder(5*nm, 5*nm) cylinder = ba.Particle(m_particle, cylinder_ff) prism_ff = ba.FormFactorPrism3(5*nm, 5*nm) prism = ba.Particle(m_particle, prism_ff) layout = ba.ParticleLayout() layout.addParticle(cylinder, 0.5) layout.addParticle(prism, 0.5) interference = ba.InterferenceFunctionNone() layout.setInterferenceFunction(interference) # air layer with particles and substrate form multi layer air_layer = ba.Layer(m_air) air_layer.addLayout(layout) substrate_layer = ba.Layer(m_substrate, 0) multi_layer = ba.MultiLayer() multi_layer.addLayer(air_layer) multi_layer.addLayer(substrate_layer) 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 run_simulation(): """ Runs simulations for the sample with different sample parameters. """ sample = get_sample() simulation = get_simulation() simulation.setSample(sample) print("The tree structure of the simulation") print(simulation.treeToString()) print("The simulation contains following parameters ('name':value)") print(simulation.parametersToString()) results = {} # simulation #1 # initial simulation is used simulation.runSimulation() results[0] = simulation.result() # simulation #2 # one sample parameter (cylinder height) is changed using exact parameter name simulation.setParameterValue("/GISASSimulation/MultiLayer/Layer0/ParticleLayout" "/Particle0/Cylinder/Height", 10.0*nm) simulation.runSimulation() results[1] = simulation.result() # simulation #3 # all parameters matching criteria will be changed (cylinder height in this case) simulation.setParameterValue("*/Cylinder/Height", 100.0*nm) simulation.runSimulation() results[2] = simulation.result() # simulation #4 # all parameters which are matching criteria will be changed simulation.setParameterValue("*/Cylinder/Height", 10.0*nm) simulation.setParameterValue("*/Prism3/*", 10.0*nm) simulation.runSimulation() results[3] = simulation.result() return results def plot(results): """ Draw results of several simulations on canvas """ from matplotlib import pyplot as plt plt.figure(figsize=(12.80, 10.24)) for nplot, hist in results.items(): plt.subplot(2, 2, nplot+1) ba.plot_colormap(hist, zlabel="") plt.tight_layout() plt.show() if __name__ == '__main__': results = run_simulation() plot(results) 
SimulationParameters.py