[d1fe925] | 1 | """ |
---|
| 2 | Conversion of scattering cross section from SANS in absolute |
---|
| 3 | units into SESANS using a Hankel transformation |
---|
| 4 | |
---|
| 5 | Everything is in units of metres except specified otherwise |
---|
| 6 | |
---|
| 7 | Wim Bouwman (w.g.bouwman@tudelft.nl), June 2013 |
---|
| 8 | """ |
---|
| 9 | |
---|
| 10 | from __future__ import division |
---|
| 11 | |
---|
| 12 | import numpy as np |
---|
| 13 | from numpy import pi, exp |
---|
| 14 | |
---|
| 15 | from scipy.special import jv as besselj |
---|
| 16 | |
---|
| 17 | def make_q(q_zmax, Rmax): |
---|
| 18 | q_min = dq = 0.1 * 2*pi / Rmax |
---|
| 19 | #q_min = 0.00003 |
---|
| 20 | return np.arange(q_min, q_zmax, dq) |
---|
| 21 | |
---|
| 22 | # TODO: dead code; for now the call to the hankel transform happens in BumpsModel |
---|
| 23 | class SesansCalculator: |
---|
| 24 | def __init__(self, kernel, q_zmax, Rmax, SElength, wavelength, thickness): |
---|
| 25 | self._set_kernel(kernel, q_zmax, Rmax) |
---|
| 26 | self.SElength = SElength |
---|
| 27 | self.wavelength = wavelength |
---|
| 28 | self.thickness = thickness |
---|
| 29 | |
---|
| 30 | def _set_kernel(self, kernel, q_zmax, Rmax): |
---|
| 31 | kernel_input = kernel.make_input([make_q(q_zmax, Rmax)]) |
---|
| 32 | self.sans_calculator = kernel(kernel_input) |
---|
| 33 | |
---|
| 34 | def __call__(self, pars, pd_pars, cutoff=1e-5): |
---|
| 35 | Iq = self.sans_calculator(pars, pd_pars, cutoff) |
---|
| 36 | P = hankel(self.SElength, self.wavelength, self.thickness, self.q, Iq) |
---|
| 37 | self.Iq = Iq |
---|
| 38 | return P |
---|
| 39 | |
---|
| 40 | def hankel(SElength, wavelength, thickness, q, Iq): |
---|
| 41 | """ |
---|
| 42 | Compute the expected SESANS polarization for a given SANS pattern. |
---|
| 43 | |
---|
| 44 | Uses the hankel transform followed by the exponential. The values |
---|
| 45 | for zz (or spin echo length, or delta), wavelength and sample thickness |
---|
| 46 | information should come from the dataset. *q* should be chosen such |
---|
| 47 | that the oscillations in *I(q)* are well sampled (e.g., 5*2*pi/d_max). |
---|
| 48 | |
---|
| 49 | *SElength* [A] is the set of z points at which to compute the hankel transform |
---|
| 50 | |
---|
| 51 | *wavelength* [m] is the wavelength of each individual point *zz* |
---|
| 52 | |
---|
| 53 | *thickness* [cm] is the sample thickness. |
---|
| 54 | |
---|
| 55 | *q* [A^{-1}] is the set of q points at which the model has been computed. |
---|
| 56 | These should be equally spaced. |
---|
| 57 | |
---|
| 58 | *I* [cm^{-1}] is the value of the SANS model at *q* |
---|
| 59 | """ |
---|
| 60 | G = np.zeros(len(SElength), 'd') |
---|
| 61 | for i in range(len(SElength)): |
---|
| 62 | integr = besselj(0,q*SElength[i])*Iq*q |
---|
| 63 | G[i] = np.sum(integr) |
---|
| 64 | dq=(q[1]-q[0])*1e10 # [m^-1] step size in q, needed for integration |
---|
| 65 | G *= dq*1e10*2*pi # integr step, conver q into [m**-1] and 2 pi circle integr |
---|
| 66 | P = exp(thickness*wavelength**2/(4*pi**2)*(G-G[0])) |
---|
| 67 | |
---|
| 68 | return P |
---|