-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathactive_area.py
396 lines (347 loc) · 13.6 KB
/
active_area.py
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
"""
Active area calculations
Authors/Modifications:
----------------------
Tom Trainor (tptrainor@alaska.edu)
Notes:
------
These area are based on completley geometric projections. ie we assume
that the beam is perfectly parrallel (no divergence)
It would be interesting if we could set the detector polygon from an
arbirary set of pixels (e.g. roi) on image detector... especially if
shifted off center (or better off center relative to a specific hkl)
"""
##########################################################################
import numpy as num
import types
from matplotlib import pyplot
from mathutil import cartesian_mag, cartesian_angle
from polygon import poly_area, poly_area_num, inner_polygon
from polygon import plot_polygon, plot_points, plot_circle
##########################################################################
def active_area(nm,ki=num.array([0.,1.,0.]),kr=num.array([0.,1.,0.]),
beam=[],det=None,sample=1.,plot=False,fig=None):
"""
Calc the area of overlap of beam, sample and detector
surface polygon projections.
Parameters:
-----------
* nm is the surface normal vector, defined in the laboratory frame.
* ki and kr are vectors defined in the lab frame parallel to the
incident beam and diffracted beam directions respectively
(magnitudes are arbitrary)
* beam, det and sample are lists that hold the the lab-frame (3D)
vectors defining the beam apperature, detector apperature
and sample shape
If det = None then we ignore it and just compute spill-off correction
If sample is a single number we take it as the diamter of a round sample
mounted flat. Otherwise sample hould be a list of lab frame vectors that
describes the sample polygon.
If sample == None, then we assume the sample is infinite in size
* If plot = True then makes plot
Note:
-----
The lab frame coordinate systems is defined such that:
x is vertical (perpendicular, pointing to the ceiling of the hutch)
y is directed along the incident beam path
z make the system right handed and lies in the horizontal scattering plane
(i.e. z is parallel to the phi axis)
The center (0,0,0) of the lab frame is the rotation center of the instrument.
Output:
-------
* A_beam = total area of the beam projection into the surface plane
* A_int = area of sample illuminated within the detector projection
Use to correct scattering data for area effects, including spilloff, i.e.
A_ratio = A_int/A_beam
Ic = I/A_ratio, I = Idet/Io
"""
# Calc surf system transformation matrix
M = calc_surf_transform(nm)
# calc k vectors in the surf frame (i.e. [xs,ys,zs])
# first make them unit vectors, even though no real need to..
ki = ki/cartesian_mag(ki)
kr = kr/cartesian_mag(kr)
ki_s = num.dot(M,ki)
kr_s = num.dot(M,kr)
#################################################################
# Get the beam, detector and surface polygons.
# Convert vectors to surface frame.
# Then for beam and det project the vectors onto
# the surface along the k vectors. ie. calc the vector
# intercepts with the surface plane
# Each of the below polygons is a list of 2D vectors
# ie in-plane [x_s, y_s] points
#################################################################
# beam
if type(beam) == types.ListType:
beam_poly = []
if len(beam) < 3:
print "Error in beam description"
return None
for v in beam:
vs = num.dot(M,v)
int = surface_intercept(ki_s, vs)
beam_poly.append(int)
else:
beam_poly=None
# det
if type(det) == types.ListType:
det_poly = []
if len(det) < 3:
print "Error in det description"
return None
for v in det:
vs = num.dot(M,v)
int = surface_intercept(kr_s, vs)
det_poly.append(int)
else:
det_poly=None
# sample
if type(sample) == types.ListType:
sam_poly = []
if len(sample) < 3:
print "Error in sample description"
return None
for v in sample:
vs = num.dot(M,v)
if num.fabs(vs[2]) > 0.01:
print "Warning sample hieght problem"
sam_poly.append(vs[:2])
sample_shape=True
else:
sam_poly = None
sample_shape = False
#####################################################################
# depending on whether we have a sample shape description
# or assume a round sample, pass along appropriate
# data to compute areas. Note all the polygons are lists
# of 2D surface frame vectors (ie in plane vectors)
#####################################################################
if sample_shape == False:
(A_beam,A_int) = _area_round(beam_poly,det_poly,diameter=sample,plot=plot,fig=fig)
else:
(A_beam,A_int) = _area_polygon(beam_poly,det_poly,sam_poly,plot=plot,fig=fig)
return (A_beam, A_int)
#########################################################################
def _area_round(beam_poly,det_poly,diameter=None,plot=False,fig=None):
"""
Compute areas for round sample of fixed diameter
Parameters:
-----------
* beam_pol is the beam polygon
* det_poly is the detector polygon
if det_poly = None, then just compute the beam and
sample overlap ie A_int/A_beam = spill fraction
"""
if plot:
if fig != None: pyplot.figure(fig)
pyplot.clf()
A_beam = poly_area(beam_poly)
if det_poly != None:
inner_poly = inner_polygon(beam_poly,det_poly)
else:
inner_poly = beam_poly
if diameter <= 0.:
diameter = None
if diameter == None:
A_int = poly_area(inner_poly)
else:
A_int = poly_area_num(beam_poly,diameter=diameter,plot=plot)
# make plots
if plot:
plot_polygon(beam_poly,fmt='ro-',label='Beam')
if det_poly != None:
plot_polygon(det_poly,fmt='ko-',label='Detector')
plot_points(inner_poly,fmt='go-')
plot_polygon(inner_poly,fmt='g--',linewidth=4,label='Intersection')
if diameter!= None:
plot_circle(diameter/2.,fmt='b-',label='Sample')
pyplot.xlabel('Xs')
pyplot.ylabel('Ys')
ll = round(num.sqrt(100.*A_int))
pyplot.xlim(-ll,ll)
pyplot.ylim(-ll,ll)
pyplot.grid()
pyplot.legend()
return (A_beam,A_int)
#########################################################################
def _area_polygon(beam_poly,det_poly,sam_poly,plot=False,fig=None):
"""
Compute areas for polgon sample
Parameters:
-----------
* beam_pol is the beam polygon
* det_poly is the detector polygon
if det_poly = None, then just compute the beam and
sample overlap ie A_int/A_beam = spill fraction
"""
if plot:
if fig != None: pyplot.figure(fig)
pyplot.clf()
A_beam = poly_area(beam_poly)
if sam_poly == None:
inner_poly = beam_poly
else:
inner_poly = inner_polygon(beam_poly,sam_poly)
if det_poly != None:
inner_poly = inner_polygon(inner_poly,det_poly)
A_int = poly_area(inner_poly)
# make plots
if plot:
plot_polygon(beam_poly,fmt='ro-',label='Beam')
if sam_poly != None:
plot_polygon(sam_poly,fmt='bo-',label='Sample')
if det_poly != None:
plot_polygon(det_poly,fmt='ko-',label='Detector')
plot_points(inner_poly,fmt='go-')
plot_polygon(inner_poly,fmt='g--',linewidth=4,label='Intersection')
pyplot.xlabel('Xs')
pyplot.ylabel('Ys')
ll = round(num.sqrt(100.*A_int))
pyplot.xlim(-ll,ll)
pyplot.ylim(-ll,ll)
pyplot.grid()
pyplot.legend()
return (A_beam,A_int)
##########################################################################
def calc_surf_transform(nm):
"""
This routine calculates the matrix, M, which transforms the
indicies of vectors defined in the lab frame basis to the
a surface frame defined such that:
zs is along the surface normal (parrallel to nm)
ys is the projection of the lab frame -y axis onto the surface
xs is defined to make it a right handed orthonormal set
(and is therefore in the surface plane)
Parameters:
-----------
* nm is the surface normal vector, defined in the
laboratory m-frame (i.e. pointing in an arbitrary
direction for a particular gonio setting)
- see gonio_psic.py for more details.
Notes:
------
The surface transform is defined as follows:
|e_xs| |e_x| |vx|
|e_ys| = F* |e_y| and F = |vy|
|e_zs| |e_z| |vz|
where e's are the (cartesian) basis vectors. Given a
vector [x,y,z] defined in the lab frame basis [e_x,e_y,e_z],
we can compute the indicies of this vector in the surface
basis [e_xs,e_ys,e_zs] from:
|xs| |x|
|ys| = M* |y|
|zs| |z|
where
M = transpose(inv(F)) = inv(transpose(F))
Note see lattice.py for more notes on general transforms, and see
gonio_psic.py for notes on calc of the surface normal vector.
"""
v_z = nm / cartesian_mag(nm)
v = num.array([0.,-1.,0.])
v_y = v - (num.dot(v,v_z) * v_z) / (cartesian_mag(v_z)**2.)
v_y = v_y / cartesian_mag(v_y)
v_x = num.cross(v_y,v_z)
v_x = v_x / cartesian_mag(v_x)
F = num.array([v_x,v_y,v_z])
M = num.linalg.inv(F.transpose())
return M
##################################################################
def surface_intercept(k,v):
"""
Find surface coordinates [x,y] when the tip of vector v is
projected into the surface plane along k. ie [x,y] for z=0
along k passing through the point defined by v
"""
# if k[2] = 0 then k is parallel to xy plane
# and no intercept is possible.
if k[2] == 0: return None
x_int = v[0] - (k[0]/k[2])*v[2]
y_int = v[1] - (k[1]/k[2])*v[2]
vi = num.array([x_int, y_int])
return vi
##########################################################################
def surface_intercept_bounds(k,v,diameter):
"""
Find z-intercepts within bounds
We assume that the plane is a circle of fixed diameter,
and scale back the intercept to the edge of the circle,
This is done along by subtracting a vector that is parrelel
to the in-plane projection of the k vector. The magnitude of
the subtracted vector is set so the result has magnitude
equal to the diameter.
If that doesnt result in an intercept then just rescale
the original intercept vector so its magnitude is the
diameter.
This is an approximate way to handle intercepts for
circular samples, not recommended for use(!)
"""
vi = surface_intercept(k,v)
r = cartesian_mag(vi)
if r <= diameter/2.:
return vi
# if r>diameter/2 need to scale it back
# Therefore solve the following for mag
# vi_new = vi - ks*mag/norm(ks),
# mag(vi_new) = diameter/2
# therefore end up with a quadratic:
ks = num.array(k[0:2])
a = 1.
b = -2. * (v[0]*ks[0] + vi[1]*ks[1])/cartesian_mag(ks)
c = cartesian_mag(vi)**2. - (diameter/2.)**2.
arg = b**2. - 4.*a*c
# make sure this method will result in a solution
if arg > 0.:
mag1 = (-b + num.sqrt(arg))/(2.*a)
mag2 = (-b - num.sqrt(arg))/(2.*a)
vi1 = vi - ks*mag1/cartesian_mag(ks)
vi2 = vi - ks*mag2/cartesian_mag(ks)
# chose soln that makes smallest angle
# with the original intercept
angle1 = num.fabs(cartesian_angle(vi,vi1))
angle2 = num.fabs(cartesian_angle(vi,vi2))
if angle1 < angle2:
return vi1
else:
return vi2
# otherwise just rescale the original vector
else:
vi = vi * (diameter/2.) / cartesian_mag(vi)
return vi
##########################################################################
##########################################################################
def test1():
r = 0.5
k = [0., 1., -0.1]
v = [.2, 0., 1.]
v1 = surface_intercept(k,v)
v2 = surface_intercept_bounds(k,v,2.*r)
print v1,v2
plot_circle(r)
plot_points([v1,v2])
pyplot.grid()
def test2():
import gonio_psic
psic = gonio_psic.test2(show=False)
psic.set_angles(phi=42.,chi=33,eta=20.,
mu=15.,nu=75.,delta=20.)
# get beam and detector vectors
beam = gonio_psic.beam_vectors(h=1.3,v=0.1)
det = gonio_psic.det_vectors(h=2.0,v=1.5,
nu=psic.angles['nu'],
delta=psic.angles['delta'])
# get sample vectors
sample = [[1.,1.], [.5,1.5], [-1.,1.], [-1.,-1.],[0.,.5],[1.,-1.]]
angles = {'phi':108.0007,'chi':0.4831}
sample = gonio_psic.sample_vectors(sample,angles=angles,gonio=psic)
#sample = 1.5
# compute active_area
print active_area(psic.nm,ki=psic.ki,kr=psic.kr,beam=beam,
det=det,sample=sample,plot=True)
##########################################################################
if __name__ == "__main__":
"""
test
"""
#test1()
test2()