source: sasmodels/sasmodels/sesans.py @ 0444c02

core_shell_microgelscostrafo411magnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 0444c02 was 0444c02, checked in by jhbakker, 8 years ago

Changes for SESANS integration, next is merge with ajj_sesans

  • Property mode set to 100644
File size: 8.5 KB
Line 
1"""
2Conversion of scattering cross section from SANS (I(q), or rather, ds/dO) in absolute
3units (cm-1)into SESANS correlation function G using a Hankel transformation, then converting
4the SESANS correlation function into polarisation from the SESANS experiment
5
6Everything is in units of metres except specified otherwise (NOT TRUE!!!)
7Everything is in conventional units (nm for spin echo length)
8
9Wim Bouwman (w.g.bouwman@tudelft.nl), June 2013
10"""
11
12from __future__ import division
13
14import numpy as np  # type: ignore
15from numpy import pi, exp  # type: ignore
16from scipy.special import jv as besselj
17from sas.sasgui.perspectives.fitting.fitpage import FitPage
18#import direct_model.DataMixin as model
19       
20def make_q(q_max, Rmax):
21    r"""
22    Return a $q$ vector suitable for SESANS covering from $2\pi/ (10 R_{\max})$
23    to $q_max$. This is the integration range of the Hankel transform; bigger range and
24    more points makes a better numerical integration.
25    Smaller q_min will increase reliable spin echo length range.
26    Rmax is the "radius" of the largest expected object and can be set elsewhere.
27    q_max is determined by the acceptance angle of the SESANS instrument.
28    """
29    from sas.sascalc.data_util.nxsunit import Converter
30
31    q_min = dq = 0.1 * 2*pi / Rmax
32    return np.arange(q_min,
33                     Converter(q_max[1])(q_max[0],
34                                         units="1/A"),
35                     dq)
36   
37def make_all_q(data):
38    """
39    Return a $q$ vector suitable for calculating the total scattering cross section for
40    calculating the effect of finite acceptance angles on Time of Flight SESANS instruments.
41    If no acceptance is given, or unwanted (set "unwanted" flag in paramfile), no all_q vector is needed.
42    If the instrument has a rectangular acceptance, 2 all_q vectors are needed.
43    If the instrument has a circular acceptance, 1 all_q vector is needed
44   
45    """
46    if not data.has_no_finite_acceptance:
47        return []
48    elif data.has_yz_acceptance(data):
49        # compute qx, qy
50        Qx, Qy = np.meshgrid(qx, qy)
51        return [Qx, Qy]
52    else:
53        # else only need q
54        # data.has_z_acceptance
55        return [q]
56
57def transform(data, q_calc, Iq_calc, qmono, Iq_mono):
58    """
59    Decides which transform type is to be used, based on the experiment data file contents (header)
60    (2016-03-19: currently controlled from parameters script)
61    nqmono is the number of q vectors to be used for the detector integration
62    """
63    nqmono = len(qmono)
64    #if nqmono == 0 # if radiobutton hankel is active
65    if FitPage.hankel.GetValue():
66        result = call_hankel(data, q_calc, Iq_calc)
67    elif nqmono == 1:
68        q = qmono[0]
69        result = call_HankelAccept(data, q_calc, Iq_calc, q, Iq_mono)
70    else: #if radiobutton Cosine is active
71        Qx, Qy = [qmono[0], qmono[1]]
72        Qx = np.reshape(Qx, nqx, nqy)
73        Qy = np.reshape(Qy, nqx, nqy)
74        Iq_mono = np.reshape(Iq_mono, nqx, nqy)
75        qx = Qx[0, :]
76        qy = Qy[:, 0]
77        result = call_Cosine2D(data, q_calc, Iq_calc, qx, qy, Iq_mono)
78
79    return result
80
81def call_hankel(data, q_calc, Iq_calc):
82    return hankel((data.x, data.x_unit),
83                  (data.lam, data.lam_unit),
84                  (data.sample.thickness,
85                   data.sample.thickness_unit),
86                  q_calc, Iq_calc)
87 
88def call_HankelAccept(data, q_calc, Iq_calc, q_mono, Iq_mono):
89    return hankel(data.x, data.lam * 1e-9,
90                  data.sample.thickness / 10,
91                  q_calc, Iq_calc)
92                 
93def call_Cosine2D(data, q_calc, Iq_calc, qx, qy, Iq_mono):
94    return hankel(data.x, data.y, data.lam * 1e-9,
95                  data.sample.thickness / 10,
96                  q_calc, Iq_calc)
97                       
98def TotalScatter(model, parameters):  #Work in progress!!
99#    Calls a model with existing model parameters already in place, then integrate the product of q and I(q) from 0 to (4*pi/lambda)
100    allq = np.linspace(0,4*pi/wavelength,1000)
101    allIq = 1
102    integral = allq*allIq
103   
104
105
106def Cosine2D(wavelength, magfield, thickness, qy, qz, Iqy, Iqz, modelname): #Work in progress!! Needs to call model still
107#==============================================================================
108#     2D Cosine Transform if "wavelength" is a vector
109#==============================================================================
110#allq is the q-space needed to create the total scattering cross-section
111
112    Gprime = np.zeros_like(wavelength, 'd')
113    s = np.zeros_like(wavelength, 'd')
114    sd = np.zeros_like(wavelength, 'd')
115    Gprime = np.zeros_like(wavelength, 'd')
116    f = np.zeros_like(wavelength, 'd')
117    for i, wavelength_i in enumerate(wavelength):
118        z = magfield*wavelength_i
119        allq=np.linspace() #for calculating the Q-range of the  scattering power integral
120        allIq=np.linspace()  # This is the model applied to the allq q-space. Needs to refference the model somehow
121        alldq = (allq[1]-allq[0])*1e10
122        sigma[i]=wavelength[i]^2*thickness/2/pi*np.sum(allIq*allq*alldq)
123        s[i]=1-exp(-sigma)
124        for j, Iqy_j, qy_j in enumerate(qy):
125            for k, Iqz_k, qz_k in enumerate(qz):
126                Iq = np.sqrt(Iqy_j^2+Iqz_k^2)
127                q = np.sqrt(qy_j^2 + qz_k^2)
128                Gintegral = Iq*cos(z*Qz_k)
129                Gprime[i] += Gintegral
130#                sigma = wavelength^2*thickness/2/pi* allq[i]*allIq[i]
131#                s[i] += 1-exp(Totalscatter(modelname)*thickness)
132#                For now, work with standard 2-phase scatter
133
134
135                sd[i] += Iq
136        f[i] = 1-s[i]+sd[i]
137        P[i] = (1-sd[i]/f[i])+1/f[i]*Gprime[i]
138
139
140
141
142def HankelAccept(wavelength, magfield, thickness, q, Iq, theta, modelname):
143#==============================================================================
144#     HankelTransform with fixed circular acceptance angle (circular aperture) for Time of Flight SESANS
145#==============================================================================
146#acceptq is the q-space needed to create limited acceptance effect
147    SElength= wavelength*magfield
148    G = np.zeros_like(SElength, 'd')
149    threshold=2*pi*theta/wavelength
150    for i, SElength_i in enumerate(SElength):
151        allq=np.linspace() #for calculating the Q-range of the  scattering power integral
152        allIq=np.linspace()  # This is the model applied to the allq q-space. Needs to refference the model somehow
153        alldq = (allq[1]-allq[0])*1e10
154        sigma[i]=wavelength[i]^2*thickness/2/pi*np.sum(allIq*allq*alldq)
155        s[i]=1-exp(-sigma)
156
157        dq = (q[1]-q[0])*1e10
158        a = (x<threshold)
159        acceptq = a*q
160        acceptIq = a*Iq
161
162        G[i] = np.sum(besselj(0, acceptq*SElength_i)*acceptIq*acceptq*dq)
163
164#        G[i]=np.sum(integral)
165
166    G *= dq*1e10*2*pi
167
168    P = exp(thickness*wavelength**2/(4*pi**2)*(G-G[0]))
169   
170def hankel(SElength, wavelength, thickness, q, Iq):
171    r"""
172    Compute the expected SESANS polarization for a given SANS pattern.
173
174    Uses the hankel transform followed by the exponential.  The values for *zz*
175    (or spin echo length, or delta), wavelength and sample thickness should
176    come from the dataset.  $q$ should be chosen such that the oscillations
177    in $I(q)$ are well sampled (e.g., $5 \cdot 2 \pi/d_{\max}$).
178
179    *SElength* [A] is the set of $z$ points at which to compute the
180    Hankel transform
181
182    *wavelength* [m]  is the wavelength of each individual point *zz*
183
184    *thickness* [cm] is the sample thickness.
185
186    *q* [A$^{-1}$] is the set of $q$ points at which the model has been
187    computed. These should be equally spaced.
188
189    *I* [cm$^{-1}$] is the value of the SANS model at *q*
190    """
191
192    from sas.sascalc.data_util.nxsunit import Converter
193    wavelength = Converter(wavelength[1])(wavelength[0],"A")
194    thickness = Converter(thickness[1])(thickness[0],"A")
195    Iq = Converter("1/cm")(Iq,"1/A") # All models default to inverse centimeters
196    SElength = Converter(SElength[1])(SElength[0],"A")
197
198    G = np.zeros_like(SElength, 'd')
199#==============================================================================
200#     Hankel Transform method if "wavelength" is a scalar; mono-chromatic SESANS
201#==============================================================================
202    for i, SElength_i in enumerate(SElength):
203        integral = besselj(0, q*SElength_i)*Iq*q
204        G[i] = np.sum(integral)
205    G0 = np.sum(Iq*q)
206
207    # [m^-1] step size in q, needed for integration
208    dq = (q[1]-q[0])
209
210    # integration step, convert q into [m**-1] and 2 pi circle integration
211    G *= dq*2*pi
212    G0 = np.sum(Iq*q)*dq*2*np.pi
213
214    P = exp(thickness*wavelength**2/(4*pi**2)*(G-G0))
215
216    return P
Note: See TracBrowser for help on using the repository browser.