source: sasmodels/sasmodels/sesans.py @ a154ad16

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since a154ad16 was a154ad16, checked in by awashington, 8 years ago

Fix one of the unit issues with make_q

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