%matplotlib widget
import numpy as np
import matplotlib.pylab as m
import escape as esc
from escape.utils.widgets import show
esc.require("0.9.7")
Loading material database from /home/dkor/Data/Development/workspace_escape/escape/python/src/escape/scattering/../data/mdb/materials.db
Arrays wrapping¶
In scattering, experimental data can be large. Depending on your sample and resolution of your instrument, single High Resolution X-ray diffraction curve measured near one diffraction peak can easily reach 5000 points. When developing library like ESCAPE one has to keep in mind a good enough performance in operating with large datasets, lack of unnecessary data copying to keep memory consumption constant and easy user interface to access and treat the data with other libraries. The escape.core.array module is responsible for that. This module provides an interface between numpy arrays and internal array_t core object which wraps a pointer to data and provides functionality to access this data by the core in an efficient way.
This notebook demonstrates how wrapping of numpy arrays works with the following ESCAPE array types: double_array_obj and mask_array_obj. The latter works with bool arrays and used for data masking. User can create a wrapper which will manage the same memory as a source numpy array or a new object with a full copy of the source.
All ESCAPE objects which require arrays for input and output do the wrapping automatically.
Below we create two double_array_obj objects, one contains a copy of a numpy array, dcopy and a second which wraps the same numpy array, dwrap
x = np.zeros(5, dtype=float)
dcopy = esc.double_array(x, copy=True)
dwrap = esc.double_array(x, copy=False)
Let's set the last value of wrapper dwrap to 5, it shoud also change the original array. And we set dcopy[0] to -5, it doesn't influence original array.
dwrap[1] = 5
dcopy[1] = 10
print("Copy:", dcopy)
print("Wrapper:", dwrap)
print("Original:", x)
Copy: array([ 0., 10., 0., 0., 0.]) Wrapper: array([0., 5., 0., 0., 0.]) Original: [0. 5. 0. 0. 0.]
Obviously, wrapping also works in another direction i.e. ESCAPE arrays double_array_obj and mask_array_obj can be wrapped by numpy library. In this case one can apply full functionality of numpy library to that arrays. Below we wrap a dcopy object. We set all its values to 10, which will not affect values of dwrap and darr arrays.
npwrapper=np.asarray(dcopy)
npwrapper[:]=20
print("Copy:", dcopy)
print("Wrapper:", dwrap)
print("Original:", x)
Copy: array([20., 20., 20., 20., 20.]) Wrapper: array([0., 5., 0., 0., 0.]) Original: [0. 5. 0. 0. 0.]
Masked arrays work in the same way as double arrays, except source array should contain boolean values. Below is an example of wrapping of boolean array.
#mask/boolean array
m=np.ones(shape=5, dtype=bool)
mwrap=esc.mask_array(m, copy=False)
mcopy=esc.mask_array(m, copy=True)
mwrap[1]=False
mcopy[2]=False
print ("Copy: ", mcopy)
print ("Wrapper: ", mwrap)
print ("Original: ", m)
Copy: array([ True, True, False, True, True]) Wrapper: array([ True, False, True, True, True]) Original: [ True False True True True]
Data¶
The object of data_obj type is a container class for experimental data. Data object is created with the following command:
esc.data(name="Data name", coordinates, experiment, errors=None, mask=None,
copy=False)
The data command expects 'coordinates' and 'experiment' arrays to have numpy.ndarray type. If not, they will be comverted and copied regardless of copy parameter.
All arrays will be copied if 'copy' parameter is True, otherwise data_obj keeps wrappers of arrays if it is possible. If 'errors' parameter is None, the experimental errors will be calculated according to Poisson distribution, i.e. errors=sqrt(experiment). The 'mask' parameter is used if some data points should be hidden, for example, primary beam or cosmic rays.
Data object has a domain_size
property which returns number of coordinates in the input array, i.e. data which has domain size equal to $2$ correspond to a modeled function $F(x, y)$ and its input coordinates array is expected to have a form of $[x0, y0, x1, y1, x2, y2, ...]$. Two-dimensional arrays of the following form
coordinates=[
[x0, y0],
[x1, y1],
[x2, y2]
]
are also supported.
Below few examples are given about how to create data objects of different domain size and how to plot them using show method from escape.utils module. In real life these arrays are read from experimental files of different formats. Currently there is no support of any format. The experimental data which is kept in ASCII text files can be read easily with exisitng numpy methods, like 'numpy.loadtxt', 'numpy.genfromtxt'.
Two-dimensional data¶
Two-dimensional data is straightforward.
# create abscissas array
x=np.arange(-10, 10, 0.1)
# create ordinates array of the same size
y=1e3*np.sin(x)**2
dobj=esc.data("2D Data", x, y)
# we use show method to plot the data
show(dobj, title="2D data Plot", xlabel="X", ylabel="Y", grid=True, xlog=True, ylog=True)
Two-dimensional data with mask¶
# create mask of the same size as the abscissas array
mask = np.ones(shape=x.shape, dtype=bool)
# masking all values in the range [-2.5, 2.5]
mask[(x>-2.5) & (x<2.5)]=False
mdobj=esc.data("2D Data with mask", x, y, mask=mask)
# we use show method to plot the data
show(mdobj, title="2D Data with mask", xlabel="X", ylabel="Y", grid=True, ylog=True)
Three-dimensional data¶
Three-dimensional data has two coordinates $x, y$ and intensity along $z$ - axis. Below is an example of how coordinate and intensity arrays can be created. Show method supports currently only map-like plots for 3d data, which should be specified with plot_type parameter.
# we create first x and y one-dimensional equidistant arrays
x=np.linspace(-5, 5, 50)
y=np.linspace(-10, 10, 50)
# create meshgrid of both coordinates
xv, yv=np.meshgrid(x, y)
# calculate 2d image of intensities
z=np.sin(xv)**2*np.sin(yv)**2
# flatten grid arrays
xvf=xv.flatten()
yvf=yv.flatten()
# create coordinates array of type [[x0, y0], [x1, y1],...]
xyf=np.column_stack([xvf, yvf]).flatten()
# flatten intensities
zf=z.flatten()
dobj=esc.data("3D Data", xyf, zf)
# we use show method to plot the data
show(dobj, figtitle="3D data Plot", xlabel="X", ylabel="Y", plot_type="map", rows=50)
Three-dimensional data with mask¶
# create mask of the same size as the abscissas array
mask = np.ones(shape=z.shape, dtype=bool)
# masking all values in the range [(-2.5, 2.5), (-2.5, 2.5)]
mask[(xv>-2.5) & (xv<2.5) & (yv>-2.5) & (yv<2.5)]=False
mask=mask.flatten()
mdobj=esc.data("3D Data", xyf, zf, mask=mask)
# we use show method to plot the data
show(mdobj, title="3D Data with mask", xlabel="X", ylabel="Y", plot_type="map", rows=50)