Changeset bc1dac6 in sasmodels
- Timestamp:
- Jul 5, 2018 8:24:26 AM (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:
- dc76240
- Parents:
- 1cdfd47 (diff), 1e7b202a (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. - Files:
-
- 1 deleted
- 18 edited
Legend:
- Unmodified
- Added
- Removed
-
.travis.yml
r335271e r5c36bf1 6 6 env: 7 7 - PY=2.7 8 - DEPLOY=True8 #- DEPLOY=True 9 9 - os: linux 10 10 env: -
doc/guide/magnetism/magnetism.rst
r4f5afc9 rbefe905 39 39 40 40 .. math:: 41 -- &= ( (1-u_i)(1-u_f))^{1/4}\\42 -+ &= ( (1-u_i)(u_f))^{1/4}\\43 +- &= ( (u_i)(1-u_f))^{1/4}\\44 ++ &= ( (u_i)(u_f))^{1/4}41 -- &= (1-u_i)(1-u_f) \\ 42 -+ &= (1-u_i)(u_f) \\ 43 +- &= (u_i)(1-u_f) \\ 44 ++ &= (u_i)(u_f) 45 45 46 46 Ideally the experiment would measure the pure spin states independently and … … 104 104 | 2015-05-02 Steve King 105 105 | 2017-11-15 Paul Kienzle 106 | 2018-06-02 Adam Washington -
doc/guide/plugin.rst
r7e6bc45e rf796469 822 822 823 823 :code:`source = ["lib/Si.c", ...]` 824 (`Si.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/ Si.c>`_)824 (`Si.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/sas_Si.c>`_) 825 825 826 826 sas_3j1x_x(x): -
sasmodels/compare.py
rd86f0fc r1e7b202a 107 107 -title="note" adds note to the plot title, after the model name 108 108 -weights shows weights plots for the polydisperse parameters 109 -profile shows the sld profile if the model has a plottable sld profile 109 110 110 111 === output options === … … 645 646 646 647 def make_data(opts): 647 # type: (Dict[str, Any] ) -> Tuple[Data, np.ndarray]648 # type: (Dict[str, Any], float) -> Tuple[Data, np.ndarray] 648 649 """ 649 650 Generate an empty dataset, used with the model to set Q points … … 667 668 if opts['zero']: 668 669 q = np.hstack((0, q)) 669 data = empty_data1D(q, resolution=res) 670 # TODO: provide command line control of lambda and Delta lambda/lambda 671 #L, dLoL = 5, 0.14/np.sqrt(6) # wavelength and 14% triangular FWHM 672 L, dLoL = 0, 0 673 data = empty_data1D(q, resolution=res, L=L, dL=L*dLoL) 670 674 index = slice(None, None) 671 675 return data, index … … 752 756 """ 753 757 for k in range(opts['sets']): 754 if k > 1:758 if k > 0: 755 759 # print a separate seed for each dataset for better reproducibility 756 760 new_seed = np.random.randint(1000000) 757 print(" Set %d uses -random=%i"%(k+1, new_seed))761 print("=== Set %d uses -random=%i ==="%(k+1, new_seed)) 758 762 np.random.seed(new_seed) 759 763 opts['pars'] = parse_pars(opts, maxdim=maxdim) … … 762 766 result = run_models(opts, verbose=True) 763 767 if opts['plot']: 768 if opts['is2d'] and k > 0: 769 import matplotlib.pyplot as plt 770 plt.figure() 764 771 limits = plot_models(opts, result, limits=limits, setnum=k) 765 772 if opts['show_weights']: … … 769 776 dim = base._kernel.dim 770 777 plot_weights(model_info, get_mesh(model_info, base_pars, dim=dim)) 778 if opts['show_profile']: 779 import pylab 780 base, comp = opts['engines'] 781 base_pars, comp_pars = opts['pars'] 782 have_base = base._kernel.info.profile is not None 783 have_comp = ( 784 comp is not None 785 and comp._kernel.info.profile is not None 786 and base_pars != comp_pars 787 ) 788 if have_base or have_comp: 789 pylab.figure() 790 if have_base: 791 plot_profile(base._kernel.info, **base_pars) 792 if have_comp: 793 plot_profile(comp._kernel.info, label='comp', **comp_pars) 794 pylab.legend() 771 795 if opts['plot']: 772 796 import matplotlib.pyplot as plt … … 774 798 return limits 775 799 800 def plot_profile(model_info, label='base', **args): 801 # type: (ModelInfo, List[Tuple[float, np.ndarray, np.ndarray]]) -> None 802 """ 803 Plot the profile returned by the model profile method. 804 805 *model_info* defines model parameters, etc. 806 807 *mesh* is a list of tuples containing (*value*, *dispersity*, *weights*) 808 for each parameter, where (*dispersity*, *weights*) pairs are the 809 distributions to be plotted. 810 """ 811 import pylab 812 813 args = dict((k, v) for k, v in args.items() 814 if "_pd" not in k 815 and ":" not in k 816 and k not in ("background", "scale", "theta", "phi", "psi")) 817 args = args.copy() 818 819 args.pop('scale', 1.) 820 args.pop('background', 0.) 821 z, rho = model_info.profile(**args) 822 #pylab.interactive(True) 823 pylab.plot(z, rho, '-', label=label) 824 pylab.grid(True) 825 #pylab.show() 826 827 828 776 829 def run_models(opts, verbose=False): 777 830 # type: (Dict[str, Any]) -> Dict[str, Any] … … 783 836 base_n, comp_n = opts['count'] 784 837 base_pars, comp_pars = opts['pars'] 785 data = opts['data']838 base_data, comp_data = opts['data'] 786 839 787 840 comparison = comp is not None … … 797 850 print("%s t=%.2f ms, intensity=%.0f" 798 851 % (base.engine, base_time, base_value.sum())) 799 _show_invalid( data, base_value)852 _show_invalid(base_data, base_value) 800 853 except ImportError: 801 854 traceback.print_exc() … … 809 862 print("%s t=%.2f ms, intensity=%.0f" 810 863 % (comp.engine, comp_time, comp_value.sum())) 811 _show_invalid( data, comp_value)864 _show_invalid(base_data, comp_value) 812 865 except ImportError: 813 866 traceback.print_exc() … … 863 916 have_base, have_comp = (base_value is not None), (comp_value is not None) 864 917 base, comp = opts['engines'] 865 data = opts['data']918 base_data, comp_data = opts['data'] 866 919 use_data = (opts['datafile'] is not None) and (have_base ^ have_comp) 867 920 868 921 # Plot if requested 869 922 view = opts['view'] 923 #view = 'log' 870 924 if limits is None: 871 925 vmin, vmax = np.inf, -np.inf … … 881 935 if have_comp: 882 936 plt.subplot(131) 883 plot_theory( data, base_value, view=view, use_data=use_data, limits=limits)937 plot_theory(base_data, base_value, view=view, use_data=use_data, limits=limits) 884 938 plt.title("%s t=%.2f ms"%(base.engine, base_time)) 885 939 #cbar_title = "log I" … … 888 942 plt.subplot(132) 889 943 if not opts['is2d'] and have_base: 890 plot_theory( data, base_value, view=view, use_data=use_data, limits=limits)891 plot_theory( data, comp_value, view=view, use_data=use_data, limits=limits)944 plot_theory(comp_data, base_value, view=view, use_data=use_data, limits=limits) 945 plot_theory(comp_data, comp_value, view=view, use_data=use_data, limits=limits) 892 946 plt.title("%s t=%.2f ms"%(comp.engine, comp_time)) 893 947 #cbar_title = "log I" … … 905 959 err[err > cutoff] = cutoff 906 960 #err,errstr = base/comp,"ratio" 907 plot_theory(data, None, resid=err, view=errview, use_data=use_data) 961 # Note: base_data only since base and comp have same q values (though 962 # perhaps different resolution), and we are plotting the difference 963 # at each q 964 plot_theory(base_data, None, resid=err, view=errview, use_data=use_data) 908 965 plt.xscale('log' if view == 'log' and not opts['is2d'] else 'linear') 909 966 plt.legend(['P%d'%(k+1) for k in range(setnum+1)], loc='best') … … 939 996 OPTIONS = [ 940 997 # Plotting 941 'plot', 'noplot', 'weights', 998 'plot', 'noplot', 999 'weights', 'profile', 942 1000 'linear', 'log', 'q4', 943 1001 'rel', 'abs', … … 1072 1130 'qmax' : 0.05, 1073 1131 'nq' : 128, 1074 'res' : 0.0,1132 'res' : '0.0', 1075 1133 'noise' : 0.0, 1076 1134 'accuracy' : 'Low', … … 1093 1151 'count' : '1', 1094 1152 'show_weights' : False, 1153 'show_profile' : False, 1095 1154 'sphere' : 0, 1096 1155 'ngauss' : '0', … … 1112 1171 elif arg.startswith('-q='): 1113 1172 opts['qmin'], opts['qmax'] = [float(v) for v in arg[3:].split(':')] 1114 elif arg.startswith('-res='): opts['res'] = float(arg[5:])1173 elif arg.startswith('-res='): opts['res'] = arg[5:] 1115 1174 elif arg.startswith('-noise='): opts['noise'] = float(arg[7:]) 1116 1175 elif arg.startswith('-sets='): opts['sets'] = int(arg[6:]) … … 1153 1212 elif arg == '-default': opts['use_demo'] = False 1154 1213 elif arg == '-weights': opts['show_weights'] = True 1214 elif arg == '-profile': opts['show_profile'] = True 1155 1215 elif arg == '-html': opts['html'] = True 1156 1216 elif arg == '-help': opts['html'] = True … … 1170 1230 if opts['qmin'] is None: 1171 1231 opts['qmin'] = 0.001*opts['qmax'] 1172 if opts['datafile'] is not None:1173 data = load_data(os.path.expanduser(opts['datafile']))1174 else:1175 data, _ = make_data(opts)1176 1232 1177 1233 comparison = any(PAR_SPLIT in v for v in values) … … 1213 1269 opts['cutoff'] = [float(opts['cutoff'])]*2 1214 1270 1215 base = make_engine(model_info[0], data, opts['engine'][0], 1271 if PAR_SPLIT in opts['res']: 1272 opts['res'] = [float(k) for k in opts['res'].split(PAR_SPLIT, 2)] 1273 comparison = True 1274 else: 1275 opts['res'] = [float(opts['res'])]*2 1276 1277 if opts['datafile'] is not None: 1278 data = load_data(os.path.expanduser(opts['datafile'])) 1279 else: 1280 # Hack around the fact that make_data doesn't take a pair of resolutions 1281 res = opts['res'] 1282 opts['res'] = res[0] 1283 data0, _ = make_data(opts) 1284 if res[0] != res[1]: 1285 opts['res'] = res[1] 1286 data1, _ = make_data(opts) 1287 else: 1288 data1 = data0 1289 opts['res'] = res 1290 data = data0, data1 1291 1292 base = make_engine(model_info[0], data[0], opts['engine'][0], 1216 1293 opts['cutoff'][0], opts['ngauss'][0]) 1217 1294 if comparison: 1218 comp = make_engine(model_info[1], data , opts['engine'][1],1295 comp = make_engine(model_info[1], data[1], opts['engine'][1], 1219 1296 opts['cutoff'][1], opts['ngauss'][1]) 1220 1297 else: … … 1329 1406 1330 1407 # Evaluate preset parameter expressions 1408 # Note: need to replace ':' with '_' in parameter names and expressions 1409 # in order to support math on magnetic parameters. 1331 1410 context = MATH.copy() 1332 1411 context['np'] = np 1333 context.update( pars)1412 context.update((k.replace(':', '_'), v) for k, v in pars.items()) 1334 1413 context.update((k, v) for k, v in presets.items() if isinstance(v, float)) 1414 #for k,v in sorted(context.items()): print(k, v) 1335 1415 for k, v in presets.items(): 1336 1416 if not isinstance(v, float) and not k.endswith('_type'): 1337 presets[k] = eval(v , context)1417 presets[k] = eval(v.replace(':', '_'), context) 1338 1418 context.update(presets) 1339 context.update((k , v) for k, v in presets2.items() if isinstance(v, float))1419 context.update((k.replace(':', '_'), v) for k, v in presets2.items() if isinstance(v, float)) 1340 1420 for k, v in presets2.items(): 1341 1421 if not isinstance(v, float) and not k.endswith('_type'): 1342 presets2[k] = eval(v , context)1422 presets2[k] = eval(v.replace(':', '_'), context) 1343 1423 1344 1424 # update parameters with presets … … 1370 1450 path = os.path.dirname(info.filename) 1371 1451 url = "file://" + path.replace("\\", "/")[2:] + "/" 1372 rst2html.view_html_ qtapp(html, url)1452 rst2html.view_html_wxapp(html, url) 1373 1453 1374 1454 def explore(opts): -
sasmodels/data.py
rd86f0fc r65fbf7c 36 36 37 37 import numpy as np # type: ignore 38 from numpy import sqrt, sin, cos, pi 38 39 39 40 # pylint: disable=unused-import … … 301 302 302 303 303 def empty_data1D(q, resolution=0.0 ):304 def empty_data1D(q, resolution=0.0, L=0., dL=0.): 304 305 # type: (np.ndarray, float) -> Data1D 305 """306 r""" 306 307 Create empty 1D data using the given *q* as the x value. 307 308 308 *resolution* dq/q defaults to 5%. 309 rms *resolution* $\Delta q/q$ defaults to 0%. If wavelength *L* and rms 310 wavelength divergence *dL* are defined, then *resolution* defines 311 rms $\Delta \theta/\theta$ for the lowest *q*, with $\theta$ derived from 312 $q = 4\pi/\lambda \sin(\theta)$. 309 313 """ 310 314 … … 313 317 Iq, dIq = None, None 314 318 q = np.asarray(q) 315 data = Data1D(q, Iq, dx=resolution * q, dy=dIq) 319 if L != 0 and resolution != 0: 320 theta = np.arcsin(q*L/(4*pi)) 321 dtheta = theta[0]*resolution 322 ## Solving Gaussian error propagation from 323 ## Dq^2 = (dq/dL)^2 DL^2 + (dq/dtheta)^2 Dtheta^2 324 ## gives 325 ## (Dq/q)^2 = (DL/L)**2 + (Dtheta/tan(theta))**2 326 ## Take the square root and multiply by q, giving 327 ## Dq = (4*pi/L) * sqrt((sin(theta)*dL/L)**2 + (cos(theta)*dtheta)**2) 328 dq = (4*pi/L) * sqrt((sin(theta)*dL/L)**2 + (cos(theta)*dtheta)**2) 329 else: 330 dq = resolution * q 331 332 data = Data1D(q, Iq, dx=dq, dy=dIq) 316 333 data.filename = "fake data" 317 334 return data -
sasmodels/details.py
r108e70e r885753a 243 243 offset = np.cumsum(np.hstack((0, length))) 244 244 call_details = make_details(kernel.info, length, offset[:-1], offset[-1]) 245 # Pad value array to a 32 value boundary d245 # Pad value array to a 32 value boundary 246 246 data_len = nvalues + 2*sum(len(v) for v in dispersity) 247 247 extra = (32 - data_len%32)%32 … … 250 250 is_magnetic = convert_magnetism(kernel.info.parameters, data) 251 251 #call_details.show() 252 #print("data", data) 252 253 return call_details, data, is_magnetic 253 254 … … 296 297 mag = values[parameters.nvalues-3*parameters.nmagnetic:parameters.nvalues] 297 298 mag = mag.reshape(-1, 3) 298 scale = mag[:, 0]299 if np.any(scale):299 if np.any(mag[:, 0] != 0.0): 300 M0 = mag[:, 0].copy() 300 301 theta, phi = radians(mag[:, 1]), radians(mag[:, 2]) 301 cos_theta = cos(theta) 302 mag[:, 0] = scale*cos_theta*cos(phi) # mx 303 mag[:, 1] = scale*sin(theta) # my 304 mag[:, 2] = -scale*cos_theta*sin(phi) # mz 302 mag[:, 0] = +M0*cos(theta)*cos(phi) # mx 303 mag[:, 1] = +M0*sin(theta) # my 304 mag[:, 2] = -M0*cos(theta)*sin(phi) # mz 305 305 return True 306 306 else: -
sasmodels/jitter.py
rb3703f5 r1198f90 774 774 # set small jitter as 0 if multiple pd dims 775 775 dims = sum(v > 0 for v in jitter) 776 limit = [0, 0 , 0.5, 5][dims]776 limit = [0, 0.5, 5][dims] 777 777 jitter = [0 if v < limit else v for v in jitter] 778 778 axes.cla() -
sasmodels/kernel_iq.c
rd86f0fc r7c35fda 79 79 // du * (m_sigma_y + 1j*m_sigma_z); 80 80 // weights for spin crosssections: dd du real, ud real, uu, du imag, ud imag 81 static void set_spin_weights(double in_spin, double out_spin, double spins[4])81 static void set_spin_weights(double in_spin, double out_spin, double weight[6]) 82 82 { 83 83 in_spin = clip(in_spin, 0.0, 1.0); 84 84 out_spin = clip(out_spin, 0.0, 1.0); 85 spins[0] = sqrt(sqrt((1.0-in_spin) * (1.0-out_spin))); // dd 86 spins[1] = sqrt(sqrt((1.0-in_spin) * out_spin)); // du real 87 spins[2] = sqrt(sqrt(in_spin * (1.0-out_spin))); // ud real 88 spins[3] = sqrt(sqrt(in_spin * out_spin)); // uu 89 spins[4] = spins[1]; // du imag 90 spins[5] = spins[2]; // ud imag 85 // Previous version of this function took the square root of the weights, 86 // under the assumption that 87 // 88 // w*I(q, rho1, rho2, ...) = I(q, sqrt(w)*rho1, sqrt(w)*rho2, ...) 89 // 90 // However, since the weights are applied to the final intensity and 91 // are not interned inside the I(q) function, we want the full 92 // weight and not the square root. Any function using 93 // set_spin_weights as part of calculating an amplitude will need to 94 // manually take that square root, but there is currently no such 95 // function. 96 weight[0] = (1.0-in_spin) * (1.0-out_spin); // dd 97 weight[1] = (1.0-in_spin) * out_spin; // du 98 weight[2] = in_spin * (1.0-out_spin); // ud 99 weight[3] = in_spin * out_spin; // uu 100 weight[4] = weight[1]; // du.imag 101 weight[5] = weight[2]; // ud.imag 91 102 } 92 103 93 104 // Compute the magnetic sld 94 105 static double mag_sld( 95 const unsigned int xs, // 0=dd, 1=du real, 2=ud real, 3=uu, 4=du imag, 5=upimag106 const unsigned int xs, // 0=dd, 1=du.real, 2=ud.real, 3=uu, 4=du.imag, 5=ud.imag 96 107 const double qx, const double qy, 97 108 const double px, const double py, … … 106 117 case 0: // uu => sld - D M_perpx 107 118 return sld - px*perp; 108 case 1: // ud 119 case 1: // ud.real => -D M_perpy 109 120 return py*perp; 110 case 2: // du 121 case 2: // du.real => -D M_perpy 111 122 return py*perp; 112 case 3: // dd real=> sld + D M_perpx123 case 3: // dd => sld + D M_perpx 113 124 return sld + px*perp; 114 125 } 115 126 } else { 116 127 if (xs== 4) { 117 return -mz; // ud imag => -D M_perpz128 return -mz; // du.imag => +D M_perpz 118 129 } else { // index == 5 119 return mz; // du imag =>D M_perpz130 return +mz; // ud.imag => -D M_perpz 120 131 } 121 132 } … … 312 323 // up_angle = values[NUM_PARS+4]; 313 324 // TODO: could precompute more magnetism parameters before calling the kernel. 314 double spins[8]; // uu, ud real, du real, dd, ud imag, du imag, fill, fill325 double xs_weights[8]; // uu, ud real, du real, dd, ud imag, du imag, fill, fill 315 326 double cos_mspin, sin_mspin; 316 set_spin_weights(values[NUM_PARS+2], values[NUM_PARS+3], spins);327 set_spin_weights(values[NUM_PARS+2], values[NUM_PARS+3], xs_weights); 317 328 SINCOS(-values[NUM_PARS+4]*M_PI_180, sin_mspin, cos_mspin); 318 329 #endif // MAGNETIC … … 661 672 // loop over uu, ud real, du real, dd, ud imag, du imag 662 673 for (unsigned int xs=0; xs<6; xs++) { 663 const double xs_weight = spins[xs];674 const double xs_weight = xs_weights[xs]; 664 675 if (xs_weight > 1.e-8) { 665 676 // Since the cross section weight is significant, set the slds … … 674 685 local_values.vector[sld_index] = 675 686 mag_sld(xs, qx, qy, px, py, values[sld_index+2], mx, my, mz); 687 //if (q_index==0) printf("%d: (qx,qy)=(%g,%g) xs=%d sld%d=%g p=(%g,%g) m=(%g,%g,%g)\n", 688 // q_index, qx, qy, xs, sk, local_values.vector[sld_index], px, py, mx, my, mz); 676 689 } 677 690 scattering += xs_weight * CALL_KERNEL(); -
sasmodels/kerneldll.py
rbf94e6e r33969b6 123 123 # add openmp support if not running on a mac 124 124 if sys.platform != "darwin": 125 CC.append("-fopenmp") 125 # OpenMP seems to be broken on gcc 5.4.0 (ubuntu 16.04.9) 126 # Shut it off for all unix until we can investigate. 127 #CC.append("-fopenmp") 128 pass 126 129 def compile_command(source, output): 127 130 """unix compiler command""" -
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 rf89ec96 4 4 5 5 Calculates the form factor for a rectangular solid with a core-shell structure. 6 The thickness and the scattering length density of the shell or 7 "rim" can be different on each (pair) of faces. 6 The thickness and the scattering length density of the shell or "rim" can be 7 different on each (pair) of faces. The three dimensions of the core of the 8 parallelepiped (strictly here a cuboid) may be given in *any* size order as 9 long as the particles are randomly oriented (i.e. take on all possible 10 orientations see notes on 2D below). To avoid multiple fit solutions, 11 especially with Monte-Carlo fit methods, it may be advisable to restrict their 12 ranges. There may be a number of closely similar "best fits", so some trial and 13 error, or fixing of some dimensions at expected values, may help. 8 14 9 15 The form factor is normalized by the particle volume $V$ such that … … 11 17 .. math:: 12 18 13 I(q) = \text{scale}\frac{\langle f^2 \rangle}{V} + \text{background} 19 I(q) = \frac{\text{scale}}{V} \langle P(q,\alpha,\beta) \rangle 20 + \text{background} 14 21 15 22 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. 19 The core of the solid is defined by the dimensions $A$, $B$, $C$ such that 20 $A < B < C$. 21 22 .. image:: img/core_shell_parallelepiped_geometry.jpg 23 of the rectangular solid, and the usual $\Delta \rho^2 \ V^2$ term cannot be 24 pulled out of the form factor term due to the multiple slds in the model. 25 26 The core of the solid is defined by the dimensions $A$, $B$, $C$ here shown 27 such that $A < B < C$. 28 29 .. figure:: img/parallelepiped_geometry.jpg 30 31 Core of the core shell parallelepiped with the corresponding definition 32 of sides. 33 23 34 24 35 There are rectangular "slabs" of thickness $t_A$ that add to the $A$ dimension 25 36 (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 37 $(=t_C)$ faces. The projection in the $AB$ plane is 38 39 .. figure:: img/core_shell_parallelepiped_projection.jpg 40 41 AB cut through the core-shell parallelipiped showing the cross secion of 42 four of the six shell slabs. As can be seen, this model leaves **"gaps"** 43 at the corners of the solid. 44 45 46 The total volume of the solid is thus given as 31 47 32 48 .. math:: 33 49 34 50 V = ABC + 2t_ABC + 2t_BAC + 2t_CAB 35 36 **meaning that there are "gaps" at the corners of the solid.**37 51 38 52 The intensity calculated follows the :ref:`parallelepiped` model, with the 39 53 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) 54 amplitudes of the core and the slabs on the edges. The scattering amplitude is 55 computed for a particular orientation of the core-shell parallelepiped with 56 respect to the scattering vector and then averaged over all possible 57 orientations, where $\alpha$ is the angle between the $z$ axis and the $C$ axis 58 of the parallelepiped, and $\beta$ is the angle between the projection of the 59 particle in the $xy$ detector plane and the $y$ axis. 60 61 .. math:: 62 63 P(q)=\frac {\int_{0}^{\pi/2}\int_{0}^{\pi/2}F^2(q,\alpha,\beta) \ sin\alpha 64 \ d\alpha \ d\beta} {\int_{0}^{\pi/2} \ sin\alpha \ d\alpha \ d\beta} 65 66 and 67 68 .. math:: 69 70 F(q,\alpha,\beta) 52 71 &= (\rho_\text{core}-\rho_\text{solvent}) 53 72 S(Q_A, A) S(Q_B, B) S(Q_C, C) \\ 54 73 &+ (\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) \\74 \left[S(Q_A, A+2t_A) - S(Q_A, A)\right] S(Q_B, B) S(Q_C, C) \\ 56 75 &+ (\rho_\text{B}-\rho_\text{solvent}) 57 76 S(Q_A, A) \left[S(Q_B, B+2t_B) - S(Q_B, B)\right] S(Q_C, C) \\ … … 63 82 .. math:: 64 83 65 S(Q , L) = L \frac{\sin \tfrac{1}{2} Q L}{\tfrac{1}{2} QL}84 S(Q_X, L) = L \frac{\sin (\tfrac{1}{2} Q_X L)}{\tfrac{1}{2} Q_X L} 66 85 67 86 and … … 69 88 .. math:: 70 89 71 Q_A &= \sin\alpha \sin\beta \\72 Q_B &= \sin\alpha \cos\beta \\73 Q_C &= \cos\alpha90 Q_A &= q \sin\alpha \sin\beta \\ 91 Q_B &= q \sin\alpha \cos\beta \\ 92 Q_C &= q \cos\alpha 74 93 75 94 76 95 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 rectangular96 are the scattering lengths of the parallelepiped core, and the rectangular 78 97 slabs of thickness $t_A$, $t_B$ and $t_C$, respectively. $\rho_\text{solvent}$ 79 98 is the scattering length of the solvent. 80 99 100 .. note:: 101 102 the code actually implements two substitutions: $d(cos\alpha)$ is 103 substituted for -$sin\alpha \ d\alpha$ (note that in the 104 :ref:`parallelepiped` code this is explicitly implemented with 105 $\sigma = cos\alpha$), and $\beta$ is set to $\beta = u \pi/2$ so that 106 $du = \pi/2 \ d\beta$. Thus both integrals go from 0 to 1 rather than 0 107 to $\pi/2$. 108 81 109 FITTING NOTES 82 110 ~~~~~~~~~~~~~ 83 111 84 If the scale is set equal to the particle volume fraction, $\phi$, the returned 85 value is the scattered intensity per unit volume, $I(q) = \phi P(q)$. However, 86 **no interparticle interference effects are included in this calculation.** 87 88 There are many parameters in this model. Hold as many fixed as possible with 89 known values, or you will certainly end up at a solution that is unphysical. 90 91 The returned value is in units of |cm^-1|, on absolute scale. 92 93 NB: The 2nd virial coefficient of the core_shell_parallelepiped is calculated 94 based on the the averaged effective radius $(=\sqrt{(A+2t_A)(B+2t_B)/\pi})$ 95 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. 98 99 For 2d data the orientation of the particle is required, described using 100 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below, for further 101 details of the calculation and angular dispersions see :ref:`orientation`. 102 The angle $\Psi$ is the rotational angle around the *long_c* axis. For example, 103 $\Psi = 0$ when the *short_b* axis is parallel to the *x*-axis of the detector. 104 105 For 2d, constraints must be applied during fitting to ensure that the 106 inequality $A < B < C$ is not violated, and hence the correct definition 107 of angles is preserved. The calculation will not report an error, 108 but the results may be not correct. 112 #. There are many parameters in this model. Hold as many fixed as possible with 113 known values, or you will certainly end up at a solution that is unphysical. 114 115 #. The 2nd virial coefficient of the core_shell_parallelepiped is calculated 116 based on the the averaged effective radius $(=\sqrt{(A+2t_A)(B+2t_B)/\pi})$ 117 and length $(C+2t_C)$ values, after appropriately sorting the three 118 dimensions to give an oblate or prolate particle, to give an effective radius 119 for $S(q)$ when $P(q) * S(q)$ is applied. 120 121 #. For 2d data the orientation of the particle is required, described using 122 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below, where $\theta$ 123 and $\phi$ define the orientation of the director in the laboratry reference 124 frame of the beam direction ($z$) and detector plane ($x-y$ plane), while 125 the angle $\Psi$ is effectively the rotational angle around the particle 126 $C$ axis. For $\theta = 0$ and $\phi = 0$, $\Psi = 0$ corresponds to the 127 $B$ axis oriented parallel to the y-axis of the detector with $A$ along 128 the x-axis. For other $\theta$, $\phi$ values, the order of rotations 129 matters. In particular, the parallelepiped must first be rotated $\theta$ 130 degrees in the $x-z$ plane before rotating $\phi$ degrees around the $z$ 131 axis (in the $x-y$ plane). Applying orientational distribution to the 132 particle orientation (i.e `jitter` to one or more of these angles) can get 133 more confusing as `jitter` is defined **NOT** with respect to the laboratory 134 frame but the particle reference frame. It is thus highly recmmended to 135 read :ref:`orientation` for further details of the calculation and angular 136 dispersions. 137 138 .. note:: For 2d, constraints must be applied during fitting to ensure that the 139 order of sides chosen is not altered, and hence that the correct definition 140 of angles is preserved. For the default choice shown here, that means 141 ensuring that the inequality $A < B < C$ is not violated, The calculation 142 will not report an error, but the results may be not correct. 109 143 110 144 .. figure:: img/parallelepiped_angle_definition.png 111 145 112 146 Definition of the angles for oriented core-shell parallelepipeds. 113 Note that rotation $\theta$, initially in the $x z$ plane, is carried147 Note that rotation $\theta$, initially in the $x-z$ plane, is carried 114 148 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-ray116 beam is along the $z$ axis .149 $\Psi$ is now around the $C$ axis of the particle. The neutron or X-ray 150 beam is along the $z$ axis and the detecotr defines the $x-y$ plane. 117 151 118 152 .. figure:: img/parallelepiped_angle_projection.png … … 120 154 Examples of the angles for oriented core-shell parallelepipeds against the 121 155 detector plane. 156 157 158 Validation 159 ---------- 160 161 Cross-checked against hollow rectangular prism and rectangular prism for equal 162 thickness overlapping sides, and by Monte Carlo sampling of points within the 163 shape for non-uniform, non-overlapping sides. 164 122 165 123 166 References … … 135 178 136 179 * **Author:** NIST IGOR/DANSE **Date:** pre 2010 137 * **Converted to sasmodels by:** Miguel Gonzale s**Date:** February 26, 2016180 * **Converted to sasmodels by:** Miguel Gonzalez **Date:** February 26, 2016 138 181 * **Last Modified by:** Paul Kienzle **Date:** October 17, 2017 139 * Cross-checked against hollow rectangular prism and rectangular prism for 140 equal thickness overlapping sides, and by Monte Carlo sampling of points 141 within the shape for non-uniform, non-overlapping sides. 182 * **Last Reviewed by:** Paul Butler **Date:** May 24, 2018 - documentation 183 updated 142 184 """ 143 185 -
sasmodels/models/mass_surface_fractal.py
r2d81cfe r7994359 39 39 The surface ( $D_s$ ) and mass ( $D_m$ ) fractal dimensions are only 40 40 valid if $0 < surface\_dim < 6$ , $0 < mass\_dim < 6$ , and 41 $(surface\_dim + mass\_dim ) < 6$ . 42 41 $(surface\_dim + mass\_dim ) < 6$ . 42 Older versions of sasview may have the default primary particle radius 43 larger than the cluster radius, this was an error, also present in the 44 Schmidt review paper below. The primary particle should be the smaller 45 as described in the original Hurd et.al. who also point out that 46 polydispersity in the primary particle sizes may affect their 47 apparent surface fractal dimension. 48 43 49 44 50 References 45 51 ---------- 46 52 47 P Schmidt, *J Appl. Cryst.*, 24 (1991) 414-435 Equation(19) 53 .. [#] P Schmidt, *J Appl. Cryst.*, 24 (1991) 414-435 Equation(19) 54 .. [#] A J Hurd, D W Schaefer, J E Martin, *Phys. Rev. A*, 55 35 (1987) 2361-2364 Equation(2) 48 56 49 A J Hurd, D W Schaefer, J E Martin, *Phys. Rev. A*, 50 35 (1987) 2361-2364 Equation(2) 57 Authorship and Verification 58 ---------------------------- 59 60 * **Converted to sasmodels by:** Piotr Rozyczko **Date:** Jan 20, 2016 61 * **Last Reviewed by:** Richard Heenan **Date:** May 30, 2018 51 62 """ 52 63 … … 67 78 rg_primary = rg 68 79 background = background 69 Ref: Schmidt, J Appl Cryst, eq(19), (1991), 24, 414-43570 80 Hurd, Schaefer, Martin, Phys Rev A, eq(2),(1987),35, 2361-2364 71 81 Note that 0 < Ds< 6 and 0 < Dm < 6. … … 78 88 ["fractal_dim_mass", "", 1.8, [0.0, 6.0], "", "Mass fractal dimension"], 79 89 ["fractal_dim_surf", "", 2.3, [0.0, 6.0], "", "Surface fractal dimension"], 80 ["rg_cluster", "Ang", 86.7, [0.0, inf], "", "Cluster radius of gyration"],81 ["rg_primary", "Ang", 4000., [0.0, inf], "", "Primary particle radius of gyration"],90 ["rg_cluster", "Ang", 4000., [0.0, inf], "", "Cluster radius of gyration"], 91 ["rg_primary", "Ang", 86.7, [0.0, inf], "", "Primary particle radius of gyration"], 82 92 ] 83 93 # pylint: enable=bad-whitespace, line-too-long … … 107 117 fractal_dim_mass=1.8, 108 118 fractal_dim_surf=2.3, 109 rg_cluster= 86.7,110 rg_primary= 4000.0)119 rg_cluster=4000.0, 120 rg_primary=86.7) 111 121 112 122 tests = [ 113 123 114 # Accuracy tests based on content in test/utest_other_models.py 115 [{'fractal_dim_mass': 124 # Accuracy tests based on content in test/utest_other_models.py All except first, changed so rg_cluster is the larger, RKH 30 May 2018 125 [{'fractal_dim_mass': 1.8, 116 126 'fractal_dim_surf': 2.3, 117 127 'rg_cluster': 86.7, … … 123 133 [{'fractal_dim_mass': 3.3, 124 134 'fractal_dim_surf': 1.0, 125 'rg_cluster': 90.0,126 'rg_primary': 4000.0,127 }, 0.001, 0. 18562699016],135 'rg_cluster': 4000.0, 136 'rg_primary': 90.0, 137 }, 0.001, 0.0932516614456], 128 138 129 139 [{'fractal_dim_mass': 1.3, 130 'fractal_dim_surf': 1.0,131 'rg_cluster': 90.0,132 'rg_primary': 2000.0,140 'fractal_dim_surf': 2.0, 141 'rg_cluster': 2000.0, 142 'rg_primary': 90.0, 133 143 'background': 0.8, 134 }, 0.001, 1. 16539753641],144 }, 0.001, 1.28296431786], 135 145 136 146 [{'fractal_dim_mass': 2.3, 137 'fractal_dim_surf': 1.0,138 'rg_cluster': 90.0,139 'rg_primary': 1000.0,147 'fractal_dim_surf': 3.1, 148 'rg_cluster': 1000.0, 149 'rg_primary': 30.0, 140 150 'scale': 10.0, 141 151 'background': 0.0, 142 }, 0.051, 0.00 0169548800377],152 }, 0.051, 0.00333804044899], 143 153 ] -
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 rf89ec96 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 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`. 7 This model calculates the scattering from a rectangular solid 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: … … 21 19 22 20 The three dimensions of the parallelepiped (strictly here a cuboid) may be 23 given in *any* size order. To avoid multiple fit solutions, especially 24 with Monte-Carlo fit methods, it may be advisable to restrict their ranges. 25 There may be a number of closely similar "best fits", so some trial and 26 error, or fixing of some dimensions at expected values, may help. 27 28 The 1D scattering intensity $I(q)$ is calculated as: 21 given in *any* size order as long as the particles are randomly oriented (i.e. 22 take on all possible orientations see notes on 2D below). To avoid multiple fit 23 solutions, especially with Monte-Carlo fit methods, it may be advisable to 24 restrict their ranges. There may be a number of closely similar "best fits", so 25 some trial and error, or fixing of some dimensions at expected values, may 26 help. 27 28 The form factor is normalized by the particle volume and the 1D scattering 29 intensity $I(q)$ is then calculated as: 29 30 30 31 .. Comment by Miguel Gonzalez: … … 39 40 40 41 I(q) = \frac{\text{scale}}{V} (\Delta\rho \cdot V)^2 41 \left< P(q, \alpha ) \right> + \text{background}42 \left< P(q, \alpha, \beta) \right> + \text{background} 42 43 43 44 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. 45 $\Delta\rho = \rho_\text{p} - \rho_\text{solvent}$, $P(q, \alpha, \beta)$ 46 is the form factor corresponding to a parallelepiped oriented 47 at an angle $\alpha$ (angle between the long axis C and $\vec q$), and $\beta$ 48 (the angle between the projection of the particle in the $xy$ detector plane 49 and the $y$ axis) and the averaging $\left<\ldots\right>$ is applied over all 50 orientations. 48 51 49 52 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 )53 form factor is given by (Mittelbach and Porod, 1961 [#Mittelbach]_) 51 54 52 55 .. math:: … … 66 69 \mu &= qB 67 70 68 The scattering intensity per unit volume is returned in units of |cm^-1|. 69 70 NB: The 2nd virial coefficient of the parallelepiped is calculated based on 71 the averaged effective radius, after appropriately sorting the three 72 dimensions, to give an oblate or prolate particle, $(=\sqrt{AB/\pi})$ and 73 length $(= C)$ values, and used as the effective radius for 74 $S(q)$ when $P(q) \cdot S(q)$ is applied. 75 76 For 2d data the orientation of the particle is required, described using 77 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below, for further details 78 of the calculation and angular dispersions see :ref:`orientation` . 79 80 .. Comment by Miguel Gonzalez: 81 The following text has been commented because I think there are two 82 mistakes. Psi is the rotational angle around C (but I cannot understand 83 what it means against the q plane) and psi=0 corresponds to a||x and b||y. 84 85 The angle $\Psi$ is the rotational angle around the $C$ axis against 86 the $q$ plane. For example, $\Psi = 0$ when the $B$ axis is parallel 87 to the $x$-axis of the detector. 88 89 The angle $\Psi$ is the rotational angle around the $C$ axis. 90 For $\theta = 0$ and $\phi = 0$, $\Psi = 0$ corresponds to the $B$ axis 91 oriented parallel to the y-axis of the detector with $A$ along the x-axis. 92 For other $\theta$, $\phi$ values, the parallelepiped has to be first rotated 93 $\theta$ degrees in the $z-x$ plane and then $\phi$ degrees around the $z$ axis, 94 before doing a final rotation of $\Psi$ degrees around the resulting $C$ axis 95 of the particle to obtain the final orientation of the parallelepiped. 96 97 .. _parallelepiped-orientation: 98 99 .. figure:: img/parallelepiped_angle_definition.png 100 101 Definition of the angles for oriented parallelepiped, shown with $A<B<C$. 102 103 .. figure:: img/parallelepiped_angle_projection.png 104 105 Examples of the angles for an oriented parallelepiped against the 106 detector plane. 107 108 On introducing "Orientational Distribution" in the angles, "distribution of 109 theta" and "distribution of phi" parameters will appear. These are actually 110 rotations about axes $\delta_1$ and $\delta_2$ of the parallelepiped, 111 perpendicular to the $a$ x $c$ and $b$ x $c$ faces. (When $\theta = \phi = 0$ 112 these are parallel to the $Y$ and $X$ axes of the instrument.) The third 113 orientation distribution, in $\psi$, is about the $c$ axis of the particle, 114 perpendicular to the $a$ x $b$ face. Some experimentation may be required to 115 understand the 2d patterns fully as discussed in :ref:`orientation` . 116 117 For a given orientation of the parallelepiped, the 2D form factor is 118 calculated as 119 120 .. math:: 121 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 125 126 with 127 128 .. math:: 129 130 \cos\alpha &= \hat A \cdot \hat q, \\ 131 \cos\beta &= \hat B \cdot \hat q, \\ 132 \cos\gamma &= \hat C \cdot \hat q 133 134 and the scattering intensity as: 135 136 .. math:: 137 138 I(q_x, q_y) = \frac{\text{scale}}{V} V^2 \Delta\rho^2 P(q_x, q_y) 71 where substitution of $\sigma = cos\alpha$ and $\beta = \pi/2 \ u$ have been 72 applied. 73 74 For **oriented** particles, the 2D scattering intensity, $I(q_x, q_y)$, is 75 given as: 76 77 .. math:: 78 79 I(q_x, q_y) = \frac{\text{scale}}{V} (\Delta\rho \cdot V)^2 P(q_x, q_y) 139 80 + \text{background} 140 81 … … 148 89 with scale being the volume fraction. 149 90 91 Where $P(q_x, q_y)$ for a given orientation of the form factor is calculated as 92 93 .. math:: 94 95 P(q_x, q_y) = \left[\frac{\sin(\tfrac{1}{2}qA\cos\alpha)}{(\tfrac{1} 96 {2}qA\cos\alpha)}\right]^2 97 \left[\frac{\sin(\tfrac{1}{2}qB\cos\beta)}{(\tfrac{1} 98 {2}qB\cos\beta)}\right]^2 99 \left[\frac{\sin(\tfrac{1}{2}qC\cos\gamma)}{(\tfrac{1} 100 {2}qC\cos\gamma)}\right]^2 101 102 with 103 104 .. math:: 105 106 \cos\alpha &= \hat A \cdot \hat q, \\ 107 \cos\beta &= \hat B \cdot \hat q, \\ 108 \cos\gamma &= \hat C \cdot \hat q 109 110 111 FITTING NOTES 112 ~~~~~~~~~~~~~ 113 114 #. The 2nd virial coefficient of the parallelepiped is calculated based on 115 the averaged effective radius, after appropriately sorting the three 116 dimensions, to give an oblate or prolate particle, $(=\sqrt{AB/\pi})$ and 117 length $(= C)$ values, and used as the effective radius for 118 $S(q)$ when $P(q) \cdot S(q)$ is applied. 119 120 #. For 2d data the orientation of the particle is required, described using 121 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below, where $\theta$ 122 and $\phi$ define the orientation of the director in the laboratry reference 123 frame of the beam direction ($z$) and detector plane ($x-y$ plane), while 124 the angle $\Psi$ is effectively the rotational angle around the particle 125 $C$ axis. For $\theta = 0$ and $\phi = 0$, $\Psi = 0$ corresponds to the 126 $B$ axis oriented parallel to the y-axis of the detector with $A$ along 127 the x-axis. For other $\theta$, $\phi$ values, the order of rotations 128 matters. In particular, the parallelepiped must first be rotated $\theta$ 129 degrees in the $x-z$ plane before rotating $\phi$ degrees around the $z$ 130 axis (in the $x-y$ plane). Applying orientational distribution to the 131 particle orientation (i.e `jitter` to one or more of these angles) can get 132 more confusing as `jitter` is defined **NOT** with respect to the laboratory 133 frame but the particle reference frame. It is thus highly recmmended to 134 read :ref:`orientation` for further details of the calculation and angular 135 dispersions. 136 137 .. note:: For 2d, constraints must be applied during fitting to ensure that the 138 order of sides chosen is not altered, and hence that the correct definition 139 of angles is preserved. For the default choice shown here, that means 140 ensuring that the inequality $A < B < C$ is not violated, The calculation 141 will not report an error, but the results may be not correct. 142 143 .. _parallelepiped-orientation: 144 145 .. figure:: img/parallelepiped_angle_definition.png 146 147 Definition of the angles for oriented parallelepiped, shown with $A<B<C$. 148 149 .. figure:: img/parallelepiped_angle_projection.png 150 151 Examples of the angles for an oriented parallelepiped against the 152 detector plane. 153 154 .. Comment by Paul Butler 155 I am commenting this section out as we are trying to minimize the amount of 156 oritentational detail here and encourage the user to go to the full 157 orientation documentation so that changes can be made in just one place. 158 below is the commented paragrah: 159 On introducing "Orientational Distribution" in the angles, "distribution of 160 theta" and "distribution of phi" parameters will appear. These are actually 161 rotations about axes $\delta_1$ and $\delta_2$ of the parallelepiped, 162 perpendicular to the $a$ x $c$ and $b$ x $c$ faces. (When $\theta = \phi = 0$ 163 these are parallel to the $Y$ and $X$ axes of the instrument.) The third 164 orientation distribution, in $\psi$, is about the $c$ axis of the particle, 165 perpendicular to the $a$ x $b$ face. Some experimentation may be required to 166 understand the 2d patterns fully as discussed in :ref:`orientation` . 167 150 168 151 169 Validation … … 156 174 angles. 157 175 158 159 176 References 160 177 ---------- 161 178 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-854179 .. [#Mittelbach] P Mittelbach and G Porod, *Acta Physica Austriaca*, 180 14 (1961) 185-211 181 .. [#] R Nayuk and K Huber, *Z. Phys. Chem.*, 226 (2012) 837-854 165 182 166 183 Authorship and Verification … … 169 186 * **Author:** NIST IGOR/DANSE **Date:** pre 2010 170 187 * **Last Modified by:** Paul Kienzle **Date:** April 05, 2017 171 * **Last Reviewed by:** Richard Heenan **Date:** April 06, 2017 188 * **Last Reviewed by:** Miguel Gonzales and Paul Butler **Date:** May 24, 189 2018 - documentation updated 172 190 """ 173 191 -
sasmodels/resolution.py
r2d81cfe r0b9c6df 20 20 MINIMUM_RESOLUTION = 1e-8 21 21 MINIMUM_ABSOLUTE_Q = 0.02 # relative to the minimum q in the data 22 PINHOLE_N_SIGMA = 2.5 # From: Barker & Pedersen 1995 JAC 22 23 23 24 class Resolution(object): … … 65 66 *q_calc* is the list of points to calculate, or None if this should 66 67 be estimated from the *q* and *q_width*. 67 """ 68 def __init__(self, q, q_width, q_calc=None, nsigma=3): 68 69 *nsigma* is the width of the resolution function. Should be 2.5. 70 See :func:`pinhole_resolution` for details. 71 """ 72 def __init__(self, q, q_width, q_calc=None, nsigma=PINHOLE_N_SIGMA): 69 73 #*min_step* is the minimum point spacing to use when computing the 70 74 #underlying model. It should be on the order of … … 82 86 83 87 # Protect against models which are not defined for very low q. Limit 84 # the smallest q value evaluated (in absolute) to 0.02*min 88 # the smallest q value evaluated to 0.02*min. Note that negative q 89 # values are trimmed even for broad resolution. Although not possible 90 # from the geometry, they may appear since we are using a truncated 91 # gaussian to represent resolution rather than a skew distribution. 85 92 cutoff = MINIMUM_ABSOLUTE_Q*np.min(self.q) 86 self.q_calc = self.q_calc[ abs(self.q_calc)>= cutoff]93 self.q_calc = self.q_calc[self.q_calc >= cutoff] 87 94 88 95 # Build weight matrix from calculated q values 89 96 self.weight_matrix = pinhole_resolution( 90 self.q_calc, self.q, np.maximum(q_width, MINIMUM_RESOLUTION) )91 self.q_calc = abs(self.q_calc)97 self.q_calc, self.q, np.maximum(q_width, MINIMUM_RESOLUTION), 98 nsigma=nsigma) 92 99 93 100 def apply(self, theory): … … 101 108 *q* points at which the data is measured. 102 109 103 * dqx* slit width in qx104 105 * dqy* slit height in qy110 *qx_width* slit width in qx 111 112 *qy_width* slit height in qy 106 113 107 114 *q_calc* is the list of points to calculate, or None if this should … … 154 161 155 162 156 def pinhole_resolution(q_calc, q, q_width ):157 """163 def pinhole_resolution(q_calc, q, q_width, nsigma=PINHOLE_N_SIGMA): 164 r""" 158 165 Compute the convolution matrix *W* for pinhole resolution 1-D data. 159 166 … … 162 169 *W*, the resolution smearing can be computed using *dot(W,q)*. 163 170 171 Note that resolution is limited to $\pm 2.5 \sigma$.[1] The true resolution 172 function is a broadened triangle, and does not extend over the entire 173 range $(-\infty, +\infty)$. It is important to impose this limitation 174 since some models fall so steeply that the weighted value in gaussian 175 tails would otherwise dominate the integral. 176 164 177 *q_calc* must be increasing. *q_width* must be greater than zero. 178 179 [1] Barker, J. G., and J. S. Pedersen. 1995. Instrumental Smearing Effects 180 in Radially Symmetric Small-Angle Neutron Scattering by Numerical and 181 Analytical Methods. Journal of Applied Crystallography 28 (2): 105--14. 182 https://doi.org/10.1107/S0021889894010095. 165 183 """ 166 184 # The current algorithm is a midpoint rectangle rule. In the test case, … … 170 188 cdf = erf((edges[:, None] - q[None, :]) / (sqrt(2.0)*q_width)[None, :]) 171 189 weights = cdf[1:] - cdf[:-1] 190 # Limit q range to +/- 2.5 sigma 191 qhigh = q + nsigma*q_width 192 #qlow = q - nsigma*q_width # linear limits 193 qlow = q*q/qhigh # log limits 194 weights[q_calc[:, None] < qlow[None, :]] = 0. 195 weights[q_calc[:, None] > qhigh[None, :]] = 0. 172 196 weights /= np.sum(weights, axis=0)[None, :] 173 197 return weights … … 494 518 495 519 496 def gaussian(q, q0, dq ):497 """ 498 Return the Gaussian resolution function.520 def gaussian(q, q0, dq, nsigma=2.5): 521 """ 522 Return the truncated Gaussian resolution function. 499 523 500 524 *q0* is the center, *dq* is the width and *q* are the points to evaluate. 501 525 """ 502 return exp(-0.5*((q-q0)/dq)**2)/(sqrt(2*pi)*dq) 526 # Calculate the density of the tails; the resulting gaussian needs to be 527 # scaled by this amount in ordere to integrate to 1.0 528 two_tail_density = 2 * (1 + erf(-nsigma/sqrt(2)))/2 529 return exp(-0.5*((q-q0)/dq)**2)/(sqrt(2*pi)*dq)/(1-two_tail_density) 503 530 504 531 … … 558 585 559 586 560 def romberg_pinhole_1d(q, q_width, form, pars, nsigma= 5):587 def romberg_pinhole_1d(q, q_width, form, pars, nsigma=2.5): 561 588 """ 562 589 Romberg integration for pinhole resolution. … … 678 705 np.testing.assert_equal(output, self.y) 679 706 707 # TODO: turn pinhole/slit demos into tests 708 709 @unittest.skip("suppress comparison with old version; pinhole calc changed") 680 710 def test_pinhole(self): 681 711 """ … … 686 716 theory = 12.0-1000.0*resolution.q_calc 687 717 output = resolution.apply(theory) 718 # Note: answer came from output of previous run. Non-integer 719 # values at ends come from the fact that q_calc does not 720 # extend beyond q, and so the weights don't balance. 688 721 answer = [ 689 10.4 4785079, 9.84991299, 8.98101708,690 7.99906585, 6.99998311, 6.00001689,691 5.00093415, 4.01898292, 3.15008701, 2.55214921,722 10.47037734, 9.86925860, 723 9., 8., 7., 6., 5., 4., 724 3.13074140, 2.52962266, 692 725 ] 693 726 np.testing.assert_allclose(output, answer, atol=1e-8) … … 732 765 self._compare(q, output, answer, 1e-6) 733 766 767 @unittest.skip("suppress comparison with old version; pinhole calc changed") 734 768 def test_pinhole(self): 735 769 """ … … 746 780 self._compare(q, output, answer, 3e-4) 747 781 782 @unittest.skip("suppress comparison with old version; pinhole calc changed") 748 783 def test_pinhole_romberg(self): 749 784 """ … … 761 796 # 2*np.pi/pars['radius']/200) 762 797 #tol = 0.001 763 ## The default 3sigma and no extra points gets 1%798 ## The default 2.5 sigma and no extra points gets 1% 764 799 q_calc = None # type: np.ndarray 765 800 tol = 0.01 … … 1080 1115 1081 1116 if isinstance(resolution, Slit1D): 1082 width, height = resolution. dqx, resolution.dqy1117 width, height = resolution.qx_width, resolution.qy_width 1083 1118 Iq_romb = romberg_slit_1d(resolution.q, width, height, model, pars) 1084 1119 else: -
sasmodels/rst2html.py
r2d81cfe r1fbadb2 56 56 # others don't work properly with math_output! 57 57 if math_output == "mathjax": 58 settings = {"math_output": math_output} 58 # TODO: this is copied from docs/conf.py; there should be only one 59 mathjax_path = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML" 60 settings = {"math_output": math_output + " " + mathjax_path} 59 61 else: 60 62 settings = {"math-output": math_output} -
sasmodels/sasview_model.py
r05df1de rd533590 686 686 self._intermediate_results = getattr(calculator, 'results', None) 687 687 calculator.release() 688 self._model.release()688 #self._model.release() 689 689 return result 690 690 … … 719 719 720 720 def set_dispersion(self, parameter, dispersion): 721 # type: (str, weights.Dispersion) -> Dict[str, Any]721 # type: (str, weights.Dispersion) -> None 722 722 """ 723 723 Set the dispersion object for a model parameter … … 742 742 self.dispersion[parameter] = dispersion.get_pars() 743 743 else: 744 raise ValueError("%r is not a dispersity or orientation parameter") 744 raise ValueError("%r is not a dispersity or orientation parameter" 745 % parameter) 745 746 746 747 def _dispersion_mesh(self): -
doc/guide/pd/polydispersity.rst
r29afc50 rd712a0f 20 20 P(q) = \text{scale} \langle F^* F \rangle / V + \text{background} 21 21 22 where $F$ is the scattering amplitude and $\langle\cdot\rangle$ denotes an 23 average over the size distribution. 22 where $F$ is the scattering amplitude and $\langle\cdot\rangle$ denotes an 23 average over the size distribution $f(x; \bar x, \sigma)$, giving 24 25 .. math:: 26 27 P(q) = \frac{\text{scale}}{V} \int_\mathbb{R} 28 f(x; \bar x, \sigma) F^2(q, x)\, dx + \text{background} 24 29 25 30 Each distribution is characterized by a center value $\bar x$ or … … 41 46 with larger values of $N_\sigma$ required for heavier tailed distributions. 42 47 The scattering in general falls rapidly with $qr$ so the usual assumption 43 that $ G(r - 3\sigma_r)$ is tiny and therefore $f(r - 3\sigma_r)G(r - 3\sigma_r)$48 that $f(r - 3\sigma_r)$ is tiny and therefore $f(r - 3\sigma_r)f(r - 3\sigma_r)$ 44 49 will not contribute much to the average may not hold when particles are large. 45 50 This, too, will require increasing $N_\sigma$. … … 63 68 64 69 Additional distributions are under consideration. 70 71 .. note:: In 2009 IUPAC decided to introduce the new term 'dispersity' to replace 72 the term 'polydispersity' (see `Pure Appl. Chem., (2009), 81(2), 73 351-353 <http://media.iupac.org/publications/pac/2009/pdf/8102x0351.pdf>`_ 74 in order to make the terminology describing distributions of properties 75 unambiguous. Throughout the SasView documentation we continue to use the 76 term polydispersity because one of the consequences of the IUPAC change is 77 that orientational polydispersity would not meet their new criteria (which 78 requires dispersity to be dimensionless). 65 79 66 80 Suggested Applications
Note: See TracChangeset
for help on using the changeset viewer.