- Timestamp:
- Mar 31, 2018 9:22:09 PM (6 years ago)
- Branches:
- master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- 9a7ef88
- Parents:
- 5bc6d21 (diff), b3703f5 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent. - Location:
- sasmodels
- Files:
-
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/compare.py
r3221de0 rd86f0fc 718 718 model = core.build_model(model_info, dtype=dtype, platform="ocl") 719 719 calculator = DirectModel(data, model, cutoff=cutoff) 720 engine_type = calculator._model.__class__.__name__.replace('Model', '').upper()720 engine_type = calculator._model.__class__.__name__.replace('Model', '').upper() 721 721 bits = calculator._model.dtype.itemsize*8 722 722 precision = "fast" if getattr(calculator._model, 'fast', False) else str(bits) … … 1491 1491 vmin, vmax = limits 1492 1492 self.limits = vmax*1e-7, 1.3*vmax 1493 import pylab; pylab.clf() 1493 import pylab 1494 pylab.clf() 1494 1495 plot_models(self.opts, result, limits=self.limits) 1495 1496 -
sasmodels/data.py
rf549e37 rd86f0fc 706 706 else: 707 707 vmin_scaled, vmax_scaled = vmin, vmax 708 nx, ny = len(data.x_bins), len(data.y_bins)708 #nx, ny = len(data.x_bins), len(data.y_bins) 709 709 x_bins, y_bins, image = _build_matrix(data, plottable) 710 710 plt.imshow(image, … … 772 772 if loop >= max_loop: # this protects never-ending loop 773 773 break 774 image = _fillup_pixels( self,image=image, weights=weights)774 image = _fillup_pixels(image=image, weights=weights) 775 775 loop += 1 776 776 … … 817 817 return x_bins, y_bins 818 818 819 def _fillup_pixels( self,image=None, weights=None):819 def _fillup_pixels(image=None, weights=None): 820 820 """ 821 821 Fill z values of the empty cells of 2d image matrix -
sasmodels/generate.py
r6cbdcd4 rd86f0fc 169 169 170 170 import sys 171 from os.path import abspath, dirname, join as joinpath, exists, getmtime 171 from os import environ 172 from os.path import abspath, dirname, join as joinpath, exists, getmtime, sep 172 173 import re 173 174 import string … … 289 290 import loops. 290 291 """ 291 if (info.source and any(lib.startswith('lib/gauss') for lib in info.source)): 292 import os.path 292 if info.source and any(lib.startswith('lib/gauss') for lib in info.source): 293 293 from .gengauss import gengauss 294 path = os.path.join(MODEL_PATH, "lib", "gauss%d.c"%n)295 if not os.path.exists(path):294 path = joinpath(MODEL_PATH, "lib", "gauss%d.c"%n) 295 if not exists(path): 296 296 gengauss(n, path) 297 297 info.source = ["lib/gauss%d.c"%n if lib.startswith('lib/gauss') 298 298 else lib for lib in info.source] 299 299 300 300 def format_units(units): … … 320 320 for w, h in zip(column_widths, PARTABLE_HEADERS)] 321 321 322 sep= " ".join("="*w for w in column_widths)322 underbar = " ".join("="*w for w in column_widths) 323 323 lines = [ 324 sep,324 underbar, 325 325 " ".join("%-*s" % (w, h) 326 326 for w, h in zip(column_widths, PARTABLE_HEADERS)), 327 sep,327 underbar, 328 328 ] 329 329 for p in pars: … … 334 334 "%*g" % (column_widths[3], p.default), 335 335 ])) 336 lines.append( sep)336 lines.append(underbar) 337 337 return "\n".join(lines) 338 338 … … 612 612 """ 613 613 spaces = " "*depth 614 sep= "\n" + spaces615 return spaces + sep.join(s.split("\n"))614 interline_separator = "\n" + spaces 615 return spaces + interline_separator.join(s.split("\n")) 616 616 617 617 … … 619 619 def load_template(filename): 620 620 # type: (str) -> str 621 """ 622 Load template file from sasmodels resource directory. 623 """ 621 624 path = joinpath(DATA_PATH, filename) 622 625 mtime = getmtime(path) … … 900 903 kernel_module = load_custom_kernel_module(model_name) 901 904 else: 902 from sasmodels import models 903 __import__('sasmodels.models.'+model_name) 904 kernel_module = getattr(models, model_name, None) 905 try: 906 from sasmodels import models 907 __import__('sasmodels.models.'+model_name) 908 kernel_module = getattr(models, model_name, None) 909 except ImportError: 910 # If the model isn't a built in model, try the plugin directory 911 plugin_path = environ.get('SAS_MODELPATH', None) 912 if plugin_path is not None: 913 file_name = model_name.split(sep)[-1] 914 model_name = plugin_path + sep + file_name + ".py" 915 kernel_module = load_custom_kernel_module(model_name) 916 else: 917 raise 905 918 return kernel_module 906 919 -
sasmodels/guyou.py
r0d5a655 rb3703f5 31 31 # 32 32 # 2017-11-01 Paul Kienzle 33 # * converted to python, using degrees rather than radians 33 # * converted to python, with degrees rather than radians 34 """ 35 Convert between latitude-longitude and Guyou map coordinates. 36 """ 37 34 38 from __future__ import division, print_function 35 39 36 40 import numpy as np 37 from numpy import sqrt, pi, tan, cos, sin, log, exp, arctan2 as atan2, sign, radians, degrees 41 from numpy import sqrt, pi, tan, cos, sin, sign, radians, degrees 42 from numpy import sinh, arctan as atan 43 44 # scipy version of special functions 45 from scipy.special import ellipj as ellipticJ, ellipkinc as ellipticF 38 46 39 47 _ = """ … … 59 67 """ 60 68 61 # scipy version of special functions62 from scipy.special import ellipj as ellipticJ, ellipkinc as ellipticF63 from numpy import sinh, sign, arctan as atan64 65 69 def ellipticJi(u, v, m): 66 70 scalar = np.isscalar(u) and np.isscalar(v) and np.isscalar(m) 67 71 u, v, m = np.broadcast_arrays(u, v, m) 68 result = np.empty_like([u, u,u], 'D')69 real = v==070 imag = u==072 result = np.empty_like([u, u, u], 'D') 73 real = (v == 0) 74 imag = (u == 0) 71 75 mixed = ~(real|imag) 72 76 result[:, real] = _ellipticJi_real(u[real], m[real]) 73 77 result[:, imag] = _ellipticJi_imag(v[imag], m[imag]) 74 78 result[:, mixed] = _ellipticJi(u[mixed], v[mixed], m[mixed]) 75 return result[0, :] if scalar else result79 return result[0, :] if scalar else result 76 80 77 81 def _ellipticJi_real(u, m): … … 104 108 result = np.empty_like(phi, 'D') 105 109 index = (phi == 0) 106 result[index] = ellipticF(atan(sinh(abs(phi[index]))), 1-m[index]) * sign(psi[index]) 110 result[index] = ellipticF(atan(sinh(abs(phi[index]))), 111 1-m[index]) * sign(psi[index]) 107 112 result[~index] = ellipticFi(phi[~index], psi[~index], m[~index]) 108 113 return result.reshape(1)[0] if scalar else result … … 117 122 cotlambda2 = (-b + sqrt(b * b - 4 * c)) / 2 118 123 re = ellipticF(atan(1 / sqrt(cotlambda2)), m) * sign(phi) 119 im = ellipticF(atan(sqrt(np.maximum(0,(cotlambda2 / cotphi2 - 1) / m))), 1 - m) * sign(psi) 124 im = ellipticF(atan(sqrt(np.maximum(0, (cotlambda2 / cotphi2 - 1) / m))), 125 1 - m) * sign(psi) 120 126 return re + 1j*im 121 127 122 sqrt2 = sqrt(2)128 SQRT2 = sqrt(2) 123 129 124 130 # [PAK] renamed k_ => cos_u, k => sin_u, k*k => sinsq_u to avoid k,K confusion … … 127 133 # K = 3.165103454447431823666270142140819753058976299237578486994... 128 134 def guyou(lam, phi): 135 """Transform from (latitude, longitude) to point (x, y)""" 129 136 # [PAK] wrap into [-pi/2, pi/2] radians 130 137 x, y = np.asarray(lam), np.asarray(phi) … … 135 142 136 143 # Compute constant K 137 cos_u = ( sqrt2 - 1) / (sqrt2 + 1)144 cos_u = (SQRT2 - 1) / (SQRT2 + 1) 138 145 sinsq_u = 1 - cos_u**2 139 146 K = ellipticF(pi/2, sinsq_u) … … 144 151 at = atan(r * (cos(lam) - 1j*sin(lam))) 145 152 t = ellipticFi(at.real, at.imag, sinsq_u) 146 x, y = (-t.imag, sign(phi + (phi ==0))*(0.5 * K - t.real))153 x, y = (-t.imag, sign(phi + (phi == 0))*(0.5 * K - t.real)) 147 154 148 155 # [PAK] convert to degrees, and return to original tile … … 150 157 151 158 def guyou_invert(x, y): 159 """Transform from point (x, y) on plot to (latitude, longitude)""" 152 160 # [PAK] wrap into [-pi/2, pi/2] radians 153 161 x, y = np.asarray(x), np.asarray(y) … … 158 166 159 167 # compute constant K 160 cos_u = ( sqrt2 - 1) / (sqrt2 + 1)168 cos_u = (SQRT2 - 1) / (SQRT2 + 1) 161 169 sinsq_u = 1 - cos_u**2 162 170 K = ellipticF(pi/2, sinsq_u) … … 174 182 175 183 def plot_grid(): 184 """Plot the latitude-longitude grid for Guyou transform""" 176 185 import matplotlib.pyplot as plt 177 186 from numpy import linspace … … 186 195 plt.plot(x, y, 'g') 187 196 188 for long in range(-limit, limit+1, step):189 x, y = guyou(scale*long , scale*long_line)197 for longitude in range(-limit, limit+1, step): 198 x, y = guyou(scale*longitude, scale*long_line) 190 199 plt.plot(x, y, 'b') 191 200 #plt.xlabel('longitude') 192 201 plt.ylabel('latitude') 202 plt.title('forward transform') 193 203 194 204 plt.subplot(212) … … 202 212 plt.xlabel('longitude') 203 213 plt.ylabel('latitude') 214 plt.title('inverse transform') 215 216 def main(): 217 """Show the Guyou transformation""" 218 plot_grid() 219 import matplotlib.pyplot as plt 220 plt.show() 204 221 205 222 if __name__ == "__main__": 206 plot_grid() 207 import matplotlib.pyplot as plt; plt.show() 208 223 main() 209 224 210 225 _ = """ -
sasmodels/jitter.py
r199bd07 rb3703f5 8 8 from __future__ import division, print_function 9 9 10 import sys, os11 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))12 13 10 import argparse 14 11 … … 19 16 20 17 import matplotlib.pyplot as plt 21 from matplotlib.widgets import Slider, CheckButtons 22 from matplotlib import cm 18 from matplotlib.widgets import Slider 23 19 import numpy as np 24 20 from numpy import pi, cos, sin, sqrt, exp, degrees, radians 25 21 26 def draw_beam(ax , view=(0, 0)):22 def draw_beam(axes, view=(0, 0)): 27 23 """ 28 24 Draw the beam going from source at (0, 0, 1) to detector at (0, 0, -1) 29 25 """ 30 #ax .plot([0,0],[0,0],[1,-1])31 #ax .scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=0.8)26 #axes.plot([0,0],[0,0],[1,-1]) 27 #axes.scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=0.8) 32 28 33 29 steps = 25 … … 46 42 x, y, z = [v.reshape(shape) for v in points] 47 43 48 ax .plot_surface(x, y, z, rstride=4, cstride=4, color='y', alpha=0.5)49 50 def draw_ellipsoid(ax , size, view, jitter, steps=25, alpha=1):44 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='y', alpha=0.5) 45 46 def draw_ellipsoid(axes, size, view, jitter, steps=25, alpha=1): 51 47 """Draw an ellipsoid.""" 52 a, b,c = size48 a, b, c = size 53 49 u = np.linspace(0, 2 * np.pi, steps) 54 50 v = np.linspace(0, np.pi, steps) … … 58 54 x, y, z = transform_xyz(view, jitter, x, y, z) 59 55 60 ax .plot_surface(x, y, z, rstride=4, cstride=4, color='w', alpha=alpha)61 62 draw_labels(ax , view, jitter, [63 ('c+', [ 0, 0, c], [ 1, 0,0]),64 ('c-', [ 0, 0,-c], [ 0, 0,-1]),65 ('a+', [ a, 0, 0], [ 0, 0,1]),66 ('a-', [-a, 0, 0], [ 0, 0,-1]),67 ('b+', [ 0, b, 0], [-1, 0,0]),68 ('b-', [ 0,-b, 0], [-1, 0,0]),56 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='w', alpha=alpha) 57 58 draw_labels(axes, view, jitter, [ 59 ('c+', [+0, +0, +c], [+1, +0, +0]), 60 ('c-', [+0, +0, -c], [+0, +0, -1]), 61 ('a+', [+a, +0, +0], [+0, +0, +1]), 62 ('a-', [-a, +0, +0], [+0, +0, -1]), 63 ('b+', [+0, +b, +0], [-1, +0, +0]), 64 ('b-', [+0, -b, +0], [-1, +0, +0]), 69 65 ]) 70 66 71 def draw_sc(ax, size, view, jitter, steps=None, alpha=1): 67 def draw_sc(axes, size, view, jitter, steps=None, alpha=1): 68 """Draw points for simple cubic paracrystal""" 72 69 atoms = _build_sc() 73 _draw_crystal(ax, size, view, jitter, atoms=atoms) 74 75 def draw_fcc(ax, size, view, jitter, steps=None, alpha=1): 70 _draw_crystal(axes, size, view, jitter, atoms=atoms) 71 72 def draw_fcc(axes, size, view, jitter, steps=None, alpha=1): 73 """Draw points for face-centered cubic paracrystal""" 76 74 # Build the simple cubic crystal 77 75 atoms = _build_sc() … … 85 83 # y and z planes can be generated by substituting x for y and z respectively 86 84 atoms.extend(zip(x+y+z, y+z+x, z+x+y)) 87 _draw_crystal(ax, size, view, jitter, atoms=atoms) 88 89 def draw_bcc(ax, size, view, jitter, steps=None, alpha=1): 85 _draw_crystal(axes, size, view, jitter, atoms=atoms) 86 87 def draw_bcc(axes, size, view, jitter, steps=None, alpha=1): 88 """Draw points for body-centered cubic paracrystal""" 90 89 # Build the simple cubic crystal 91 90 atoms = _build_sc() … … 98 97 ) 99 98 atoms.extend(zip(x, y, z)) 100 _draw_crystal(ax , size, view, jitter, atoms=atoms)101 102 def _draw_crystal(ax , size, view, jitter, steps=None, alpha=1, atoms=None):99 _draw_crystal(axes, size, view, jitter, atoms=atoms) 100 101 def _draw_crystal(axes, size, view, jitter, atoms=None): 103 102 atoms, size = np.asarray(atoms, 'd').T, np.asarray(size, 'd') 104 103 x, y, z = atoms*size[:, None] 105 104 x, y, z = transform_xyz(view, jitter, x, y, z) 106 ax .scatter([x[0]], [y[0]], [z[0]], c='yellow', marker='o')107 ax .scatter(x[1:], y[1:], z[1:], c='r', marker='o')105 axes.scatter([x[0]], [y[0]], [z[0]], c='yellow', marker='o') 106 axes.scatter(x[1:], y[1:], z[1:], c='r', marker='o') 108 107 109 108 def _build_sc(): … … 124 123 return atoms 125 124 126 def draw_parallelepiped(ax , size, view, jitter, steps=None, alpha=1):125 def draw_parallelepiped(axes, size, view, jitter, steps=None, alpha=1): 127 126 """Draw a parallelepiped.""" 128 127 a, b, c = size 129 x = a*np.array([ 1,-1, 1,-1, 1,-1, 1,-1])130 y = b*np.array([ 1, 1,-1,-1, 1, 1,-1,-1])131 z = c*np.array([ 1, 1, 1, 1,-1,-1,-1,-1])128 x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1]) 129 y = b*np.array([+1, +1, -1, -1, +1, +1, -1, -1]) 130 z = c*np.array([+1, +1, +1, +1, -1, -1, -1, -1]) 132 131 tri = np.array([ 133 132 # counter clockwise triangles 134 133 # z: up/down, x: right/left, y: front/back 135 [0, 1,2], [3,2,1], # top face136 [6, 5,4], [5,6,7], # bottom face137 [0, 2,6], [6,4,0], # right face138 [1, 5,7], [7,3,1], # left face139 [2, 3,6], [7,6,3], # front face140 [4, 1,0], [5,1,4], # back face134 [0, 1, 2], [3, 2, 1], # top face 135 [6, 5, 4], [5, 6, 7], # bottom face 136 [0, 2, 6], [6, 4, 0], # right face 137 [1, 5, 7], [7, 3, 1], # left face 138 [2, 3, 6], [7, 6, 3], # front face 139 [4, 1, 0], [5, 1, 4], # back face 141 140 ]) 142 141 143 142 x, y, z = transform_xyz(view, jitter, x, y, z) 144 ax .plot_trisurf(x, y, triangles=tri, Z=z, color='w', alpha=alpha)143 axes.plot_trisurf(x, y, triangles=tri, Z=z, color='w', alpha=alpha) 145 144 146 145 # Draw pink face on box. … … 149 148 # rotate that face. 150 149 if 1: 151 x = a*np.array([ 1,-1, 1,-1, 1,-1, 1,-1])152 y = b*np.array([ 1, 1,-1,-1, 1, 1,-1,-1])153 z = c*np.array([ 1, 1, 1, 1,-1,-1,-1,-1])150 x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1]) 151 y = b*np.array([+1, +1, -1, -1, +1, +1, -1, -1]) 152 z = c*np.array([+1, +1, +1, +1, -1, -1, -1, -1]) 154 153 x, y, z = transform_xyz(view, jitter, x, y, abs(z)+0.001) 155 ax .plot_trisurf(x, y, triangles=tri, Z=z, color=[1,0.6,0.6], alpha=alpha)156 157 draw_labels(ax , view, jitter, [158 ('c+', [ 0, 0, c], [ 1, 0,0]),159 ('c-', [ 0, 0,-c], [ 0, 0,-1]),160 ('a+', [ a, 0, 0], [ 0, 0,1]),161 ('a-', [-a, 0, 0], [ 0, 0,-1]),162 ('b+', [ 0, b, 0], [-1, 0,0]),163 ('b-', [ 0,-b, 0], [-1, 0,0]),154 axes.plot_trisurf(x, y, triangles=tri, Z=z, color=[1, 0.6, 0.6], alpha=alpha) 155 156 draw_labels(axes, view, jitter, [ 157 ('c+', [+0, +0, +c], [+1, +0, +0]), 158 ('c-', [+0, +0, -c], [+0, +0, -1]), 159 ('a+', [+a, +0, +0], [+0, +0, +1]), 160 ('a-', [-a, +0, +0], [+0, +0, -1]), 161 ('b+', [+0, +b, +0], [-1, +0, +0]), 162 ('b-', [+0, -b, +0], [-1, +0, +0]), 164 163 ]) 165 164 166 def draw_sphere(ax , radius=10., steps=100):165 def draw_sphere(axes, radius=10., steps=100): 167 166 """Draw a sphere""" 168 167 u = np.linspace(0, 2 * np.pi, steps) … … 172 171 y = radius * np.outer(np.sin(u), np.sin(v)) 173 172 z = radius * np.outer(np.ones(np.size(u)), np.cos(v)) 174 ax .plot_surface(x, y, z, rstride=4, cstride=4, color='w')175 176 def draw_jitter(ax , view, jitter, dist='gaussian', size=(0.1, 0.4, 1.0),173 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='w') 174 175 def draw_jitter(axes, view, jitter, dist='gaussian', size=(0.1, 0.4, 1.0), 177 176 draw_shape=draw_parallelepiped): 178 177 """ … … 187 186 cloud = [ 188 187 [-1, -1, -1], 189 [-1, -1, 190 [-1, -1, 191 [-1, 192 [-1, 0,0],193 [-1, 0,1],194 [-1, 195 [-1, 1,0],196 [-1, 1,1],197 [ 198 [ 0, -1,0],199 [ 0, -1,1],200 [ 0,0, -1],201 [ 0, 0,0],202 [ 0, 0,1],203 [ 0,1, -1],204 [ 0, 1,0],205 [ 0, 1,1],206 [ 207 [ 1, -1,0],208 [ 1, -1,1],209 [ 1,0, -1],210 [ 1, 0,0],211 [ 1, 0,1],212 [ 1,1, -1],213 [ 1, 1,0],214 [ 1, 1,1],188 [-1, -1, +0], 189 [-1, -1, +1], 190 [-1, +0, -1], 191 [-1, +0, +0], 192 [-1, +0, +1], 193 [-1, +1, -1], 194 [-1, +1, +0], 195 [-1, +1, +1], 196 [+0, -1, -1], 197 [+0, -1, +0], 198 [+0, -1, +1], 199 [+0, +0, -1], 200 [+0, +0, +0], 201 [+0, +0, +1], 202 [+0, +1, -1], 203 [+0, +1, +0], 204 [+0, +1, +1], 205 [+1, -1, -1], 206 [+1, -1, +0], 207 [+1, -1, +1], 208 [+1, +0, -1], 209 [+1, +0, +0], 210 [+1, +0, +1], 211 [+1, +1, -1], 212 [+1, +1, +0], 213 [+1, +1, +1], 215 214 ] 216 215 dtheta, dphi, dpsi = jitter … … 221 220 if dpsi == 0: 222 221 cloud = [v for v in cloud if v[2] == 0] 223 draw_shape(ax , size, view, [0, 0, 0], steps=100, alpha=0.8)222 draw_shape(axes, size, view, [0, 0, 0], steps=100, alpha=0.8) 224 223 scale = {'gaussian':1, 'rectangle':1/sqrt(3), 'uniform':1/3}[dist] 225 224 for point in cloud: 226 225 delta = [scale*dtheta*point[0], scale*dphi*point[1], scale*dpsi*point[2]] 227 draw_shape(ax , size, view, delta, alpha=0.8)226 draw_shape(axes, size, view, delta, alpha=0.8) 228 227 for v in 'xyz': 229 228 a, b, c = size 230 lim = np.sqrt(a**2 +b**2+c**2)231 getattr(ax , 'set_'+v+'lim')([-lim, lim])232 getattr(ax , v+'axis').label.set_text(v)229 lim = np.sqrt(a**2 + b**2 + c**2) 230 getattr(axes, 'set_'+v+'lim')([-lim, lim]) 231 getattr(axes, v+'axis').label.set_text(v) 233 232 234 233 PROJECTIONS = [ … … 238 237 'azimuthal_equal_area', 239 238 ] 240 def draw_mesh(ax , view, jitter, radius=1.2, n=11, dist='gaussian',239 def draw_mesh(axes, view, jitter, radius=1.2, n=11, dist='gaussian', 241 240 projection='equirectangular'): 242 241 """ … … 297 296 Should allow free movement in theta, but phi is distorted. 298 297 """ 299 theta, phi, psi = view 300 dtheta, dphi, dpsi = jitter 301 302 t = np.linspace(-1, 1, n) 303 weights = np.ones_like(t) 298 # TODO: try Kent distribution instead of a gaussian warped by projection 299 300 dist_x = np.linspace(-1, 1, n) 301 weights = np.ones_like(dist_x) 304 302 if dist == 'gaussian': 305 t*= 3306 weights = exp(-0.5* t**2)303 dist_x *= 3 304 weights = exp(-0.5*dist_x**2) 307 305 elif dist == 'rectangle': 308 306 # Note: uses sasmodels ridiculous definition of rectangle width 309 t*= sqrt(3)307 dist_x *= sqrt(3) 310 308 elif dist == 'uniform': 311 309 pass … … 314 312 315 313 if projection == 'equirectangular': #define PROJECTION 1 316 def rotate(theta_i, phi_j):314 def _rotate(theta_i, phi_j): 317 315 return Rx(phi_j)*Ry(theta_i) 318 def weight(theta_i, phi_j, wi, wj):319 return w i*wj*abs(cos(radians(theta_i)))316 def _weight(theta_i, phi_j, w_i, w_j): 317 return w_i*w_j*abs(cos(radians(theta_i))) 320 318 elif projection == 'sinusoidal': #define PROJECTION 2 321 def rotate(theta_i, phi_j):319 def _rotate(theta_i, phi_j): 322 320 latitude = theta_i 323 321 scale = cos(radians(latitude)) … … 325 323 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 326 324 return Rx(longitude)*Ry(latitude) 327 def weight(theta_i, phi_j, wi, wj):325 def _weight(theta_i, phi_j, w_i, w_j): 328 326 latitude = theta_i 329 327 scale = cos(radians(latitude)) 330 w= 1 if abs(phi_j) < abs(scale)*180 else 0331 return w*wi*wj328 active = 1 if abs(phi_j) < abs(scale)*180 else 0 329 return active*w_i*w_j 332 330 elif projection == 'guyou': #define PROJECTION 3 (eventually?) 333 def rotate(theta_i, phi_j):334 from guyou import guyou_invert331 def _rotate(theta_i, phi_j): 332 from .guyou import guyou_invert 335 333 #latitude, longitude = guyou_invert([theta_i], [phi_j]) 336 334 longitude, latitude = guyou_invert([phi_j], [theta_i]) 337 335 return Rx(longitude[0])*Ry(latitude[0]) 338 def weight(theta_i, phi_j, wi, wj):339 return w i*wj336 def _weight(theta_i, phi_j, w_i, w_j): 337 return w_i*w_j 340 338 elif projection == 'azimuthal_equidistance': # Note: Rz Ry, not Rx Ry 341 def rotate(theta_i, phi_j):339 def _rotate(theta_i, phi_j): 342 340 latitude = sqrt(theta_i**2 + phi_j**2) 343 341 longitude = degrees(np.arctan2(phi_j, theta_i)) 344 342 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 345 343 return Rz(longitude)*Ry(latitude) 346 def weight(theta_i, phi_j, wi, wj):344 def _weight(theta_i, phi_j, w_i, w_j): 347 345 # Weighting for each point comes from the integral: 348 346 # \int\int I(q, lat, log) sin(lat) dlat dlog … … 374 372 # the entire sphere, and treats theta and phi identically. 375 373 latitude = sqrt(theta_i**2 + phi_j**2) 376 w = sin(radians(latitude))/latitude if latitude != 0 else 1377 return w *wi*wj if latitude < 180 else 0374 weight = sin(radians(latitude))/latitude if latitude != 0 else 1 375 return weight*w_i*w_j if latitude < 180 else 0 378 376 elif projection == 'azimuthal_equal_area': 379 def rotate(theta_i, phi_j):380 R= min(1, sqrt(theta_i**2 + phi_j**2)/180)381 latitude = 180-degrees(2*np.arccos( R))377 def _rotate(theta_i, phi_j): 378 radius = min(1, sqrt(theta_i**2 + phi_j**2)/180) 379 latitude = 180-degrees(2*np.arccos(radius)) 382 380 longitude = degrees(np.arctan2(phi_j, theta_i)) 383 381 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 384 382 return Rz(longitude)*Ry(latitude) 385 def weight(theta_i, phi_j, wi, wj):383 def _weight(theta_i, phi_j, w_i, w_j): 386 384 latitude = sqrt(theta_i**2 + phi_j**2) 387 w = sin(radians(latitude))/latitude if latitude != 0 else 1388 return w *wi*wj if latitude < 180 else 0385 weight = sin(radians(latitude))/latitude if latitude != 0 else 1 386 return weight*w_i*w_j if latitude < 180 else 0 389 387 else: 390 388 raise ValueError("unknown projection %r"%projection) 391 389 392 390 # mesh in theta, phi formed by rotating z 391 dtheta, dphi, dpsi = jitter 393 392 z = np.matrix([[0], [0], [radius]]) 394 points = np.hstack([rotate(theta_i, phi_j)*z 395 for theta_i in dtheta*t 396 for phi_j in dphi*t]) 397 # select just the active points (i.e., those with phi < 180 398 w = np.array([weight(theta_i, phi_j, wi, wj) 399 for wi, theta_i in zip(weights, dtheta*t) 400 for wj, phi_j in zip(weights, dphi*t)]) 401 #print(max(w), min(w), min(w[w>0])) 402 points = points[:, w>0] 403 w = w[w>0] 404 w /= max(w) 405 406 if 0: # Kent distribution 407 points = np.hstack([Rx(phi_j)*Ry(theta_i)*z for theta_i in 30*t for phi_j in 60*t]) 408 xp, yp, zp = [np.array(v).flatten() for v in points] 409 kappa = max(1e6, radians(dtheta)/(2*pi)) 410 beta = 1/max(1e-6, radians(dphi)/(2*pi))/kappa 411 w = exp(kappa*zp) #+ beta*(xp**2 + yp**2) 412 print(kappa, dtheta, radians(dtheta), min(w), max(w), sum(w)) 413 #w /= abs(cos(radians( 414 #w /= sum(w) 393 points = np.hstack([_rotate(theta_i, phi_j)*z 394 for theta_i in dtheta*dist_x 395 for phi_j in dphi*dist_x]) 396 dist_w = np.array([_weight(theta_i, phi_j, w_i, w_j) 397 for w_i, theta_i in zip(weights, dtheta*dist_x) 398 for w_j, phi_j in zip(weights, dphi*dist_x)]) 399 #print(max(dist_w), min(dist_w), min(dist_w[dist_w > 0])) 400 points = points[:, dist_w > 0] 401 dist_w = dist_w[dist_w > 0] 402 dist_w /= max(dist_w) 415 403 416 404 # rotate relative to beam … … 419 407 x, y, z = [np.array(v).flatten() for v in points] 420 408 #plt.figure(2); plt.clf(); plt.hist(z, bins=np.linspace(-1, 1, 51)) 421 ax .scatter(x, y, z, c=w, marker='o', vmin=0., vmax=1.)422 423 def draw_labels(ax , view, jitter, text):409 axes.scatter(x, y, z, c=dist_w, marker='o', vmin=0., vmax=1.) 410 411 def draw_labels(axes, view, jitter, text): 424 412 """ 425 413 Draw text at a particular location. … … 435 423 for label, p, zdir in zip(labels, zip(px, py, pz), zip(dx, dy, dz)): 436 424 zdir = np.asarray(zdir).flatten() 437 ax .text(p[0], p[1], p[2], label, zdir=zdir)425 axes.text(p[0], p[1], p[2], label, zdir=zdir) 438 426 439 427 # Definition of rotation matrices comes from wikipedia: … … 441 429 def Rx(angle): 442 430 """Construct a matrix to rotate points about *x* by *angle* degrees.""" 443 a = radians(angle)444 R= [[1, 0, 0],445 [0, +cos(a), -sin(a)],446 [0, +sin(a), +cos(a)]]447 return np.matrix( R)431 angle = radians(angle) 432 rot = [[1, 0, 0], 433 [0, +cos(angle), -sin(angle)], 434 [0, +sin(angle), +cos(angle)]] 435 return np.matrix(rot) 448 436 449 437 def Ry(angle): 450 438 """Construct a matrix to rotate points about *y* by *angle* degrees.""" 451 a = radians(angle)452 R = [[+cos(a), 0, +sin(a)],453 [0, 1, 0],454 [-sin(a), 0, +cos(a)]]455 return np.matrix( R)439 angle = radians(angle) 440 rot = [[+cos(angle), 0, +sin(angle)], 441 [0, 1, 0], 442 [-sin(angle), 0, +cos(angle)]] 443 return np.matrix(rot) 456 444 457 445 def Rz(angle): 458 446 """Construct a matrix to rotate points about *z* by *angle* degrees.""" 459 a = radians(angle)460 R = [[+cos(a), -sin(a), 0],461 [+sin(a), +cos(a), 0],462 [0, 0, 1]]463 return np.matrix( R)447 angle = radians(angle) 448 rot = [[+cos(angle), -sin(angle), 0], 449 [+sin(angle), +cos(angle), 0], 450 [0, 0, 1]] 451 return np.matrix(rot) 464 452 465 453 def transform_xyz(view, jitter, x, y, z): … … 469 457 x, y, z = [np.asarray(v) for v in (x, y, z)] 470 458 shape = x.shape 471 points = np.matrix([x.flatten(), y.flatten(),z.flatten()])459 points = np.matrix([x.flatten(), y.flatten(), z.flatten()]) 472 460 points = apply_jitter(jitter, points) 473 461 points = orient_relative_to_beam(view, points) … … 526 514 return data[offset], data[-1] 527 515 528 def draw_scattering(calculator, ax , view, jitter, dist='gaussian'):516 def draw_scattering(calculator, axes, view, jitter, dist='gaussian'): 529 517 """ 530 518 Plot the scattering for the particular view. 531 519 532 *calculator* is returned from :func:`build_model`. *ax * are the 3D axes520 *calculator* is returned from :func:`build_model`. *axes* are the 3D axes 533 521 on which the data will be plotted. *view* and *jitter* are the current 534 522 orientation and orientation dispersity. *dist* is one of the sasmodels … … 543 531 theta, phi, psi = view 544 532 theta_pd, phi_pd, psi_pd = [scale*v for v in jitter] 545 theta_pd_n, phi_pd_n, psi_pd_n = PD_N_TABLE[(theta_pd >0, phi_pd>0, psi_pd>0)]533 theta_pd_n, phi_pd_n, psi_pd_n = PD_N_TABLE[(theta_pd > 0, phi_pd > 0, psi_pd > 0)] 546 534 ## increase pd_n for testing jitter integration rather than simple viz 547 535 #theta_pd_n, phi_pd_n, psi_pd_n = [5*v for v in (theta_pd_n, phi_pd_n, psi_pd_n)] … … 571 559 if 0: 572 560 level = np.asarray(255*(Iqxy - vmin)/(vmax - vmin), 'i') 573 level[level <0] = 0561 level[level < 0] = 0 574 562 colors = plt.get_cmap()(level) 575 ax .plot_surface(qx, qy, -1.1, rstride=1, cstride=1, facecolors=colors)563 axes.plot_surface(qx, qy, -1.1, rstride=1, cstride=1, facecolors=colors) 576 564 elif 1: 577 ax .contourf(qx/qx.max(), qy/qy.max(), Iqxy, zdir='z', offset=-1.1,578 levels=np.linspace(vmin, vmax, 24))565 axes.contourf(qx/qx.max(), qy/qy.max(), Iqxy, zdir='z', offset=-1.1, 566 levels=np.linspace(vmin, vmax, 24)) 579 567 else: 580 ax .pcolormesh(qx, qy, Iqxy)568 axes.pcolormesh(qx, qy, Iqxy) 581 569 582 570 def build_model(model_name, n=150, qmax=0.5, **pars): … … 595 583 for details. 596 584 """ 597 from sasmodels.core import load_model_info, build_model 585 from sasmodels.core import load_model_info, build_model as build_sasmodel 598 586 from sasmodels.data import empty_data2D 599 587 from sasmodels.direct_model import DirectModel 600 588 601 589 model_info = load_model_info(model_name) 602 model = build_ model(model_info) #, dtype='double!')590 model = build_sasmodel(model_info) #, dtype='double!') 603 591 q = np.linspace(-qmax, qmax, n) 604 592 data = empty_data2D(q, q) … … 618 606 return calculator 619 607 620 def select_calculator(model_name, n=150, size=(10, 40,100)):608 def select_calculator(model_name, n=150, size=(10, 40, 100)): 621 609 """ 622 610 Create a model calculator for the given shape. … … 641 629 radius = 0.5*c 642 630 calculator = build_model('sc_paracrystal', n=n, dnn=dnn, 643 644 631 d_factor=d_factor, radius=(1-d_factor)*radius, 632 background=0) 645 633 elif model_name == 'fcc_paracrystal': 646 634 a = b = c … … 650 638 radius = sqrt(2)/4 * c 651 639 calculator = build_model('fcc_paracrystal', n=n, dnn=dnn, 652 653 640 d_factor=d_factor, radius=(1-d_factor)*radius, 641 background=0) 654 642 elif model_name == 'bcc_paracrystal': 655 643 a = b = c … … 659 647 radius = sqrt(3)/2 * c 660 648 calculator = build_model('bcc_paracrystal', n=n, dnn=dnn, 661 662 649 d_factor=d_factor, radius=(1-d_factor)*radius, 650 background=0) 663 651 elif model_name == 'cylinder': 664 652 calculator = build_model('cylinder', n=n, qmax=0.3, radius=b, length=c) … … 685 673 'cylinder', 686 674 'fcc_paracrystal', 'bcc_paracrystal', 'sc_paracrystal', 687 675 ] 688 676 689 677 DRAW_SHAPES = { … … 751 739 plt.gcf().canvas.set_window_title(projection) 752 740 #gs = gridspec.GridSpec(2,1,height_ratios=[4,1]) 753 #ax = plt.subplot(gs[0], projection='3d')754 ax = plt.axes([0.0, 0.2, 1.0, 0.8], projection='3d')741 #axes = plt.subplot(gs[0], projection='3d') 742 axes = plt.axes([0.0, 0.2, 1.0, 0.8], projection='3d') 755 743 try: # CRUFT: not all versions of matplotlib accept 'square' 3d projection 756 ax .axis('square')744 axes.axis('square') 757 745 except Exception: 758 746 pass … … 761 749 762 750 ## add control widgets to plot 763 ax theta= plt.axes([0.1, 0.15, 0.45, 0.04], axisbg=axcolor)764 ax phi = plt.axes([0.1, 0.1, 0.45, 0.04], axisbg=axcolor)765 ax psi = plt.axes([0.1, 0.05, 0.45, 0.04], axisbg=axcolor)766 stheta = Slider(ax theta, 'Theta', -90, 90, valinit=theta)767 sphi = Slider(ax phi, 'Phi', -180, 180, valinit=phi)768 spsi = Slider(ax psi, 'Psi', -180, 180, valinit=psi)769 770 ax dtheta= plt.axes([0.75, 0.15, 0.15, 0.04], axisbg=axcolor)771 ax dphi = plt.axes([0.75, 0.1, 0.15, 0.04], axisbg=axcolor)772 ax dpsi= plt.axes([0.75, 0.05, 0.15, 0.04], axisbg=axcolor)751 axes_theta = plt.axes([0.1, 0.15, 0.45, 0.04], axisbg=axcolor) 752 axes_phi = plt.axes([0.1, 0.1, 0.45, 0.04], axisbg=axcolor) 753 axes_psi = plt.axes([0.1, 0.05, 0.45, 0.04], axisbg=axcolor) 754 stheta = Slider(axes_theta, 'Theta', -90, 90, valinit=theta) 755 sphi = Slider(axes_phi, 'Phi', -180, 180, valinit=phi) 756 spsi = Slider(axes_psi, 'Psi', -180, 180, valinit=psi) 757 758 axes_dtheta = plt.axes([0.75, 0.15, 0.15, 0.04], axisbg=axcolor) 759 axes_dphi = plt.axes([0.75, 0.1, 0.15, 0.04], axisbg=axcolor) 760 axes_dpsi = plt.axes([0.75, 0.05, 0.15, 0.04], axisbg=axcolor) 773 761 # Note: using ridiculous definition of rectangle distribution, whose width 774 762 # in sasmodels is sqrt(3) times the given width. Divide by sqrt(3) to keep 775 763 # the maximum width to 90. 776 764 dlimit = DIST_LIMITS[dist] 777 sdtheta = Slider(ax dtheta, 'dTheta', 0, 2*dlimit, valinit=dtheta)778 sdphi = Slider(ax dphi, 'dPhi', 0, 2*dlimit, valinit=dphi)779 sdpsi = Slider(ax dpsi, 'dPsi', 0, 2*dlimit, valinit=dpsi)765 sdtheta = Slider(axes_dtheta, 'dTheta', 0, 2*dlimit, valinit=dtheta) 766 sdphi = Slider(axes_dphi, 'dPhi', 0, 2*dlimit, valinit=dphi) 767 sdpsi = Slider(axes_dpsi, 'dPsi', 0, 2*dlimit, valinit=dpsi) 780 768 781 769 … … 788 776 limit = [0, 0, 0.5, 5][dims] 789 777 jitter = [0 if v < limit else v for v in jitter] 790 ax .cla()791 draw_beam(ax , (0, 0))792 draw_jitter(ax , view, jitter, dist=dist, size=size, draw_shape=draw_shape)793 #draw_jitter(ax , view, (0,0,0))794 draw_mesh(ax , view, jitter, dist=dist, n=mesh, projection=projection)795 draw_scattering(calculator, ax , view, jitter, dist=dist)778 axes.cla() 779 draw_beam(axes, (0, 0)) 780 draw_jitter(axes, view, jitter, dist=dist, size=size, draw_shape=draw_shape) 781 #draw_jitter(axes, view, (0,0,0)) 782 draw_mesh(axes, view, jitter, dist=dist, n=mesh, projection=projection) 783 draw_scattering(calculator, axes, view, jitter, dist=dist) 796 784 plt.gcf().canvas.draw() 797 785 798 786 ## bind control widgets to view updater 799 stheta.on_changed(lambda v: update(v, 'theta'))787 stheta.on_changed(lambda v: update(v, 'theta')) 800 788 sphi.on_changed(lambda v: update(v, 'phi')) 801 789 spsi.on_changed(lambda v: update(v, 'psi')) … … 815 803 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 816 804 ) 817 parser.add_argument('-p', '--projection', choices=PROJECTIONS, default=PROJECTIONS[0], help='coordinate projection') 818 parser.add_argument('-s', '--size', type=str, default='10,40,100', help='a,b,c lengths') 819 parser.add_argument('-d', '--distribution', choices=DISTRIBUTIONS, default=DISTRIBUTIONS[0], help='jitter distribution') 820 parser.add_argument('-m', '--mesh', type=int, default=30, help='#points in theta-phi mesh') 821 parser.add_argument('shape', choices=SHAPES, nargs='?', default=SHAPES[0], help='oriented shape') 805 parser.add_argument('-p', '--projection', choices=PROJECTIONS, 806 default=PROJECTIONS[0], 807 help='coordinate projection') 808 parser.add_argument('-s', '--size', type=str, default='10,40,100', 809 help='a,b,c lengths') 810 parser.add_argument('-d', '--distribution', choices=DISTRIBUTIONS, 811 default=DISTRIBUTIONS[0], 812 help='jitter distribution') 813 parser.add_argument('-m', '--mesh', type=int, default=30, 814 help='#points in theta-phi mesh') 815 parser.add_argument('shape', choices=SHAPES, nargs='?', default=SHAPES[0], 816 help='oriented shape') 822 817 opts = parser.parse_args() 823 818 size = tuple(int(v) for v in opts.size.split(',')) -
sasmodels/kernel_iq.c
raadec17 rd86f0fc 173 173 // Apply the rotation matrix returned from qac_rotation to the point (qx,qy), 174 174 // returning R*[qx,qy]' = [qa,qc]' 175 static double175 static void 176 176 qac_apply( 177 177 QACRotation *rotation, … … 246 246 // Apply the rotation matrix returned from qabc_rotation to the point (qx,qy), 247 247 // returning R*[qx,qy]' = [qa,qb,qc]' 248 static double248 static void 249 249 qabc_apply( 250 250 QABCRotation *rotation, -
sasmodels/kernelcl.py
r6cbdcd4 rd86f0fc 151 151 if not HAVE_OPENCL: 152 152 raise RuntimeError("OpenCL startup failed with ***" 153 + OPENCL_ERROR + "***; using C compiler instead")153 + OPENCL_ERROR + "***; using C compiler instead") 154 154 reset_environment() 155 155 if ENV is None: -
sasmodels/models/core_shell_cylinder.c
r108e70e rd86f0fc 48 48 49 49 50 double Iqac(double qab, double qc, 50 static double 51 Iqac(double qab, double qc, 51 52 double core_sld, 52 53 double shell_sld, -
sasmodels/models/hollow_rectangular_prism.c
r108e70e rd86f0fc 1 double form_volume(double length_a, double b2a_ratio, double c2a_ratio, double thickness); 2 double Iq(double q, double sld, double solvent_sld, double length_a, 3 double b2a_ratio, double c2a_ratio, double thickness); 4 5 double form_volume(double length_a, double b2a_ratio, double c2a_ratio, double thickness) 1 static double 2 form_volume(double length_a, double b2a_ratio, double c2a_ratio, double thickness) 6 3 { 7 4 double length_b = length_a * b2a_ratio; … … 16 13 } 17 14 18 double Iq(double q, 15 static double 16 Iq(double q, 19 17 double sld, 20 18 double solvent_sld, … … 85 83 } 86 84 87 double Iqabc(double qa, double qb, double qc, 85 static double 86 Iqabc(double qa, double qb, double qc, 88 87 double sld, 89 88 double solvent_sld, -
sasmodels/models/hollow_rectangular_prism_thin_walls.c
r74768cb rd86f0fc 1 double form_volume(double length_a, double b2a_ratio, double c2a_ratio); 2 double Iq(double q, double sld, double solvent_sld, double length_a, 3 double b2a_ratio, double c2a_ratio); 4 5 double form_volume(double length_a, double b2a_ratio, double c2a_ratio) 1 static double 2 form_volume(double length_a, double b2a_ratio, double c2a_ratio) 6 3 { 7 4 double length_b = length_a * b2a_ratio; … … 11 8 } 12 9 13 double Iq(double q, 10 static double 11 Iq(double q, 14 12 double sld, 15 13 double solvent_sld, -
sasmodels/models/rectangular_prism.c
r108e70e rd86f0fc 1 double form_volume(double length_a, double b2a_ratio, double c2a_ratio); 2 double Iq(double q, double sld, double solvent_sld, double length_a, 3 double b2a_ratio, double c2a_ratio); 4 5 double form_volume(double length_a, double b2a_ratio, double c2a_ratio) 1 static double 2 form_volume(double length_a, double b2a_ratio, double c2a_ratio) 6 3 { 7 4 return length_a * (length_a*b2a_ratio) * (length_a*c2a_ratio); 8 5 } 9 6 10 double Iq(double q, 7 static double 8 Iq(double q, 11 9 double sld, 12 10 double solvent_sld, … … 71 69 72 70 73 double Iqabc(double qa, double qb, double qc, 71 static double 72 Iqabc(double qa, double qb, double qc, 74 73 double sld, 75 74 double solvent_sld, -
sasmodels/multiscat.py
r49d1f8b8 rb3703f5 73 73 import argparse 74 74 import time 75 import os.path76 75 77 76 import numpy as np … … 81 80 from sasmodels import core 82 81 from sasmodels import compare 83 from sasmodels import resolution2d84 82 from sasmodels.resolution import Resolution, bin_edges 85 from sasmodels.data import empty_data1D, empty_data2D, plot_data86 83 from sasmodels.direct_model import call_kernel 87 84 import sasmodels.kernelcl … … 106 103 USE_FAST = True # OpenCL faster, less accurate math 107 104 108 class NumpyCalculator: 105 class ICalculator: 106 """ 107 Multiple scattering calculator 108 """ 109 def fft(self, Iq): 110 """ 111 Compute the forward FFT for an image, real -> complex. 112 """ 113 raise NotImplementedError() 114 115 def ifft(self, Iq): 116 """ 117 Compute the inverse FFT for an image, complex -> complex. 118 """ 119 raise NotImplementedError() 120 121 def mulitple_scattering(self, Iq): 122 r""" 123 Compute multiple scattering for I(q) given scattering probability p. 124 125 Given a probability p of scattering with the thickness, the expected 126 number of scattering events, $\lambda$ is $-\log(1 - p)$, giving a 127 Poisson weighted sum of single, double, triple, etc. scattering patterns. 128 The number of patterns used is based on coverage (default 99%). 129 """ 130 raise NotImplementedError() 131 132 class NumpyCalculator(ICalculator): 133 """ 134 Multiple scattering calculator using numpy fft. 135 """ 109 136 def __init__(self, dims=None, dtype=PRECISION): 110 137 self.dtype = dtype 111 138 self.complex_dtype = np.dtype('F') if dtype == np.dtype('f') else np.dtype('D') 112 pass113 139 114 140 def fft(self, Iq): … … 127 153 128 154 def multiple_scattering(self, Iq, p, coverage=0.99): 129 r"""130 Compute multiple scattering for I(q) given scattering probability p.131 132 Given a probability p of scattering with the thickness, the expected133 number of scattering events, $\lambda$ is $-\log(1 - p)$, giving a134 Poisson weighted sum of single, double, triple, etc. scattering patterns.135 The number of patterns used is based on coverage (default 99%).136 """137 155 #t0 = time.time() 138 156 coeffs = scattering_coeffs(p, coverage) … … 140 158 scale = np.sum(Iq) 141 159 frame = _forward_shift(Iq/scale, dtype=self.dtype) 142 F= np.fft.fft2(frame)143 F_convolved = F * np.polyval(poly, F)144 frame = np.fft.ifft2( F_convolved)160 fourier_frame = np.fft.fft2(frame) 161 convolved = fourier_frame * np.polyval(poly, fourier_frame) 162 frame = np.fft.ifft2(convolved) 145 163 result = scale * _inverse_shift(frame.real, dtype=self.dtype) 146 164 #print("numpy multiscat time", time.time()-t0) … … 173 191 """ 174 192 175 class OpenclCalculator(NumpyCalculator): 193 class OpenclCalculator(ICalculator): 194 """ 195 Multiple scattering calculator using OpenCL via pyfft. 196 """ 176 197 polyval1f = None 177 198 polyval1d = None … … 180 201 context = env.get_context(dtype) 181 202 if dtype == np.dtype('f'): 182 if self.polyval1f is None:203 if OpenclCalculator.polyval1f is None: 183 204 program = sasmodels.kernelcl.compile_model( 184 205 context, POLYVAL1_KERNEL, dtype, fast=USE_FAST) … … 187 208 self.dtype = dtype 188 209 self.complex_dtype = np.dtype('F') 189 self.polyval1 = self.polyval1f210 self.polyval1 = OpenclCalculator.polyval1f 190 211 else: 191 if self.polyval1d is None:212 if OpenclCalculator.polyval1d is None: 192 213 program = sasmodels.kernelcl.compile_model( 193 214 context, POLYVAL1_KERNEL, dtype, fast=False) … … 196 217 self.dtype = dtype 197 218 self.complex_type = np.dtype('D') 198 self.polyval1 = self.polyval1d219 self.polyval1 = OpenclCalculator.polyval1d 199 220 self.queue = env.get_queue(dtype) 200 221 self.plan = pyfft.cl.Plan(dims, queue=self.queue) … … 229 250 gpu_poly = cl_array.to_device(self.queue, poly) 230 251 self.plan.execute(gpu_data.data) 231 degree, n= poly.shape[0], frame.shape[0]*frame.shape[1]252 degree, data_size= poly.shape[0], frame.shape[0]*frame.shape[1] 232 253 self.polyval1( 233 self.queue, [ n], None,234 np.int32(degree), gpu_poly.data, np.int32( n), gpu_data.data)254 self.queue, [data_size], None, 255 np.int32(degree), gpu_poly.data, np.int32(data_size), gpu_data.data) 235 256 self.plan.execute(gpu_data.data, inverse=True) 236 257 frame = gpu_data.get() … … 251 272 """ 252 273 if transform is None: 253 n x, ny = Iq.shape254 transform = Calculator(dims=(n x*2, ny*2), dtype=dtype)274 n_x, n_y = Iq.shape 275 transform = Calculator(dims=(n_x*2, n_y*2), dtype=dtype) 255 276 scale = np.sum(Iq) 256 277 frame = _forward_shift(Iq/scale, dtype=dtype) … … 261 282 262 283 def scattering_coeffs(p, coverage=0.99): 284 r""" 285 Return the coefficients of the scattering powers for transmission 286 probability *p*. This is just the corresponding values for the 287 Poisson distribution for $\lambda = -\ln(1-p)$ such that 288 $\sum_{k = 0 \ldots n} P(k; \lambda)$ is larger than *coverage*. 289 """ 263 290 L = -np.log(1-p) 264 291 num_scatter = truncated_poisson_invcdf(coverage, L) … … 266 293 return coeffs 267 294 268 def truncated_poisson_invcdf( p, L):295 def truncated_poisson_invcdf(coverage, L): 269 296 r""" 270 Return smallest k such that cdf(k; L) > pfrom the truncated Poisson297 Return smallest k such that cdf(k; L) > coverage from the truncated Poisson 271 298 probability excluding k=0 272 299 """ … … 275 302 pmf = -np.exp(-L) / np.expm1(-L) 276 303 k = 0 277 while cdf < p:304 while cdf < coverage: 278 305 k += 1 279 306 pmf *= L/k … … 305 332 306 333 class MultipleScattering(Resolution): 334 r""" 335 Compute multiple scattering using Fourier convolution. 336 337 The fourier steps are determined by *qmax*, the maximum $q$ value 338 desired, *nq* the number of $q$ steps and *window*, the amount 339 of padding around the circular convolution. The $q$ spacing 340 will be $\Delta q = 2 q_\mathrm{max} w / n_q$. If *nq* is not 341 given it will use $n_q = 2^k$ such that $\Delta q < q_\mathrm{min}$. 342 343 *probability* is related to the expected number of scattering 344 events in the sample $\lambda$ as $p = 1 = e^{-\lambda}$. As a 345 hack to allow probability to be a fitted parameter, the "value" 346 can be a function that takes no parameters and returns the current 347 value of the probability. *coverage* determines how many scattering 348 steps to consider. The default is 0.99, which sets $n$ such that 349 $1 \ldots n$ covers 99% of the Poisson probability mass function. 350 351 *is2d* is True then 2D scattering is used, otherwise it accepts 352 and returns 1D scattering. 353 354 *resolution* is the resolution function to apply after multiple 355 scattering. If present, then the resolution $q$ vectors will provide 356 default values for *qmin*, *qmax* and *nq*. 357 """ 307 358 def __init__(self, qmin=None, qmax=None, nq=None, window=2, 308 359 probability=None, coverage=0.99, 309 360 is2d=False, resolution=None, 310 361 dtype=PRECISION): 311 r"""312 Compute multiple scattering using Fourier convolution.313 314 The fourier steps are determined by *qmax*, the maximum $q$ value315 desired, *nq* the number of $q$ steps and *window*, the amount316 of padding around the circular convolution. The $q$ spacing317 will be $\Delta q = 2 q_\mathrm{max} w / n_q$. If *nq* is not318 given it will use $n_q = 2^k$ such that $\Delta q < q_\mathrm{min}$.319 320 *probability* is related to the expected number of scattering321 events in the sample $\lambda$ as $p = 1 = e^{-\lambda}$. As a322 hack to allow probability to be a fitted parameter, the "value"323 can be a function that takes no parameters and returns the current324 value of the probability. *coverage* determines how many scattering325 steps to consider. The default is 0.99, which sets $n$ such that326 $1 \ldots n$ covers 99% of the Poisson probability mass function.327 328 *is2d* is True then 2D scattering is used, otherwise it accepts329 and returns 1D scattering.330 331 *resolution* is the resolution function to apply after multiple332 scattering. If present, then the resolution $q$ vectors will provide333 default values for *qmin*, *qmax* and *nq*.334 """335 362 # Infer qmin, qmax from instrument resolution calculator, if present 336 363 if resolution is not None: … … 424 451 # Prepare the multiple scattering calculator (either numpy or OpenCL) 425 452 self.transform = Calculator((2*nq, 2*nq), dtype=dtype) 453 454 # Iq and Iqxy will be set during apply 455 self.Iq = None # type: np.ndarray 456 self.Iqxy = None # type: np.ndarray 426 457 427 458 def apply(self, theory): … … 471 502 472 503 def radial_profile(self, Iqxy): 504 """ 505 Compute that radial profile for the given Iqxy grid. The grid should 506 be defined as for 507 """ 473 508 # circular average, no anti-aliasing 474 509 Iq = np.histogram(self._radius, bins=self._edges, weights=Iqxy)[0]/self._norm … … 478 513 def annular_average(qxy, Iqxy, qbins): 479 514 """ 480 Compute annular average of points at 515 Compute annular average of points in *Iqxy* at *qbins*. The $q_x$, $q_y$ 516 coordinates for *Iqxy* are given in *qxy*. 481 517 """ 482 518 qxy, Iqxy = qxy.flatten(), Iqxy.flatten() … … 513 549 def parse_pars(model, opts): 514 550 # type: (ModelInfo, argparse.Namespace) -> Dict[str, float] 551 """ 552 Parse par=val arguments from the command line. 553 """ 515 554 516 555 seed = np.random.randint(1000000) if opts.random and opts.seed < 0 else opts.seed … … 526 565 'is2d': opts.is2d, 527 566 } 528 pars, pars2 = compare.parse_pars(compare_opts) 567 # Note: sascomp allows comparison on a pair of models, so ignore the second. 568 pars, _ = compare.parse_pars(compare_opts) 529 569 return pars 530 570 … … 535 575 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 536 576 ) 537 parser.add_argument('-p', '--probability', type=float, default=0.1, help="scattering probability") 538 parser.add_argument('-n', '--nq', type=int, default=1024, help='number of mesh points') 539 parser.add_argument('-q', '--qmax', type=float, default=0.5, help='max q') 540 parser.add_argument('-w', '--window', type=float, default=2.0, help='q calc = q max * window') 541 parser.add_argument('-2', '--2d', dest='is2d', action='store_true', help='oriented sample') 542 parser.add_argument('-s', '--seed', default=-1, help='random pars with given seed') 543 parser.add_argument('-r', '--random', action='store_true', help='random pars with random seed') 544 parser.add_argument('-o', '--outfile', type=str, default="", help='random pars with random seed') 545 parser.add_argument('model', type=str, help='sas model name such as cylinder') 546 parser.add_argument('pars', type=str, nargs='*', help='model parameters such as radius=30') 577 parser.add_argument('-p', '--probability', type=float, default=0.1, 578 help="scattering probability") 579 parser.add_argument('-n', '--nq', type=int, default=1024, 580 help='number of mesh points') 581 parser.add_argument('-q', '--qmax', type=float, default=0.5, 582 help='max q') 583 parser.add_argument('-w', '--window', type=float, default=2.0, 584 help='q calc = q max * window') 585 parser.add_argument('-2', '--2d', dest='is2d', action='store_true', 586 help='oriented sample') 587 parser.add_argument('-s', '--seed', default=-1, 588 help='random pars with given seed') 589 parser.add_argument('-r', '--random', action='store_true', 590 help='random pars with random seed') 591 parser.add_argument('-o', '--outfile', type=str, default="", 592 help='random pars with random seed') 593 parser.add_argument('model', type=str, 594 help='sas model name such as cylinder') 595 parser.add_argument('pars', type=str, nargs='*', 596 help='model parameters such as radius=30') 547 597 opts = parser.parse_args() 548 598 assert opts.nq%2 == 0, "require even # points" … … 592 642 plotxy((res._q_steps, res._q_steps), res.Iqxy+background) 593 643 pylab.title("total scattering for p=%g" % probability) 644 if res.resolution is not None: 645 pylab.figure() 646 plotxy((res._q_steps, res._q_steps), result) 647 pylab.title("total scattering with resolution") 594 648 else: 595 649 q = res._q … … 609 663 # Plot 1D pattern for partial scattering 610 664 pylab.loglog(q, res.Iq+background, label="total for p=%g"%probability) 665 if res.resolution is not None: 666 pylab.loglog(q, result, label="total with dQ") 611 667 #new_annulus = annular_average(res._radius, res.Iqxy, res._edges) 612 668 #pylab.loglog(q, new_annulus+background, label="new total for p=%g"%probability) -
sasmodels/models/core_shell_parallelepiped.c
re077231 rdbf1a60 59 59 60 60 // outer integral (with gauss points), integration limits = 0, 1 61 // substitute d_cos_alpha for sin_alpha d_alpha 61 62 double outer_sum = 0; //initialize integral 62 63 for( int i=0; i<GAUSS_N; i++) { 63 64 const double cos_alpha = 0.5 * ( GAUSS_Z[i] + 1.0 ); 64 65 const double mu = half_q * sqrt(1.0-cos_alpha*cos_alpha); 65 66 // inner integral (with gauss points), integration limits = 0, pi/267 66 const double siC = length_c * sas_sinx_x(length_c * cos_alpha * half_q); 68 67 const double siCt = tC * sas_sinx_x(tC * cos_alpha * half_q); 68 69 // inner integral (with gauss points), integration limits = 0, 1 70 // substitute beta = PI/2 u (so 2/PI * d_(PI/2 * beta) = d_beta) 69 71 double inner_sum = 0.0; 70 72 for(int j=0; j<GAUSS_N; j++) { 71 const double beta= 0.5 * ( GAUSS_Z[j] + 1.0 );73 const double u = 0.5 * ( GAUSS_Z[j] + 1.0 ); 72 74 double sin_beta, cos_beta; 73 SINCOS(M_PI_2* beta, sin_beta, cos_beta);75 SINCOS(M_PI_2*u, sin_beta, cos_beta); 74 76 const double siA = length_a * sas_sinx_x(length_a * mu * sin_beta); 75 77 const double siB = length_b * sas_sinx_x(length_b * mu * cos_beta); … … 91 93 inner_sum += GAUSS_W[j] * f * f; 92 94 } 95 // now complete change of inner integration variable (1-0)/(1-(-1))= 0.5 93 96 inner_sum *= 0.5; 94 97 // now sum up the outer integral 95 98 outer_sum += GAUSS_W[i] * inner_sum; 96 99 } 100 // now complete change of outer integration variable (1-0)/(1-(-1))= 0.5 97 101 outer_sum *= 0.5; 98 102 -
sasmodels/models/core_shell_parallelepiped.py
r97be877 r5bc6d21 11 11 .. math:: 12 12 13 I(q) = \text{scale}\frac{\langle f^2 \rangle}{V} + \text{background} 13 I(q) = \frac{\text{scale}}{V} \langle P(q,\alpha,\beta) \rangle 14 + \text{background} 14 15 15 16 where $\langle \ldots \rangle$ is an average over all possible orientations 16 of the rectangular solid .17 18 The function calculated is the form factor of the rectangular solid below. 17 of the rectangular solid, and the usual $\Delta \rho^2 \ V^2$ term cannot be 18 pulled out of the form factor term due to the multiple slds in the model. 19 19 20 The core of the solid is defined by the dimensions $A$, $B$, $C$ such that 20 21 $A < B < C$. 21 22 22 .. image:: img/core_shell_parallelepiped_geometry.jpg 23 .. figure:: img/parallelepiped_geometry.jpg 24 25 Core of the core shell parallelepiped with the corresponding definition 26 of sides. 27 23 28 24 29 There are rectangular "slabs" of thickness $t_A$ that add to the $A$ dimension 25 30 (on the $BC$ faces). There are similar slabs on the $AC$ $(=t_B)$ and $AB$ 26 $(=t_C)$ faces. The projection in the $AB$ plane is then 27 28 .. image:: img/core_shell_parallelepiped_projection.jpg 29 30 The volume of the solid is 31 $(=t_C)$ faces. The projection in the $AB$ plane is 32 33 .. figure:: img/core_shell_parallelepiped_projection.jpg 34 35 AB cut through the core-shell parallelipiped showing the cross secion of 36 four of the six shell slabs. As can be seen, this model leaves **"gaps"** 37 at the corners of the solid. 38 39 40 The total volume of the solid is thus given as 31 41 32 42 .. math:: 33 43 34 44 V = ABC + 2t_ABC + 2t_BAC + 2t_CAB 35 36 **meaning that there are "gaps" at the corners of the solid.**37 45 38 46 The intensity calculated follows the :ref:`parallelepiped` model, with the 39 47 core-shell intensity being calculated as the square of the sum of the 40 amplitudes of the core and the slabs on the edges. 41 42 the scattering amplitude is computed for a particular orientation of the 43 core-shell parallelepiped with respect to the scattering vector and then 44 averaged over all possible orientations, where $\alpha$ is the angle between 45 the $z$ axis and the $C$ axis of the parallelepiped, $\beta$ is 46 the angle between projection of the particle in the $xy$ detector plane 47 and the $y$ axis. 48 49 .. math:: 50 51 F(Q) 48 amplitudes of the core and the slabs on the edges. The scattering amplitude is 49 computed for a particular orientation of the core-shell parallelepiped with 50 respect to the scattering vector and then averaged over all possible 51 orientations, where $\alpha$ is the angle between the $z$ axis and the $C$ axis 52 of the parallelepiped, and $\beta$ is the angle between the projection of the 53 particle in the $xy$ detector plane and the $y$ axis. 54 55 .. math:: 56 57 P(q)=\frac {\int_{0}^{\pi/2}\int_{0}^{\pi/2}F^2(q,\alpha,\beta) \ sin\alpha 58 \ d\alpha \ d\beta} {\int_{0}^{\pi/2} \ sin\alpha \ d\alpha \ d\beta} 59 60 and 61 62 .. math:: 63 64 F(q,\alpha,\beta) 52 65 &= (\rho_\text{core}-\rho_\text{solvent}) 53 66 S(Q_A, A) S(Q_B, B) S(Q_C, C) \\ 54 67 &+ (\rho_\text{A}-\rho_\text{solvent}) 55 \left[S(Q_A, A+2t_A) - S(Q_A, Q)\right] S(Q_B, B) S(Q_C, C) \\68 \left[S(Q_A, A+2t_A) - S(Q_A, A)\right] S(Q_B, B) S(Q_C, C) \\ 56 69 &+ (\rho_\text{B}-\rho_\text{solvent}) 57 70 S(Q_A, A) \left[S(Q_B, B+2t_B) - S(Q_B, B)\right] S(Q_C, C) \\ … … 63 76 .. math:: 64 77 65 S(Q , L) = L \frac{\sin \tfrac{1}{2} Q L}{\tfrac{1}{2} QL}78 S(Q_X, L) = L \frac{\sin (\tfrac{1}{2} Q_X L)}{\tfrac{1}{2} Q_X L} 66 79 67 80 and … … 69 82 .. math:: 70 83 71 Q_A &= \sin\alpha \sin\beta \\72 Q_B &= \sin\alpha \cos\beta \\73 Q_C &= \cos\alpha84 Q_A &= q \sin\alpha \sin\beta \\ 85 Q_B &= q \sin\alpha \cos\beta \\ 86 Q_C &= q \cos\alpha 74 87 75 88 76 89 where $\rho_\text{core}$, $\rho_\text{A}$, $\rho_\text{B}$ and $\rho_\text{C}$ 77 are the scattering length of the parallelepiped core, and the rectangular90 are the scattering lengths of the parallelepiped core, and the rectangular 78 91 slabs of thickness $t_A$, $t_B$ and $t_C$, respectively. $\rho_\text{solvent}$ 79 92 is the scattering length of the solvent. 93 94 .. note:: 95 96 the code actually implements two substitutions: $d(cos\alpha)$ is 97 substituted for -$sin\alpha \ d\alpha$ (note that in the 98 :ref:`parallelepiped` code this is explicitly implemented with 99 $\sigma = cos\alpha$), and $\beta$ is set to $\beta = u \pi/2$ so that 100 $du = \pi/2 \ d\beta$. Thus both integrals go from 0 to 1 rather than 0 101 to $\pi/2$. 80 102 81 103 FITTING NOTES … … 94 116 based on the the averaged effective radius $(=\sqrt{(A+2t_A)(B+2t_B)/\pi})$ 95 117 and length $(C+2t_C)$ values, after appropriately sorting the three dimensions 96 to give an oblate or prolate particle, to give an effective radius ,97 for $S( Q)$ when $P(Q) * S(Q)$ is applied.118 to give an oblate or prolate particle, to give an effective radius 119 for $S(q)$ when $P(q) * S(q)$ is applied. 98 120 99 121 For 2d data the orientation of the particle is required, described using 100 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below , for further122 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below. For further 101 123 details of the calculation and angular dispersions see :ref:`orientation`. 102 124 The angle $\Psi$ is the rotational angle around the *long_c* axis. For example, 103 125 $\Psi = 0$ when the *short_b* axis is parallel to the *x*-axis of the detector. 104 126 105 For 2d, constraints must be applied during fitting to ensure that the106 inequality $A < B < C$ is not violated, and hence the correct definition107 of angles is preserved. The calculation will not report an error,108 but the results may be not correct.127 .. note:: For 2d, constraints must be applied during fitting to ensure that the 128 inequality $A < B < C$ is not violated, and hence the correct definition 129 of angles is preserved. The calculation will not report an error, 130 but the results may be not correct. 109 131 110 132 .. figure:: img/parallelepiped_angle_definition.png … … 113 135 Note that rotation $\theta$, initially in the $xz$ plane, is carried 114 136 out first, then rotation $\phi$ about the $z$ axis, finally rotation 115 $\Psi$ is now around the axis of the cylinder. The neutron or X-ray137 $\Psi$ is now around the axis of the particle. The neutron or X-ray 116 138 beam is along the $z$ axis. 117 139 … … 120 142 Examples of the angles for oriented core-shell parallelepipeds against the 121 143 detector plane. 144 145 146 Validation 147 ---------- 148 149 Cross-checked against hollow rectangular prism and rectangular prism for equal 150 thickness overlapping sides, and by Monte Carlo sampling of points within the 151 shape for non-uniform, non-overlapping sides. 152 122 153 123 154 References … … 135 166 136 167 * **Author:** NIST IGOR/DANSE **Date:** pre 2010 137 * **Converted to sasmodels by:** Miguel Gonzale s**Date:** February 26, 2016168 * **Converted to sasmodels by:** Miguel Gonzalez **Date:** February 26, 2016 138 169 * **Last Modified by:** Paul Kienzle **Date:** October 17, 2017 139 * Cross-checked against hollow rectangular prism and rectangular prism for140 equal thickness overlapping sides, and by Monte Carlo sampling of points141 within the shape for non-uniform, non-overlapping sides.142 170 """ 143 171 -
sasmodels/models/parallelepiped.c
r108e70e rdbf1a60 38 38 inner_total += GAUSS_W[j] * square(si1 * si2); 39 39 } 40 // now complete change of inner integration variable (1-0)/(1-(-1))= 0.5 40 41 inner_total *= 0.5; 41 42 … … 43 44 outer_total += GAUSS_W[i] * inner_total * si * si; 44 45 } 46 // now complete change of outer integration variable (1-0)/(1-(-1))= 0.5 45 47 outer_total *= 0.5; 46 48 -
sasmodels/models/parallelepiped.py
ref07e95 r5bc6d21 2 2 # Note: model title and parameter table are inserted automatically 3 3 r""" 4 The form factor is normalized by the particle volume.5 For information about polarised and magnetic scattering, see6 the :ref:`magnetism` documentation.7 8 4 Definition 9 5 ---------- 10 6 11 7 This model calculates the scattering from a rectangular parallelepiped 12 (\:numref:`parallelepiped-image`\). 13 If you need to apply polydispersity, see also :ref:`rectangular-prism`. 8 (:numref:`parallelepiped-image`). 9 If you need to apply polydispersity, see also :ref:`rectangular-prism`. For 10 information about polarised and magnetic scattering, see 11 the :ref:`magnetism` documentation. 14 12 15 13 .. _parallelepiped-image: … … 26 24 error, or fixing of some dimensions at expected values, may help. 27 25 28 The 1D scattering intensity $I(q)$ is calculated as: 26 The form factor is normalized by the particle volume and the 1D scattering 27 intensity $I(q)$ is then calculated as: 29 28 30 29 .. Comment by Miguel Gonzalez: … … 39 38 40 39 I(q) = \frac{\text{scale}}{V} (\Delta\rho \cdot V)^2 41 \left< P(q, \alpha ) \right> + \text{background}40 \left< P(q, \alpha, \beta) \right> + \text{background} 42 41 43 42 where the volume $V = A B C$, the contrast is defined as 44 $\Delta\rho = \rho_\text{p} - \rho_\text{solvent}$, 45 $P(q, \alpha)$ is the form factor corresponding to a parallelepiped oriented 46 at an angle $\alpha$ (angle between the long axis C and $\vec q$), 47 and the averaging $\left<\ldots\right>$ is applied over all orientations. 43 $\Delta\rho = \rho_\text{p} - \rho_\text{solvent}$, $P(q, \alpha, \beta)$ 44 is the form factor corresponding to a parallelepiped oriented 45 at an angle $\alpha$ (angle between the long axis C and $\vec q$), and $\beta$ 46 ( the angle between the projection of the particle in the $xy$ detector plane 47 and the $y$ axis) and the averaging $\left<\ldots\right>$ is applied over all 48 orientations. 48 49 49 50 Assuming $a = A/B < 1$, $b = B /B = 1$, and $c = C/B > 1$, the 50 form factor is given by (Mittelbach and Porod, 1961 )51 form factor is given by (Mittelbach and Porod, 1961 [#Mittelbach]_) 51 52 52 53 .. math:: … … 66 67 \mu &= qB 67 68 68 The scattering intensity per unit volume is returned in units of |cm^-1|. 69 where substitution of $\sigma = cos\alpha$ and $\beta = \pi/2 \ u$ have been 70 applied. 69 71 70 72 NB: The 2nd virial coefficient of the parallelepiped is calculated based on … … 120 122 .. math:: 121 123 122 P(q_x, q_y) = \left[\frac{\sin(\tfrac{1}{2}qA\cos\alpha)}{(\tfrac{1}{2}qA\cos\alpha)}\right]^2 123 \left[\frac{\sin(\tfrac{1}{2}qB\cos\beta)}{(\tfrac{1}{2}qB\cos\beta)}\right]^2 124 \left[\frac{\sin(\tfrac{1}{2}qC\cos\gamma)}{(\tfrac{1}{2}qC\cos\gamma)}\right]^2 124 P(q_x, q_y) = \left[\frac{\sin(\tfrac{1}{2}qA\cos\alpha)}{(\tfrac{1} 125 {2}qA\cos\alpha)}\right]^2 126 \left[\frac{\sin(\tfrac{1}{2}qB\cos\beta)}{(\tfrac{1} 127 {2}qB\cos\beta)}\right]^2 128 \left[\frac{\sin(\tfrac{1}{2}qC\cos\gamma)}{(\tfrac{1} 129 {2}qC\cos\gamma)}\right]^2 125 130 126 131 with … … 160 165 ---------- 161 166 162 P Mittelbach and G Porod, *Acta Physica Austriaca*, 14 (1961) 185-211 163 164 R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854167 .. [#Mittelbach] P Mittelbach and G Porod, *Acta Physica Austriaca*, 168 14 (1961) 185-211 169 .. [#] R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854 165 170 166 171 Authorship and Verification
Note: See TracChangeset
for help on using the changeset viewer.