Changes in explore/realspace.py [a1c32c2:8fb2a94] in sasmodels


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • explore/realspace.py

    ra1c32c2 r8fb2a94  
    4444    """ 
    4545    return Rx(phi)*Ry(theta)*Rz(psi) 
     46 
     47def apply_view(points, view): 
     48    """ 
     49    Apply the view transform (theta, phi, psi) to a set of points. 
     50 
     51    Points are stored in a 3 x n numpy array. 
     52 
     53    View angles are in degrees. 
     54    """ 
     55    theta, phi, psi = view 
     56    return np.asarray((Rz(phi)*Ry(theta)*Rz(psi))*np.matrix(points.T)).T 
     57 
     58 
     59def invert_view(qx, qy, view): 
     60    """ 
     61    Return (qa, qb, qc) for the (theta, phi, psi) view angle at detector 
     62    pixel (qx, qy). 
     63 
     64    View angles are in degrees. 
     65    """ 
     66    theta, phi, psi = view 
     67    q = np.vstack((qx.flatten(), qy.flatten(), 0*qx.flatten())) 
     68    return np.asarray((Rz(-psi)*Ry(-theta)*Rz(-phi))*np.matrix(q)) 
     69 
    4670 
    4771class Shape: 
     
    219243        values = self.value.repeat(points.shape[0]) 
    220244        return values, self._adjust(points) 
     245 
     246NUMBA = False 
     247if NUMBA: 
     248    from numba import njit 
     249    @njit("f8[:](f8[:],f8[:],f8[:],f8[:],f8[:],f8[:],f8[:])") 
     250    def _Iqxy(values, x, y, z, qa, qb, qc): 
     251        Iq = np.zeros_like(qa) 
     252        for j in range(len(Iq)): 
     253            total = 0. + 0j 
     254            for k in range(len(Iq)): 
     255                total += values[k]*np.exp(1j*(qa[j]*x[k] + qb[j]*y[k] + qc[j]*z[k])) 
     256            Iq[j] = abs(total)**2 
     257        return Iq 
     258 
     259def calc_Iqxy(qx, qy, rho, points, volume=1.0, view=(0, 0, 0)): 
     260    qx, qy = np.broadcast_arrays(qx, qy) 
     261    qa, qb, qc = invert_view(qx, qy, view) 
     262    rho, volume = np.broadcast_arrays(rho, volume) 
     263    values = rho*volume 
     264    x, y, z = points.T 
     265 
     266    # I(q) = |sum V(r) rho(r) e^(1j q.r)|^2 / sum V(r) 
     267    if NUMBA: 
     268        Iq = _Iqxy(values, x, y, z, qa.flatten(), qb.flatten(), qc.flatten()) 
     269    else: 
     270        Iq = [abs(np.sum(values*np.exp(1j*(qa_k*x + qb_k*y + qc_k*z))))**2 
     271              for qa_k, qb_k, qc_k in zip(qa.flat, qb.flat, qc.flat)] 
     272    return np.asarray(Iq).reshape(qx.shape) / np.sum(volume) 
    221273 
    222274def _calc_Pr_nonuniform(r, rho, points): 
     
    239291            print("processing %d of %d"%(k, len(rho)-1)) 
    240292    Pr = extended_Pr[1:-1] 
    241     return Pr / Pr.max() 
    242  
    243 def calc_Pr(r, rho, points): 
    244     # P(r) with uniform steps in r is 3x faster; check if we are uniform 
    245     # before continuing 
    246     if np.max(np.abs(np.diff(r) - r[0])) > r[0]*0.01: 
    247         return _calc_Pr_nonuniform(r, rho, points) 
    248  
     293    return Pr 
     294 
     295def _calc_Pr_uniform(r, rho, points): 
    249296    # Make Pr a little be bigger than necessary so that only distances 
    250297    # min < d < max end up in Pr 
    251     n_max = len(r) 
     298    dr, n_max = r[0], len(r) 
    252299    extended_Pr = np.zeros(n_max+1, 'd') 
    253300    t0 = time.clock() 
    254301    t_next = t0 + 3 
    255     row_zero = np.zeros(len(rho), 'i') 
    256302    for k, rho_k in enumerate(rho[:-1]): 
    257303        distances = sqrt(np.sum((points[k] - points[k+1:])**2, axis=1)) 
    258304        weights = rho_k * rho[k+1:] 
    259         index = np.minimum(np.asarray(distances/r[0], 'i'), n_max) 
     305        index = np.minimum(np.asarray(distances/dr, 'i'), n_max) 
    260306        # Note: indices may be duplicated, so "Pr[index] += w" will not work!! 
    261307        extended_Pr += np.bincount(index, weights, n_max+1) 
     
    269315    # we are only accumulating the upper triangular distances. 
    270316    #Pr = Pr * 2 / len(rho)**2 
    271     return Pr / Pr.max() 
     317    return Pr 
    272318 
    273319    # Can get an additional 2x by going to C.  Cuda/OpenCL will allow even 
     
    291337} 
    292338""" 
     339 
     340if NUMBA: 
     341    @njit("f8[:](f8[:], f8[:], f8[:,:])") 
     342    def _calc_Pr_uniform_jit(r, rho, points): 
     343        dr = r[0] 
     344        n_max = len(r) 
     345        Pr = np.zeros_like(r) 
     346        for j in range(len(rho) - 1): 
     347            x, y, z = points[j, 0], points[j, 1], points[j, 2] 
     348            for k in range(j+1, len(rho)): 
     349                distance = sqrt((x - points[k, 0])**2 
     350                                + (y - points[k, 1])**2 
     351                                + (z - points[k, 2])**2) 
     352                index = int(distance/dr) 
     353                if index < n_max: 
     354                    Pr[index] += rho[j] * rho[k] 
     355        # Make Pr independent of sampling density.  The factor of 2 comes because 
     356        # we are only accumulating the upper triangular distances. 
     357        #Pr = Pr * 2 / len(rho)**2 
     358        return Pr 
     359 
     360 
     361def calc_Pr(r, rho, points): 
     362    # P(r) with uniform steps in r is 3x faster; check if we are uniform 
     363    # before continuing 
     364    if np.max(np.abs(np.diff(r) - r[0])) > r[0]*0.01: 
     365        Pr = _calc_Pr_nonuniform(r, rho, points) 
     366    else: 
     367        if NUMBA: 
     368            Pr = _calc_Pr_uniform_jit(r, rho, points) 
     369        else: 
     370            Pr = _calc_Pr_uniform(r, rho, points) 
     371    return Pr / Pr.max() 
     372 
    293373 
    294374def j0(x): 
     
    333413        plt.legend() 
    334414 
     415def plot_calc_2d(qx, qy, Iqxy, theory=None): 
     416    import matplotlib.pyplot as plt 
     417    qx, qy = bin_edges(qx), bin_edges(qy) 
     418    #qx, qy = np.meshgrid(qx, qy) 
     419    if theory is not None: 
     420        plt.subplot(121) 
     421    plt.pcolormesh(qx, qy, np.log10(Iqxy)) 
     422    plt.xlabel('qx (1/A)') 
     423    plt.ylabel('qy (1/A)') 
     424    if theory is not None: 
     425        plt.subplot(122) 
     426        plt.pcolormesh(qx, qy, np.log10(theory)) 
     427        plt.xlabel('qx (1/A)') 
     428 
    335429def plot_points(rho, points): 
    336430    import mpl_toolkits.mplot3d 
     
    343437        pass 
    344438    n = len(points) 
    345     index = np.random.choice(n, size=1000) if n > 1000 else slice(None, None) 
     439    #print("len points", n) 
     440    index = np.random.choice(n, size=500) if n > 500 else slice(None, None) 
    346441    ax.scatter(points[index, 0], points[index, 1], points[index, 2], c=rho[index]) 
    347442    #low, high = points.min(axis=0), points.max(axis=0) 
    348443    #ax.axis([low[0], high[0], low[1], high[1], low[2], high[2]]) 
    349444    ax.autoscale(True) 
     445 
     446def check_shape(shape, fn=None): 
     447    rho_solvent = 0 
     448    q = np.logspace(-3, 0, 200) 
     449    r = shape.r_bins(q, r_step=0.01) 
     450    sampling_density = 6*5000 / shape.volume() 
     451    rho, points = shape.sample(sampling_density) 
     452    t0 = time.time() 
     453    Pr = calc_Pr(r, rho-rho_solvent, points) 
     454    print("calc Pr time", time.time() - t0) 
     455    Iq = calc_Iq(q, r, Pr) 
     456    theory = (q, fn(q)) if fn is not None else None 
     457 
     458    import pylab 
     459    #plot_points(rho, points); pylab.figure() 
     460    plot_calc(r, Pr, q, Iq, theory=theory) 
     461    pylab.show() 
     462 
     463def check_shape_2d(shape, fn=None, view=(0, 0, 0)): 
     464    rho_solvent = 0 
     465    nq, qmax = 100, 1.0 
     466    qx = np.linspace(0.0, qmax, nq) 
     467    qy = np.linspace(0.0, qmax, nq) 
     468    Qx, Qy = np.meshgrid(qx, qy) 
     469    sampling_density = 50000 / shape.volume() 
     470    #t0 = time.time() 
     471    rho, points = shape.sample(sampling_density) 
     472    #print("sample time", time.time() - t0) 
     473    t0 = time.time() 
     474    Iqxy = calc_Iqxy(Qx, Qy, rho, points, view=view) 
     475    print("calc time", time.time() - t0) 
     476    theory = fn(Qx, Qy) if fn is not None else None 
     477    Iqxy += 0.001 * Iqxy.max() 
     478    if theory is not None: 
     479        theory += 0.001 * theory.max() 
     480 
     481    import pylab 
     482    #plot_points(rho, points); pylab.figure() 
     483    plot_calc_2d(qx, qy, Iqxy, theory=theory) 
     484    pylab.show() 
     485 
     486def sas_sinx_x(x): 
     487    with np.errstate(all='ignore'): 
     488        retvalue = sin(x)/x 
     489    retvalue[x == 0.] = 1. 
     490    return retvalue 
    350491 
    351492def sas_2J1x_x(x): 
     
    373514    Iq = Iq/Iq[0] 
    374515    return Iq 
     516 
     517def cylinder_Iqxy(qx, qy, radius, length, view=(0, 0, 0)): 
     518    qa, qb, qc = invert_view(qx, qy, view) 
     519    qab = np.sqrt(qa**2 + qb**2) 
     520    Fq = sas_2J1x_x(qab*radius) * j0(qc*length/2) 
     521    Iq = Fq**2 
     522    return Iq.reshape(qx.shape) 
    375523 
    376524def sphere_Iq(q, radius): 
     
    415563    return Iq/Iq[0] 
    416564 
    417 def check_shape(shape, fn=None): 
    418     rho_solvent = 0 
    419     q = np.logspace(-3, 0, 200) 
    420     r = shape.r_bins(q, r_step=0.01) 
    421     sampling_density = 15000 / shape.volume() 
    422     rho, points = shape.sample(sampling_density) 
    423     Pr = calc_Pr(r, rho-rho_solvent, points) 
    424     Iq = calc_Iq(q, r, Pr) 
    425     theory = (q, fn(q)) if fn is not None else None 
    426  
    427     import pylab 
    428     #plot_points(rho, points); pylab.figure() 
    429     plot_calc(r, Pr, q, Iq, theory=theory) 
    430     pylab.show() 
     565def csbox_Iqxy(qx, qy, a, b, c, da, db, dc, slda, sldb, sldc, sld_core, view=(0,0,0)): 
     566    qa, qb, qc = invert_view(qx, qy, view) 
     567 
     568    sld_solvent = 0 
     569    overlapping = False 
     570    dr0 = sld_core - sld_solvent 
     571    drA, drB, drC = slda-sld_solvent, sldb-sld_solvent, sldc-sld_solvent 
     572    tA, tB, tC = a + 2*da, b + 2*db, c + 2*dc 
     573    siA = a*sas_sinx_x(a*qa/2) 
     574    siB = b*sas_sinx_x(b*qb/2) 
     575    siC = c*sas_sinx_x(c*qc/2) 
     576    siAt = tA*sas_sinx_x(tA*qa/2) 
     577    siBt = tB*sas_sinx_x(tB*qb/2) 
     578    siCt = tC*sas_sinx_x(tC*qc/2) 
     579    Fq = (dr0*siA*siB*siC 
     580          + drA*(siAt-siA)*siB*siC 
     581          + drB*siA*(siBt-siB)*siC 
     582          + drC*siA*siB*(siCt-siC)) 
     583    Iq = Fq**2 
     584    return Iq.reshape(qx.shape) 
    431585 
    432586def check_cylinder(radius=25, length=125, rho=2.): 
     
    434588    fn = lambda q: cylinder_Iq(q, radius, length) 
    435589    check_shape(shape, fn) 
     590 
     591def check_cylinder_2d(radius=25, length=125, rho=2., view=(0, 0, 0)): 
     592    shape = EllipticalCylinder(radius, radius, length, rho) 
     593    fn = lambda qx, qy, view=view: cylinder_Iqxy(qx, qy, radius, length, view=view) 
     594    check_shape_2d(shape, fn, view=view) 
     595 
     596def check_cylinder_2d_lattice(radius=25, length=125, rho=2., 
     597                              view=(0, 0, 0)): 
     598    nx, dx = 1, 2*radius 
     599    ny, dy = 30, 2*radius 
     600    nz, dz = 30, length 
     601    dx, dy, dz = 2*dx, 2*dy, 2*dz 
     602    def center(*args): 
     603        sigma = 0.333 
     604        space = 2 
     605        return [(space*n+np.random.randn()*sigma)*x for n, x in args] 
     606    shapes = [EllipticalCylinder(radius, radius, length, rho, 
     607                                 #center=(ix*dx, iy*dy, iz*dz) 
     608                                 orientation=np.random.randn(3)*0, 
     609                                 center=center((ix, dx), (iy, dy), (iz, dz)) 
     610                                ) 
     611              for ix in range(nx) 
     612              for iy in range(ny) 
     613              for iz in range(nz)] 
     614    shape = Composite(shapes) 
     615    fn = lambda qx, qy, view=view: cylinder_Iqxy(qx, qy, radius, length, view=view) 
     616    check_shape_2d(shape, fn, view=view) 
    436617 
    437618def check_sphere(radius=125, rho=2): 
     
    449630    side_c2 = copy(side_c).shift(0, 0, -c-dc) 
    450631    shape = Composite((core, side_a, side_b, side_c, side_a2, side_b2, side_c2)) 
    451     fn = lambda q: csbox_Iq(q, a, b, c, da, db, dc, slda, sldb, sldc, sld_core) 
    452     check_shape(shape, fn) 
     632    def fn(q): 
     633        return csbox_Iq(q, a, b, c, da, db, dc, slda, sldb, sldc, sld_core) 
     634    #check_shape(shape, fn) 
     635 
     636    view = (20, 30, 40) 
     637    def fn_xy(qx, qy): 
     638        return csbox_Iqxy(qx, qy, a, b, c, da, db, dc, 
     639                          slda, sldb, sldc, sld_core, view=view) 
     640    check_shape_2d(shape, fn_xy, view=view) 
    453641 
    454642if __name__ == "__main__": 
    455643    check_cylinder(radius=10, length=40) 
     644    #check_cylinder_2d(radius=10, length=40, view=(90,30,0)) 
     645    #check_cylinder_2d_lattice(radius=10, length=50, view=(90,30,0)) 
    456646    #check_sphere() 
    457647    #check_csbox() 
Note: See TracChangeset for help on using the changeset viewer.