import numpy as np
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
Functors¶
Theoretical description of scattering experiments is based on Scattering Theory, which is a general framework for studying scattering processes of waves and particles. The final equation of intensity of scattered beam depends on a sample type, thin film or bulky sample, isotropic or anisotropic; setup components like, detector type - stripe, point or 2d image detector, slits configuration, beam profile, etc. One should not forget about diffuse scattering and background which sometimes cannot be defined precisely and are included in the optimized intensity equation as a polynom. To include all that into one framework and to give a user a freedom to describe special customized solutions for their laboratory setups or samples is a challenging task. After some trial and error we cam to conclusion that ESCAPE framework requires a flexible functor object.
Functor is an object which plays the role of a function of one or several variables. The maximum number of variables which is currenly supported is 5. Functors support mathematical operations and arithmetic operators.
When ESCAPE was developed we tried to implement functors so, that their notations would be as close as possible to functions in algebra. For that several entites have been introduced. The first one is a function variable.
Variables¶
One creates a variable in the follwoing way:
X=esc.var("X")
Y=esc.var("Y")
Z=esc.var("Z")
Variables support algebraic expressions and operators. If one writes $x+y$, the result of this expression is a two-dimensional functor, i.e. function with two variables $f(x, y)$
f=X+Y
print("Type: ", type(f))
print(f.domain_size)
print(f.variables)
Type: <class 'escape.core.objects.functor_obj'> 2 [variable(name='X'), variable(name='Y')]
Const functor¶
Const functor is a functor which regardless of its variables values, always returns a given constant value. Constant can be also a parameter. As an input constant functor requires also a list of variables.
p1 = esc.par("par1", 23.5)
#one-dimensional const functors
f1 = esc.func("const functor", X, p1)
f2 = esc.func("const functor", Y, 10)
#two-dimensional const functors
f3 = esc.func("const functor", [X, Y], p1)
print (f1(10))
print (f2(10))
print (f3(1, 2))
23.5 10.0 23.5
Expressions¶
As it has already been mentioned, functors support arithmetic operators and mathematical functions: sin(), cos(), tan(), sinh(), cosh(), tanh(), exp(), sqrt(), log(), log10(), pow(), min(), max(), erf().
Below we demonstrate a simple oscillating functor with damping amplitude.
q=esc.par("Damping", 0.05, userlim=[0, 2])
expr=esc.pow(esc.sin(X), 2.0)*esc.exp(-q*esc.abs(X))
#functors can be visualized with a 'show' command
show(expr, title="Damping sine")
#two-dimensional example
Qx=esc.par("Damping X", 0.05, userlim=[0, 2])
Qy=esc.par("Damping Y", 0.05, userlim=[0, 2])
expr2d=esc.pow(esc.cos(X), 2.0)*esc.pow(esc.cos(Y), 2.0)*esc.exp(-Qx*esc.abs(X))*esc.exp(-Qy*esc.abs(Y))
x=np.linspace(-5, 5, 100)
y=np.linspace(-5, 5, 100)
# create meshgrid of both coordinates
xv, yv=np.meshgrid(x, y)
# create coordinates array of type [x0, y0, x1, y1,...]
coords=np.column_stack([xv.flatten(), yv.flatten()]).flatten()
# for the 2d case one has to provide a plot_type, coordinates index and number of rows
# cbmin, cbmax and cblof are the colorbox properties
show(expr2d, coordinates=coords, xlabel="X", ylabel="Y", plot_type="map", rows=100,
coord_index=[0, 1], cblog=True, cbmin=1e-2)
Variables order¶
ESCAPE supports expressions with functors of different domain size and preserves the order of variables in the resulted functor. For example, $f_1(z, x)+f_2(x)+f_3(y)$ will result in the following functor $f_r(z, x, y)$, not $f_r(x, y, z)$ as maybe desired. One has to keep this in mind when creating large complex expressions. One can always check the order of variables using variables property of functors.
F1=esc.func("F1(z,x)", [Z, X], 10)
F2=esc.func("F2(x)", X, 0.1)
F3=esc.func("F3(y)", Y, 0.05)
Fr=F1+F2+F3
print(Fr.variables)
[variable(name='Z'), variable(name='X'), variable(name='Y')]
Boolean functors and conditional expressions¶
If two functors are compared using one of the standard comparison operators >, <, >=, <=, ==, !=, a product of this comparison is a boolean functor. For example, we have two functors f1(x)=x^2 and f2(x,y)=x+y
F1 = X*X
F2 = X+Y
Fb = F1 >= F2
print(type(Fb))
Fb.variables
<class 'escape.core.objects.bool_functor_obj'>
[variable(name='X'), variable(name='Y')]
Fb is a boolean functor of two variables $f(x, y)$. When beeing called with with values of variables $x$ and $y$, it calls f1(x) and f2(x, y) and returns a result of f1(x) >= f2(x). Boolean functors support logical operators: or - $|$, and - $&$ and not - '~'. Python doesn't allow to override standard operators like and, not, or, thus, we use bitwise operators for this purpose.
Fb(1, 10)
False
Fb(4, 5)
True
Boolean functors are typically used with conditional expressions. The first argument of the conditional method is a condition itself, i.e. a boolean functor, the second one is a functor which has to be executed if the condition returns true, and the last one is executed if the condition returns false.
P=esc.par("P", 10, userlim=[-1e-3, 1e-3])
B=esc.par("B", 0, userlim=[-10, 10])
F1=B*X+P
F2=P*X+B
Fc=esc.conditional((X>=-5) & (X<=5), F1, F2)
a=np.arange(-10, 10, 0.01)
show(F1, title="F1(x)")
show(F2, title="F2(x)")
show(Fc, title="Fc(x)")
Functors with complex return type¶
In scattering the connection between experiment geometry and equations describing measured intensity is done in terms of reciprocal space. In the first approximation, called Born approximation, the conversion between real- and reciprocal- space is performed using Fourier Transform. The result of Fourier Transform can be a complex function. Thus, it make sense to introduce a functor with complex return type.
#Constant complex functor
Cf=esc.cfunc("", [X], -1j)
type(Cf)
escape.core.objects.cplx_functor_obj
Complex functors support the same arithmetic operators and mathematical functors as functors with double return type. Additionally there are several other methods like, norm, real, imag, conjugate which require cplx_functor_obj input parameter. Few examples are given below.
F=esc.real((esc.exp(1j*X)+esc.exp(-1j*X))/2.0)
show(F)
F=esc.real((esc.exp(1j*X)-esc.exp(-1j*X))/(2*1j))
show(F)
F=esc.imag(esc.sin(-1j*X))+esc.real(esc.cos(-1j*X))
show(F)