source: sasmodels/sasmodels/sesans.py @ d554bd7

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

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