[c97724e] | 1 | """ |
---|
[d459d4e] | 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 |
---|
[c97724e] | 5 | |
---|
[d459d4e] | 6 | Everything is in units of metres except specified otherwise (NOT TRUE!!!) |
---|
| 7 | Everything is in conventional units (nm for spin echo length) |
---|
[c97724e] | 8 | |
---|
| 9 | Wim Bouwman (w.g.bouwman@tudelft.nl), June 2013 |
---|
| 10 | """ |
---|
| 11 | |
---|
| 12 | from __future__ import division |
---|
| 13 | |
---|
[7ae2b7f] | 14 | import numpy as np # type: ignore |
---|
| 15 | from numpy import pi, exp # type: ignore |
---|
[589b740] | 16 | #from scipy.special import jv as besselj |
---|
[46d9f48] | 17 | from scipy.special import j0 |
---|
[589b740] | 18 | #from mpmath import j0 as j0 |
---|
| 19 | #from mpmath import besselj |
---|
| 20 | #from mpmath import mpf |
---|
[46d9f48] | 21 | from src.sas.sascalc.data_util.nxsunit import Converter |
---|
[eb8a82e] | 22 | #from sas.sasgui.perspectives.fitting.fitpage import FitPage |
---|
[02e70ff] | 23 | #import direct_model.DataMixin as model |
---|
| 24 | |
---|
[09ebe8c] | 25 | def make_q(q_max, Rmax): |
---|
[384d114] | 26 | r""" |
---|
[09ebe8c] | 27 | Return a $q$ vector suitable for SESANS covering from $2\pi/ (10 R_{\max})$ |
---|
[d459d4e] | 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. |
---|
[09ebe8c] | 33 | """ |
---|
[a154ad16] | 34 | from sas.sascalc.data_util.nxsunit import Converter |
---|
| 35 | |
---|
[c97724e] | 36 | q_min = dq = 0.1 * 2*pi / Rmax |
---|
[2684d45] | 37 | return np.arange(q_min, |
---|
| 38 | Converter(q_max[1])(q_max[0], |
---|
| 39 | units="1/A"), |
---|
| 40 | dq) |
---|
[46d9f48] | 41 | |
---|
| 42 | def Hankelconstructor(data): |
---|
| 43 | Rmax = 1000000 |
---|
[589b740] | 44 | #Rmax = #value in text box? |
---|
[46d9f48] | 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) |
---|
[589b740] | 50 | repq=np.array(repq,dtype='f') |
---|
| 51 | repSE=np.array(repSE,dtype='f') |
---|
[46d9f48] | 52 | H = dq / (2 * pi) * j0(repSE*repq)*repq |
---|
| 53 | return H0, H, q_calc |
---|
[02e70ff] | 54 | |
---|
[2ccb775] | 55 | def hankeltrafo(H0, H, Iq_calc): |
---|
| 56 | G0 = np.dot(H0, Iq_calc) |
---|
[46d9f48] | 57 | G = np.dot(H.T, Iq_calc) |
---|
[2ccb775] | 58 | P = G - G0 |
---|
[46d9f48] | 59 | return P # This is the logarithmic Polarization, not the linear one as found in Andersson2008! |
---|
[589b740] | 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 | |
---|