1 | """ |
---|
2 | Conversion of scattering cross section from SANS (I(q), or rather, ds/dO) in absolute |
---|
3 | units (cm-1)into SESANS correlation function G using a Hankel transformation, then converting |
---|
4 | the SESANS correlation function into polarisation from the SESANS experiment |
---|
5 | |
---|
6 | Everything is in units of metres except specified otherwise (NOT TRUE!!!) |
---|
7 | Everything is in conventional units (nm for spin echo length) |
---|
8 | |
---|
9 | Wim Bouwman (w.g.bouwman@tudelft.nl), June 2013 |
---|
10 | """ |
---|
11 | |
---|
12 | from __future__ import division |
---|
13 | |
---|
14 | import numpy as np # type: ignore |
---|
15 | from numpy import pi, exp # type: ignore |
---|
16 | #from scipy.special import jv as besselj |
---|
17 | from scipy.special import j0 |
---|
18 | #from mpmath import j0 as j0 |
---|
19 | #from mpmath import besselj |
---|
20 | #from mpmath import mpf |
---|
21 | from src.sas.sascalc.data_util.nxsunit import Converter |
---|
22 | #from sas.sasgui.perspectives.fitting.fitpage import FitPage |
---|
23 | #import direct_model.DataMixin as model |
---|
24 | |
---|
25 | def make_q(q_max, Rmax): |
---|
26 | r""" |
---|
27 | Return a $q$ vector suitable for SESANS covering from $2\pi/ (10 R_{\max})$ |
---|
28 | to $q_max$. This is the integration range of the Hankel transform; bigger range and |
---|
29 | more points makes a better numerical integration. |
---|
30 | Smaller q_min will increase reliable spin echo length range. |
---|
31 | Rmax is the "radius" of the largest expected object and can be set elsewhere. |
---|
32 | q_max is determined by the acceptance angle of the SESANS instrument. |
---|
33 | """ |
---|
34 | from sas.sascalc.data_util.nxsunit import Converter |
---|
35 | |
---|
36 | q_min = dq = 0.1 * 2*pi / Rmax |
---|
37 | return np.arange(q_min, |
---|
38 | Converter(q_max[1])(q_max[0], |
---|
39 | units="1/A"), |
---|
40 | dq) |
---|
41 | |
---|
42 | def Hankelconstructor(data): |
---|
43 | Rmax = 1000000 |
---|
44 | #Rmax = #value in text box? |
---|
45 | q_calc = make_q(data.sample.zacceptance, Rmax) |
---|
46 | SElength = Converter(data._xunit)(data.x, "A") |
---|
47 | dq = q_calc[1] - q_calc[0] |
---|
48 | H0 = dq / (2 * pi) * q_calc |
---|
49 | repSE, repq = np.meshgrid(SElength,q_calc) |
---|
50 | repq=np.array(repq,dtype='f') |
---|
51 | repSE=np.array(repSE,dtype='f') |
---|
52 | H = dq / (2 * pi) * j0(repSE*repq)*repq |
---|
53 | return H0, H, q_calc |
---|
54 | |
---|
55 | def hankeltrafo(H0, H, Iq_calc): |
---|
56 | G0 = np.dot(H0, Iq_calc) |
---|
57 | G = np.dot(H.T, Iq_calc) |
---|
58 | P = G - G0 |
---|
59 | return P # This is the logarithmic Polarization, not the linear one as found in Andersson2008! |
---|
60 | |
---|
61 | |
---|
62 | class SesansTransform(object): |
---|
63 | #: Set of spin-echo lengths in the measured data |
---|
64 | SElength = None # type: np.ndarray |
---|
65 | #: Maximum acceptance of scattering vector in the spin echo encoding dimension (for ToF: Q of min(R) and max(lam)) |
---|
66 | zaccept = None # type: float |
---|
67 | #: Maximum size sensitivity; larger radius requires more computation |
---|
68 | Rmax = None # type: float |
---|
69 | #: q values to calculate when computing transform |
---|
70 | q = None # type: np.ndarray |
---|
71 | |
---|
72 | # transform arrays |
---|
73 | _H = None # type: np.ndarray |
---|
74 | _H0 = None # type: np.ndarray |
---|
75 | |
---|
76 | def set_transform(self, SE, zaccept, Rmax): |
---|
77 | if self.SE is None or len(SE) != len(self.SE) or np.any(SE != self.SE) or zaccept != self.zaccept or Rmax != self.Rmax: |
---|
78 | self.SE, self.zaccept, self.Rmax = SE, zaccept, Rmax |
---|
79 | self._set_q() |
---|
80 | self._set_hankel() |
---|
81 | |
---|
82 | def apply(self, Iq): |
---|
83 | G0 = np.dot(self._H0, Iq) |
---|
84 | G = np.dot(self._H.T, Iq) |
---|
85 | P = G - G0 |
---|
86 | return P |
---|
87 | |
---|
88 | def _set_q(self): |
---|
89 | q_min = dq = 0.1 * 2*pi / self.Rmax |
---|
90 | q_max = self.zaccept |
---|
91 | q=np.arange(q_min,q_max) |
---|
92 | self.q = q |
---|
93 | self.dq = dq |
---|
94 | |
---|
95 | def _set_hankel(self): |
---|
96 | #Rmax = #value in text box somewhere in FitPage? |
---|
97 | q = self.q |
---|
98 | dq = self.dq |
---|
99 | SElength = self.SE |
---|
100 | |
---|
101 | H0 = dq / (2 * pi) * q |
---|
102 | repSE, repq = np.meshgrid(SElength,q) |
---|
103 | repq=np.array(repq,dtype='f') |
---|
104 | repSE=np.array(repSE,dtype='f') |
---|
105 | H = dq / (2 * pi) * j0(repSE*repq)*repq |
---|
106 | |
---|
107 | self._H, self._H0 = H, H0 |
---|
108 | |
---|