source: sasmodels/explore/symint.py @ fac6657

core_shell_microgelsmagnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since fac6657 was fac23b3, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

play with cylinder integral

  • Property mode set to 100644
File size: 5.1 KB
Line 
1"""
2Explore integration of rotationally symmetric shapes
3"""
4
5from __future__ import print_function, division
6
7import os, sys
8sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
9
10import numpy as np
11from numpy import pi, sin, cos, sqrt, exp, expm1, degrees, log10
12from scipy.integrate import dblquad, simps, romb, romberg
13from scipy.special.orthogonal import p_roots
14import pylab
15
16from sasmodels.special import square
17from sasmodels.special import Gauss20Wt, Gauss20Z
18from sasmodels.special import Gauss76Wt, Gauss76Z
19from sasmodels.special import Gauss150Wt, Gauss150Z
20from sasmodels.special import sas_2J1x_x, sas_sinx_x, sas_3j1x_x
21
22SLD = 3.0
23SLD_SOLVENT = 6.3
24CONTRAST = SLD - SLD_SOLVENT
25
26def make_cylinder(radius, length):
27    def cylinder(qab, qc):
28        return sas_2J1x_x(qab*radius) * sas_sinx_x(qc*0.5*length)
29    cylinder.__doc__ = "cylinder radius=%g, length=%g"%(radius, length)
30    volume = pi*radius**2*length
31    norm = 1e-4*volume*CONTRAST**2
32    return norm, cylinder
33
34def make_sphere(radius):
35    def sphere(qab, qc):
36        q = sqrt(qab**2 + qc**2)
37        return sas_3j1x_x(q*radius)
38    sphere.__doc__ = "sphere radius=%g"%(radius,)
39    volume = 4*pi*radius**3/3
40    norm = 1e-4*volume*CONTRAST**2
41    return norm, sphere
42
43THETA_LOW, THETA_HIGH = 0, pi/2
44SCALE = 1
45
46
47def kernel(q, theta):
48    """
49    S(q) kernel for paracrystal forms.
50    """
51    qab = q*sin(theta)
52    qc = q*cos(theta)
53    return NORM*KERNEL(qab, qc)**2
54
55
56def gauss_quad(q, n=150):
57    """
58    Compute the integral using gaussian quadrature for n = 20, 76 or 150.
59    """
60    z, w = p_roots(n)
61    theta = (THETA_HIGH-THETA_LOW)*(z + 1)/2 + THETA_LOW
62    sin_theta = abs(sin(theta))
63    Zq = kernel(q=q, theta=theta)
64    return np.sum(Zq*w*sin_theta)*(THETA_HIGH-THETA_LOW)/2
65
66
67def gridded_integrals(q, n=300):
68    """
69    Compute the integral on a regular grid using rectangular, trapezoidal,
70    simpsons, and romberg integration.  Romberg integration requires that
71    the grid be of size n = 2**k + 1.
72    """
73    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
74    Zq = kernel(q=q, theta=theta)
75    Zq *= abs(sin(theta))
76    dx = theta[1]-theta[0]
77    print("rect", n, np.sum(Zq)*dx*SCALE)
78    print("trapz", n, np.trapz(Zq, dx=dx)*SCALE)
79    print("simpson", n, simps(Zq, dx=dx)*SCALE)
80    print("romb", n, romb(Zq, dx=dx)*SCALE)
81
82def scipy_romberg(q):
83    """
84    Compute the integral using romberg integration.  This function does not
85    complete in a reasonable time.  No idea if it is accurate.
86    """
87    evals = [0]
88    def outer(theta):
89        evals[0] += 1
90        return kernel(q, theta=theta)*abs(sin(theta))
91    result = romberg(outer, THETA_LOW, THETA_HIGH, divmax=100)*SCALE
92    print("scipy romberg", evals[0], result)
93
94def plot(q, n=300):
95    """
96    Plot the 2D surface that needs to be integrated in order to compute
97    the BCC S(q) at a particular q, dnn and d_factor.  *n* is the number
98    of points in the grid.
99    """
100    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
101    Zq = kernel(q=q, theta=theta)
102    Zq *= abs(sin(theta))
103    pylab.semilogy(degrees(theta), np.fmax(Zq, 1.e-6), label="Q=%g"%q)
104    pylab.title("%s I(q, theta) sin(theta)" % (KERNEL.__doc__,))
105    pylab.xlabel("theta (degrees)")
106    pylab.ylabel("Iq 1/cm")
107
108def Iq_trapz(q, n):
109    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
110    Zq = kernel(q=q, theta=theta)
111    Zq *= abs(sin(theta))
112    dx = theta[1]-theta[0]
113    return np.trapz(Zq, dx=dx)*SCALE
114
115def plot_Iq(q, n, form="trapz"):
116    if form == "trapz":
117        I = np.array([Iq_trapz(qk, n) for qk in q])
118    elif form == "gauss":
119        I = np.array([gauss_quad(qk, n) for qk in q])
120    pylab.loglog(q, I, label="%s, n=%d"%(form, n))
121    pylab.xlabel("q (1/A)")
122    pylab.ylabel("Iq (1/cm)")
123    pylab.title(KERNEL.__doc__ + " I(q) circular average")
124    return q, I
125
126NORM, KERNEL = make_cylinder(radius=10., length=100000.)
127#NORM, KERNEL = make_cylinder(radius=10., length=50000.)
128#NORM, KERNEL = make_cylinder(radius=10., length=20000.)
129#NORM, KERNEL = make_cylinder(radius=10., length=10000.)
130#NORM, KERNEL = make_cylinder(radius=10., length=5000.)
131#NORM, KERNEL = make_cylinder(radius=10., length=1000.)
132#NORM, KERNEL = make_cylinder(radius=10., length=500.)
133#NORM, KERNEL = make_cylinder(radius=10., length=100.)
134#NORM, KERNEL = make_cylinder(radius=10., length=30.)
135#NORM, KERNEL = make_sphere(radius=50.)
136
137if __name__ == "__main__":
138    Q = 0.8
139    for n in (20, 76, 150, 300, 1000): #, 10000, 30000):
140        print("gauss", n, gauss_quad(Q, n=n))
141    for k in (8, 10, 13, 16, 19):
142        gridded_integrals(Q, n=2**k+1)
143    #scipy_romberg(Q)
144    plot(0.5, n=2000)
145    plot(0.6, n=2000)
146    plot(0.8, n=2000)
147    pylab.legend()
148    pylab.figure()
149    #plot_Iq(np.logspace(-3, 0, 400), n=2**19+1, form="trapz")
150    q1, I1 = plot_Iq(np.logspace(-3, 0, 400), n=2**16+1, form="trapz")
151    #plot_Iq(np.logspace(-3, 0, 400), n=2**10+1, form="trapz")
152    q2, I2 = plot_Iq(np.logspace(-3, 0, 400), n=1024, form="gauss")
153    #plot_Iq(np.logspace(-3, 0, 400), n=300, form="gauss")
154    plot_Iq(np.logspace(-3, 0, 400), n=150, form="gauss")
155    plot_Iq(np.logspace(-3, 0, 400), n=76, form="gauss")
156    pylab.legend()
157    #pylab.figure()
158    #pylab.semilogx(q1, (I2 - I1)/I1)
159    pylab.show()
Note: See TracBrowser for help on using the repository browser.