source: sasmodels/sasmodels/sesans.py @ 3023268

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

This version will be merged with master. Added 2 data files for testing.

  • Property mode set to 100644
File size: 8.8 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
17#from 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    qmono are the detector limits: can be a 1D or 2D array (depending on Q-limit or Qx,Qy limits)
63    """
64    if qmono == None:
65        result = call_hankel(data, q_calc, Iq_calc)
66    else:
67         nqmono = len(qmono)
68         if nqmono == 0: # if radiobutton hankel is active
69        #if FitPage.hankel.GetValue():
70            result = call_hankel(data, q_calc, Iq_calc)
71         elif nqmono == 1:
72            q = qmono[0]
73            result = call_HankelAccept(data, q_calc, Iq_calc, q, Iq_mono)
74         else: #if radiobutton Cosine is active
75            Qx, Qy = [qmono[0], qmono[1]]
76            Qx = np.reshape(Qx, nqx, nqy)
77            Qy = np.reshape(Qy, nqx, nqy)
78            Iq_mono = np.reshape(Iq_mono, nqx, nqy)
79            qx = Qx[0, :]
80            qy = Qy[:, 0]
81            result = call_Cosine2D(data, q_calc, Iq_calc, qx, qy, Iq_mono)
82
83    return result
84
85def call_hankel(data, q_calc, Iq_calc):
86 #   data.lam_unit='nm'
87    #return hankel((data.x, data.x_unit),
88      #            (data.lam, data.lam_unit),
89       #           (data.sample.thickness,
90        #           data.sample.thickness_unit),
91         #         q_calc, Iq_calc)
92
93    return hankel((data.x, data._xunit), q_calc, Iq_calc)
94 
95def call_HankelAccept(data, q_calc, Iq_calc, q_mono, Iq_mono):
96    return hankel(data.x, data.lam * 1e-9,
97                  data.sample.thickness / 10,
98                  q_calc, Iq_calc)
99                 
100def call_Cosine2D(data, q_calc, Iq_calc, qx, qy, Iq_mono):
101    return hankel(data.x, data.y, data.lam * 1e-9,
102                  data.sample.thickness / 10,
103                  q_calc, Iq_calc)
104                       
105def TotalScatter(model, parameters):  #Work in progress!!
106#    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)
107    allq = np.linspace(0,4*pi/wavelength,1000)
108    allIq = 1
109    integral = allq*allIq
110   
111
112
113def Cosine2D(wavelength, magfield, thickness, qy, qz, Iqy, Iqz, modelname): #Work in progress!! Needs to call model still
114#==============================================================================
115#     2D Cosine Transform if "wavelength" is a vector
116#==============================================================================
117#allq is the q-space needed to create the total scattering cross-section
118
119    Gprime = np.zeros_like(wavelength, 'd')
120    s = np.zeros_like(wavelength, 'd')
121    sd = np.zeros_like(wavelength, 'd')
122    Gprime = np.zeros_like(wavelength, 'd')
123    f = np.zeros_like(wavelength, 'd')
124    for i, wavelength_i in enumerate(wavelength):
125        z = magfield*wavelength_i
126        allq=np.linspace() #for calculating the Q-range of the  scattering power integral
127        allIq=np.linspace()  # This is the model applied to the allq q-space. Needs to refference the model somehow
128        alldq = (allq[1]-allq[0])*1e10
129        sigma[i]=wavelength[i]^2*thickness/2/pi*np.sum(allIq*allq*alldq)
130        s[i]=1-exp(-sigma)
131        for j, Iqy_j, qy_j in enumerate(qy):
132            for k, Iqz_k, qz_k in enumerate(qz):
133                Iq = np.sqrt(Iqy_j^2+Iqz_k^2)
134                q = np.sqrt(qy_j^2 + qz_k^2)
135                Gintegral = Iq*cos(z*Qz_k)
136                Gprime[i] += Gintegral
137#                sigma = wavelength^2*thickness/2/pi* allq[i]*allIq[i]
138#                s[i] += 1-exp(Totalscatter(modelname)*thickness)
139#                For now, work with standard 2-phase scatter
140
141
142                sd[i] += Iq
143        f[i] = 1-s[i]+sd[i]
144        P[i] = (1-sd[i]/f[i])+1/f[i]*Gprime[i]
145
146
147
148
149def HankelAccept(wavelength, magfield, thickness, q, Iq, theta, modelname):
150#==============================================================================
151#     HankelTransform with fixed circular acceptance angle (circular aperture) for Time of Flight SESANS
152#==============================================================================
153#acceptq is the q-space needed to create limited acceptance effect
154    SElength= wavelength*magfield
155    G = np.zeros_like(SElength, 'd')
156    threshold=2*pi*theta/wavelength
157    for i, SElength_i in enumerate(SElength):
158        allq=np.linspace() #for calculating the Q-range of the  scattering power integral
159        allIq=np.linspace()  # This is the model applied to the allq q-space. Needs to refference the model somehow
160        alldq = (allq[1]-allq[0])*1e10
161        sigma[i]=wavelength[i]^2*thickness/2/pi*np.sum(allIq*allq*alldq)
162        s[i]=1-exp(-sigma)
163
164        dq = (q[1]-q[0])*1e10
165        a = (x<threshold)
166        acceptq = a*q
167        acceptIq = a*Iq
168
169        G[i] = np.sum(besselj(0, acceptq*SElength_i)*acceptIq*acceptq*dq)
170
171#        G[i]=np.sum(integral)
172
173    G *= dq*1e10*2*pi
174
175    P = exp(thickness*wavelength**2/(4*pi**2)*(G-G[0]))
176   
177#def hankel(SElength, wavelength, thickness, q, Iq):
178def hankel(SElength, q, Iq):
179    r"""
180    Compute the expected SESANS polarization for a given SANS pattern.
181
182    Uses the hankel transform followed by the exponential.  The values for *zz*
183    (or spin echo length, or delta), wavelength and sample thickness should
184    come from the dataset.  $q$ should be chosen such that the oscillations
185    in $I(q)$ are well sampled (e.g., $5 \cdot 2 \pi/d_{\max}$).
186
187    *SElength* [A] is the set of $z$ points at which to compute the
188    Hankel transform
189
190    *wavelength* [m]  is the wavelength of each individual point *zz*
191
192    *thickness* [cm] is the sample thickness.
193
194    *q* [A$^{-1}$] is the set of $q$ points at which the model has been
195    computed. These should be equally spaced.
196
197    *I* [cm$^{-1}$] is the value of the SANS model at *q*
198    """
199
200    from sas.sascalc.data_util.nxsunit import Converter
201    #wavelength = Converter(wavelength[1])(wavelength[0],"A")
202    #thickness = Converter(thickness[1])(thickness[0],"A")
203    #Iq = Converter("1/cm")(Iq,"1/A") # All models default to inverse centimeters
204    SElength = Converter(SElength[1])(SElength[0],"A")
205
206    G = np.zeros_like(SElength, 'd')
207    for i, SElength_i in enumerate(SElength):
208        integral = besselj(0, q*SElength_i)*Iq*q
209        G[i] = np.sum(integral)
210    G0 = np.sum(Iq*q) #relation to ksi? For generalization to all models
211
212    # [m^-1] step size in q, needed for integration
213    dq = (q[1]-q[0])
214
215    # integration step, convert q into [m**-1] and 2 pi circle integration
216    G *= dq*2*pi
217    G0 = np.sum(Iq*q)*dq*2*np.pi
218
219    #P = exp(thickness*wavelength**2/(4*pi**2)*(G-G0))
220    P = (G-G0)/(4*pi**2)
221    #P=G-G0
222
223    return P
Note: See TracBrowser for help on using the repository browser.