Changes in / [bc1dac6:1cdfd47] in sasmodels
- Files:
-
- 1 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
.travis.yml
r5c36bf1 r335271e 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
rbefe905 r4f5afc9 39 39 40 40 .. math:: 41 -- &= ( 1-u_i)(1-u_f)\\42 -+ &= ( 1-u_i)(u_f)\\43 +- &= ( u_i)(1-u_f)\\44 ++ &= ( u_i)(u_f)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} 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
rf796469 r7e6bc45e 822 822 823 823 :code:`source = ["lib/Si.c", ...]` 824 (`Si.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/ sas_Si.c>`_)824 (`Si.c <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib/Si.c>`_) 825 825 826 826 sas_3j1x_x(x): -
sasmodels/compare.py
r1e7b202a rd86f0fc 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 profile110 109 111 110 === output options === … … 646 645 647 646 def make_data(opts): 648 # type: (Dict[str, Any] , float) -> Tuple[Data, np.ndarray]647 # type: (Dict[str, Any]) -> Tuple[Data, np.ndarray] 649 648 """ 650 649 Generate an empty dataset, used with the model to set Q points … … 668 667 if opts['zero']: 669 668 q = np.hstack((0, q)) 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) 669 data = empty_data1D(q, resolution=res) 674 670 index = slice(None, None) 675 671 return data, index … … 756 752 """ 757 753 for k in range(opts['sets']): 758 if k > 0:754 if k > 1: 759 755 # print a separate seed for each dataset for better reproducibility 760 756 new_seed = np.random.randint(1000000) 761 print(" === Set %d uses -random=%i ==="%(k+1, new_seed))757 print("Set %d uses -random=%i"%(k+1, new_seed)) 762 758 np.random.seed(new_seed) 763 759 opts['pars'] = parse_pars(opts, maxdim=maxdim) … … 766 762 result = run_models(opts, verbose=True) 767 763 if opts['plot']: 768 if opts['is2d'] and k > 0:769 import matplotlib.pyplot as plt770 plt.figure()771 764 limits = plot_models(opts, result, limits=limits, setnum=k) 772 765 if opts['show_weights']: … … 776 769 dim = base._kernel.dim 777 770 plot_weights(model_info, get_mesh(model_info, base_pars, dim=dim)) 778 if opts['show_profile']:779 import pylab780 base, comp = opts['engines']781 base_pars, comp_pars = opts['pars']782 have_base = base._kernel.info.profile is not None783 have_comp = (784 comp is not None785 and comp._kernel.info.profile is not None786 and base_pars != comp_pars787 )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()795 771 if opts['plot']: 796 772 import matplotlib.pyplot as plt … … 798 774 return limits 799 775 800 def plot_profile(model_info, label='base', **args):801 # type: (ModelInfo, List[Tuple[float, np.ndarray, np.ndarray]]) -> None802 """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 the809 distributions to be plotted.810 """811 import pylab812 813 args = dict((k, v) for k, v in args.items()814 if "_pd" not in k815 and ":" not in k816 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 829 776 def run_models(opts, verbose=False): 830 777 # type: (Dict[str, Any]) -> Dict[str, Any] … … 836 783 base_n, comp_n = opts['count'] 837 784 base_pars, comp_pars = opts['pars'] 838 base_data, comp_data = opts['data']785 data = opts['data'] 839 786 840 787 comparison = comp is not None … … 850 797 print("%s t=%.2f ms, intensity=%.0f" 851 798 % (base.engine, base_time, base_value.sum())) 852 _show_invalid( base_data, base_value)799 _show_invalid(data, base_value) 853 800 except ImportError: 854 801 traceback.print_exc() … … 862 809 print("%s t=%.2f ms, intensity=%.0f" 863 810 % (comp.engine, comp_time, comp_value.sum())) 864 _show_invalid( base_data, comp_value)811 _show_invalid(data, comp_value) 865 812 except ImportError: 866 813 traceback.print_exc() … … 916 863 have_base, have_comp = (base_value is not None), (comp_value is not None) 917 864 base, comp = opts['engines'] 918 base_data, comp_data = opts['data']865 data = opts['data'] 919 866 use_data = (opts['datafile'] is not None) and (have_base ^ have_comp) 920 867 921 868 # Plot if requested 922 869 view = opts['view'] 923 #view = 'log'924 870 if limits is None: 925 871 vmin, vmax = np.inf, -np.inf … … 935 881 if have_comp: 936 882 plt.subplot(131) 937 plot_theory( base_data, base_value, view=view, use_data=use_data, limits=limits)883 plot_theory(data, base_value, view=view, use_data=use_data, limits=limits) 938 884 plt.title("%s t=%.2f ms"%(base.engine, base_time)) 939 885 #cbar_title = "log I" … … 942 888 plt.subplot(132) 943 889 if not opts['is2d'] and have_base: 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)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) 946 892 plt.title("%s t=%.2f ms"%(comp.engine, comp_time)) 947 893 #cbar_title = "log I" … … 959 905 err[err > cutoff] = cutoff 960 906 #err,errstr = base/comp,"ratio" 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) 907 plot_theory(data, None, resid=err, view=errview, use_data=use_data) 965 908 plt.xscale('log' if view == 'log' and not opts['is2d'] else 'linear') 966 909 plt.legend(['P%d'%(k+1) for k in range(setnum+1)], loc='best') … … 996 939 OPTIONS = [ 997 940 # Plotting 998 'plot', 'noplot', 999 'weights', 'profile', 941 'plot', 'noplot', 'weights', 1000 942 'linear', 'log', 'q4', 1001 943 'rel', 'abs', … … 1130 1072 'qmax' : 0.05, 1131 1073 'nq' : 128, 1132 'res' : '0.0',1074 'res' : 0.0, 1133 1075 'noise' : 0.0, 1134 1076 'accuracy' : 'Low', … … 1151 1093 'count' : '1', 1152 1094 'show_weights' : False, 1153 'show_profile' : False,1154 1095 'sphere' : 0, 1155 1096 'ngauss' : '0', … … 1171 1112 elif arg.startswith('-q='): 1172 1113 opts['qmin'], opts['qmax'] = [float(v) for v in arg[3:].split(':')] 1173 elif arg.startswith('-res='): opts['res'] = arg[5:]1114 elif arg.startswith('-res='): opts['res'] = float(arg[5:]) 1174 1115 elif arg.startswith('-noise='): opts['noise'] = float(arg[7:]) 1175 1116 elif arg.startswith('-sets='): opts['sets'] = int(arg[6:]) … … 1212 1153 elif arg == '-default': opts['use_demo'] = False 1213 1154 elif arg == '-weights': opts['show_weights'] = True 1214 elif arg == '-profile': opts['show_profile'] = True1215 1155 elif arg == '-html': opts['html'] = True 1216 1156 elif arg == '-help': opts['html'] = True … … 1230 1170 if opts['qmin'] is None: 1231 1171 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) 1232 1176 1233 1177 comparison = any(PAR_SPLIT in v for v in values) … … 1269 1213 opts['cutoff'] = [float(opts['cutoff'])]*2 1270 1214 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], 1215 base = make_engine(model_info[0], data, opts['engine'][0], 1293 1216 opts['cutoff'][0], opts['ngauss'][0]) 1294 1217 if comparison: 1295 comp = make_engine(model_info[1], data [1], opts['engine'][1],1218 comp = make_engine(model_info[1], data, opts['engine'][1], 1296 1219 opts['cutoff'][1], opts['ngauss'][1]) 1297 1220 else: … … 1406 1329 1407 1330 # Evaluate preset parameter expressions 1408 # Note: need to replace ':' with '_' in parameter names and expressions1409 # in order to support math on magnetic parameters.1410 1331 context = MATH.copy() 1411 1332 context['np'] = np 1412 context.update( (k.replace(':', '_'), v) for k, v in pars.items())1333 context.update(pars) 1413 1334 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)1415 1335 for k, v in presets.items(): 1416 1336 if not isinstance(v, float) and not k.endswith('_type'): 1417 presets[k] = eval(v .replace(':', '_'), context)1337 presets[k] = eval(v, context) 1418 1338 context.update(presets) 1419 context.update((k .replace(':', '_'), v) for k, v in presets2.items() if isinstance(v, float))1339 context.update((k, v) for k, v in presets2.items() if isinstance(v, float)) 1420 1340 for k, v in presets2.items(): 1421 1341 if not isinstance(v, float) and not k.endswith('_type'): 1422 presets2[k] = eval(v .replace(':', '_'), context)1342 presets2[k] = eval(v, context) 1423 1343 1424 1344 # update parameters with presets … … 1450 1370 path = os.path.dirname(info.filename) 1451 1371 url = "file://" + path.replace("\\", "/")[2:] + "/" 1452 rst2html.view_html_ wxapp(html, url)1372 rst2html.view_html_qtapp(html, url) 1453 1373 1454 1374 def explore(opts): -
sasmodels/data.py
r65fbf7c rd86f0fc 36 36 37 37 import numpy as np # type: ignore 38 from numpy import sqrt, sin, cos, pi39 38 40 39 # pylint: disable=unused-import … … 302 301 303 302 304 def empty_data1D(q, resolution=0.0 , L=0., dL=0.):303 def empty_data1D(q, resolution=0.0): 305 304 # type: (np.ndarray, float) -> Data1D 306 r"""305 """ 307 306 Create empty 1D data using the given *q* as the x value. 308 307 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)$. 308 *resolution* dq/q defaults to 5%. 313 309 """ 314 310 … … 317 313 Iq, dIq = None, None 318 314 q = np.asarray(q) 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) 315 data = Data1D(q, Iq, dx=resolution * q, dy=dIq) 333 316 data.filename = "fake data" 334 317 return data -
sasmodels/details.py
r885753a r108e70e 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 245 # Pad value array to a 32 value boundaryd 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)253 252 return call_details, data, is_magnetic 254 253 … … 297 296 mag = values[parameters.nvalues-3*parameters.nmagnetic:parameters.nvalues] 298 297 mag = mag.reshape(-1, 3) 299 if np.any(mag[:, 0] != 0.0):300 M0 = mag[:, 0].copy()298 scale = mag[:, 0] 299 if np.any(scale): 301 300 theta, phi = radians(mag[:, 1]), radians(mag[:, 2]) 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 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 305 305 return True 306 306 else: -
sasmodels/jitter.py
r1198f90 rb3703f5 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 .5, 5][dims]776 limit = [0, 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
r7c35fda rd86f0fc 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 weight[6])81 static void set_spin_weights(double in_spin, double out_spin, double spins[4]) 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 // 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 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 102 91 } 103 92 104 93 // Compute the magnetic sld 105 94 static double mag_sld( 106 const unsigned int xs, // 0=dd, 1=du .real, 2=ud.real, 3=uu, 4=du.imag, 5=ud.imag95 const unsigned int xs, // 0=dd, 1=du real, 2=ud real, 3=uu, 4=du imag, 5=up imag 107 96 const double qx, const double qy, 108 97 const double px, const double py, … … 117 106 case 0: // uu => sld - D M_perpx 118 107 return sld - px*perp; 119 case 1: // ud .real => -D M_perpy108 case 1: // ud real => -D M_perpy 120 109 return py*perp; 121 case 2: // du .real => -D M_perpy110 case 2: // du real => -D M_perpy 122 111 return py*perp; 123 case 3: // dd => sld + D M_perpx112 case 3: // dd real => sld + D M_perpx 124 113 return sld + px*perp; 125 114 } 126 115 } else { 127 116 if (xs== 4) { 128 return -mz; // du.imag => +D M_perpz117 return -mz; // ud imag => -D M_perpz 129 118 } else { // index == 5 130 return +mz; // ud.imag => -D M_perpz119 return mz; // du imag => D M_perpz 131 120 } 132 121 } … … 323 312 // up_angle = values[NUM_PARS+4]; 324 313 // TODO: could precompute more magnetism parameters before calling the kernel. 325 double xs_weights[8]; // uu, ud real, du real, dd, ud imag, du imag, fill, fill314 double spins[8]; // uu, ud real, du real, dd, ud imag, du imag, fill, fill 326 315 double cos_mspin, sin_mspin; 327 set_spin_weights(values[NUM_PARS+2], values[NUM_PARS+3], xs_weights);316 set_spin_weights(values[NUM_PARS+2], values[NUM_PARS+3], spins); 328 317 SINCOS(-values[NUM_PARS+4]*M_PI_180, sin_mspin, cos_mspin); 329 318 #endif // MAGNETIC … … 672 661 // loop over uu, ud real, du real, dd, ud imag, du imag 673 662 for (unsigned int xs=0; xs<6; xs++) { 674 const double xs_weight = xs_weights[xs];663 const double xs_weight = spins[xs]; 675 664 if (xs_weight > 1.e-8) { 676 665 // Since the cross section weight is significant, set the slds … … 685 674 local_values.vector[sld_index] = 686 675 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);689 676 } 690 677 scattering += xs_weight * CALL_KERNEL(); -
sasmodels/kerneldll.py
r33969b6 rbf94e6e 123 123 # add openmp support if not running on a mac 124 124 if sys.platform != "darwin": 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 125 CC.append("-fopenmp") 129 126 def compile_command(source, output): 130 127 """unix compiler command""" -
sasmodels/models/core_shell_parallelepiped.c
rdbf1a60 re077231 59 59 60 60 // outer integral (with gauss points), integration limits = 0, 1 61 // substitute d_cos_alpha for sin_alpha d_alpha62 61 double outer_sum = 0; //initialize integral 63 62 for( int i=0; i<GAUSS_N; i++) { 64 63 const double cos_alpha = 0.5 * ( GAUSS_Z[i] + 1.0 ); 65 64 const double mu = half_q * sqrt(1.0-cos_alpha*cos_alpha); 65 66 // inner integral (with gauss points), integration limits = 0, pi/2 66 67 const double siC = length_c * sas_sinx_x(length_c * cos_alpha * half_q); 67 68 const double siCt = tC * sas_sinx_x(tC * cos_alpha * half_q); 68 69 // inner integral (with gauss points), integration limits = 0, 170 // substitute beta = PI/2 u (so 2/PI * d_(PI/2 * beta) = d_beta)71 69 double inner_sum = 0.0; 72 70 for(int j=0; j<GAUSS_N; j++) { 73 const double u= 0.5 * ( GAUSS_Z[j] + 1.0 );71 const double beta = 0.5 * ( GAUSS_Z[j] + 1.0 ); 74 72 double sin_beta, cos_beta; 75 SINCOS(M_PI_2* u, sin_beta, cos_beta);73 SINCOS(M_PI_2*beta, sin_beta, cos_beta); 76 74 const double siA = length_a * sas_sinx_x(length_a * mu * sin_beta); 77 75 const double siB = length_b * sas_sinx_x(length_b * mu * cos_beta); … … 93 91 inner_sum += GAUSS_W[j] * f * f; 94 92 } 95 // now complete change of inner integration variable (1-0)/(1-(-1))= 0.596 93 inner_sum *= 0.5; 97 94 // now sum up the outer integral 98 95 outer_sum += GAUSS_W[i] * inner_sum; 99 96 } 100 // now complete change of outer integration variable (1-0)/(1-(-1))= 0.5101 97 outer_sum *= 0.5; 102 98 -
sasmodels/models/core_shell_parallelepiped.py
rf89ec96 r97be877 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 "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. 6 The thickness and the scattering length density of the shell or 7 "rim" can be different on each (pair) of faces. 14 8 15 9 The form factor is normalized by the particle volume $V$ such that … … 17 11 .. math:: 18 12 19 I(q) = \frac{\text{scale}}{V} \langle P(q,\alpha,\beta) \rangle 20 + \text{background} 13 I(q) = \text{scale}\frac{\langle f^2 \rangle}{V} + \text{background} 21 14 22 15 where $\langle \ldots \rangle$ is an average over all possible orientations 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 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 34 23 35 24 There are rectangular "slabs" of thickness $t_A$ that add to the $A$ dimension 36 25 (on the $BC$ faces). There are similar slabs on the $AC$ $(=t_B)$ and $AB$ 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 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 47 31 48 32 .. math:: 49 33 50 34 V = ABC + 2t_ABC + 2t_BAC + 2t_CAB 35 36 **meaning that there are "gaps" at the corners of the solid.** 51 37 52 38 The intensity calculated follows the :ref:`parallelepiped` model, with the 53 39 core-shell intensity being calculated as the square of the sum of the 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) 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) 71 52 &= (\rho_\text{core}-\rho_\text{solvent}) 72 53 S(Q_A, A) S(Q_B, B) S(Q_C, C) \\ 73 54 &+ (\rho_\text{A}-\rho_\text{solvent}) 74 \left[S(Q_A, A+2t_A) - S(Q_A, A)\right] S(Q_B, B) S(Q_C, C) \\55 \left[S(Q_A, A+2t_A) - S(Q_A, Q)\right] S(Q_B, B) S(Q_C, C) \\ 75 56 &+ (\rho_\text{B}-\rho_\text{solvent}) 76 57 S(Q_A, A) \left[S(Q_B, B+2t_B) - S(Q_B, B)\right] S(Q_C, C) \\ … … 82 63 .. math:: 83 64 84 S(Q _X, L) = L \frac{\sin (\tfrac{1}{2} Q_X L)}{\tfrac{1}{2} Q_XL}65 S(Q, L) = L \frac{\sin \tfrac{1}{2} Q L}{\tfrac{1}{2} Q L} 85 66 86 67 and … … 88 69 .. math:: 89 70 90 Q_A &= q\sin\alpha \sin\beta \\91 Q_B &= q\sin\alpha \cos\beta \\92 Q_C &= q\cos\alpha71 Q_A &= \sin\alpha \sin\beta \\ 72 Q_B &= \sin\alpha \cos\beta \\ 73 Q_C &= \cos\alpha 93 74 94 75 95 76 where $\rho_\text{core}$, $\rho_\text{A}$, $\rho_\text{B}$ and $\rho_\text{C}$ 96 are the scattering length sof the parallelepiped core, and the rectangular77 are the scattering length of the parallelepiped core, and the rectangular 97 78 slabs of thickness $t_A$, $t_B$ and $t_C$, respectively. $\rho_\text{solvent}$ 98 79 is the scattering length of the solvent. 99 80 100 .. note::101 102 the code actually implements two substitutions: $d(cos\alpha)$ is103 substituted for -$sin\alpha \ d\alpha$ (note that in the104 :ref:`parallelepiped` code this is explicitly implemented with105 $\sigma = cos\alpha$), and $\beta$ is set to $\beta = u \pi/2$ so that106 $du = \pi/2 \ d\beta$. Thus both integrals go from 0 to 1 rather than 0107 to $\pi/2$.108 109 81 FITTING NOTES 110 82 ~~~~~~~~~~~~~ 111 83 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. 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. 143 109 144 110 .. figure:: img/parallelepiped_angle_definition.png 145 111 146 112 Definition of the angles for oriented core-shell parallelepipeds. 147 Note that rotation $\theta$, initially in the $x -z$ plane, is carried113 Note that rotation $\theta$, initially in the $xz$ plane, is carried 148 114 out first, then rotation $\phi$ about the $z$ axis, finally rotation 149 $\Psi$ is now around the $C$ axis of the particle. The neutron or X-ray150 beam is along the $z$ axis and the detecotr defines the $x-y$ plane.115 $\Psi$ is now around the axis of the cylinder. The neutron or X-ray 116 beam is along the $z$ axis. 151 117 152 118 .. figure:: img/parallelepiped_angle_projection.png … … 154 120 Examples of the angles for oriented core-shell parallelepipeds against the 155 121 detector plane. 156 157 158 Validation159 ----------160 161 Cross-checked against hollow rectangular prism and rectangular prism for equal162 thickness overlapping sides, and by Monte Carlo sampling of points within the163 shape for non-uniform, non-overlapping sides.164 165 122 166 123 References … … 178 135 179 136 * **Author:** NIST IGOR/DANSE **Date:** pre 2010 180 * **Converted to sasmodels by:** Miguel Gonzale z**Date:** February 26, 2016137 * **Converted to sasmodels by:** Miguel Gonzales **Date:** February 26, 2016 181 138 * **Last Modified by:** Paul Kienzle **Date:** October 17, 2017 182 * **Last Reviewed by:** Paul Butler **Date:** May 24, 2018 - documentation 183 updated 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. 184 142 """ 185 143 -
sasmodels/models/mass_surface_fractal.py
r7994359 r2d81cfe 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 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 41 $(surface\_dim + mass\_dim ) < 6$ . 42 49 43 50 44 References 51 45 ---------- 52 46 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) 47 P Schmidt, *J Appl. Cryst.*, 24 (1991) 414-435 Equation(19) 56 48 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 49 A J Hurd, D W Schaefer, J E Martin, *Phys. Rev. A*, 50 35 (1987) 2361-2364 Equation(2) 62 51 """ 63 52 … … 78 67 rg_primary = rg 79 68 background = background 69 Ref: Schmidt, J Appl Cryst, eq(19), (1991), 24, 414-435 80 70 Hurd, Schaefer, Martin, Phys Rev A, eq(2),(1987),35, 2361-2364 81 71 Note that 0 < Ds< 6 and 0 < Dm < 6. … … 88 78 ["fractal_dim_mass", "", 1.8, [0.0, 6.0], "", "Mass fractal dimension"], 89 79 ["fractal_dim_surf", "", 2.3, [0.0, 6.0], "", "Surface fractal dimension"], 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"],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"], 92 82 ] 93 83 # pylint: enable=bad-whitespace, line-too-long … … 117 107 fractal_dim_mass=1.8, 118 108 fractal_dim_surf=2.3, 119 rg_cluster= 4000.0,120 rg_primary= 86.7)109 rg_cluster=86.7, 110 rg_primary=4000.0) 121 111 122 112 tests = [ 123 113 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 2018125 [{'fractal_dim_mass': 1.8,114 # Accuracy tests based on content in test/utest_other_models.py 115 [{'fractal_dim_mass': 1.8, 126 116 'fractal_dim_surf': 2.3, 127 117 'rg_cluster': 86.7, … … 133 123 [{'fractal_dim_mass': 3.3, 134 124 'fractal_dim_surf': 1.0, 135 'rg_cluster': 4000.0,136 'rg_primary': 90.0,137 }, 0.001, 0. 0932516614456],125 'rg_cluster': 90.0, 126 'rg_primary': 4000.0, 127 }, 0.001, 0.18562699016], 138 128 139 129 [{'fractal_dim_mass': 1.3, 140 'fractal_dim_surf': 2.0,141 'rg_cluster': 2000.0,142 'rg_primary': 90.0,130 'fractal_dim_surf': 1.0, 131 'rg_cluster': 90.0, 132 'rg_primary': 2000.0, 143 133 'background': 0.8, 144 }, 0.001, 1. 28296431786],134 }, 0.001, 1.16539753641], 145 135 146 136 [{'fractal_dim_mass': 2.3, 147 'fractal_dim_surf': 3.1,148 'rg_cluster': 1000.0,149 'rg_primary': 30.0,137 'fractal_dim_surf': 1.0, 138 'rg_cluster': 90.0, 139 'rg_primary': 1000.0, 150 140 'scale': 10.0, 151 141 'background': 0.0, 152 }, 0.051, 0.00 333804044899],142 }, 0.051, 0.000169548800377], 153 143 ] -
sasmodels/models/parallelepiped.c
rdbf1a60 r108e70e 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.541 40 inner_total *= 0.5; 42 41 … … 44 43 outer_total += GAUSS_W[i] * inner_total * si * si; 45 44 } 46 // now complete change of outer integration variable (1-0)/(1-(-1))= 0.547 45 outer_total *= 0.5; 48 46 -
sasmodels/models/parallelepiped.py
rf89ec96 ref07e95 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, see 6 the :ref:`magnetism` documentation. 7 4 8 Definition 5 9 ---------- 6 10 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. 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`. 12 14 13 15 .. _parallelepiped-image: … … 19 21 20 22 The three dimensions of the parallelepiped (strictly here a cuboid) may be 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: 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: 30 29 31 30 .. Comment by Miguel Gonzalez: … … 40 39 41 40 I(q) = \frac{\text{scale}}{V} (\Delta\rho \cdot V)^2 42 \left< P(q, \alpha , \beta) \right> + \text{background}41 \left< P(q, \alpha) \right> + \text{background} 43 42 44 43 where the volume $V = A B C$, the contrast is defined as 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. 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. 51 48 52 49 Assuming $a = A/B < 1$, $b = B /B = 1$, and $c = C/B > 1$, the 53 form factor is given by (Mittelbach and Porod, 1961 [#Mittelbach]_)50 form factor is given by (Mittelbach and Porod, 1961) 54 51 55 52 .. math:: … … 69 66 \mu &= qB 70 67 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) 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) 80 139 + \text{background} 81 140 … … 89 148 with scale being the volume fraction. 90 149 91 Where $P(q_x, q_y)$ for a given orientation of the form factor is calculated as92 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]^297 \left[\frac{\sin(\tfrac{1}{2}qB\cos\beta)}{(\tfrac{1}98 {2}qB\cos\beta)}\right]^299 \left[\frac{\sin(\tfrac{1}{2}qC\cos\gamma)}{(\tfrac{1}100 {2}qC\cos\gamma)}\right]^2101 102 with103 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 q109 110 111 FITTING NOTES112 ~~~~~~~~~~~~~113 114 #. The 2nd virial coefficient of the parallelepiped is calculated based on115 the averaged effective radius, after appropriately sorting the three116 dimensions, to give an oblate or prolate particle, $(=\sqrt{AB/\pi})$ and117 length $(= C)$ values, and used as the effective radius for118 $S(q)$ when $P(q) \cdot S(q)$ is applied.119 120 #. For 2d data the orientation of the particle is required, described using121 angles $\theta$, $\phi$ and $\Psi$ as in the diagrams below, where $\theta$122 and $\phi$ define the orientation of the director in the laboratry reference123 frame of the beam direction ($z$) and detector plane ($x-y$ plane), while124 the angle $\Psi$ is effectively the rotational angle around the particle125 $C$ axis. For $\theta = 0$ and $\phi = 0$, $\Psi = 0$ corresponds to the126 $B$ axis oriented parallel to the y-axis of the detector with $A$ along127 the x-axis. For other $\theta$, $\phi$ values, the order of rotations128 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 the131 particle orientation (i.e `jitter` to one or more of these angles) can get132 more confusing as `jitter` is defined **NOT** with respect to the laboratory133 frame but the particle reference frame. It is thus highly recmmended to134 read :ref:`orientation` for further details of the calculation and angular135 dispersions.136 137 .. note:: For 2d, constraints must be applied during fitting to ensure that the138 order of sides chosen is not altered, and hence that the correct definition139 of angles is preserved. For the default choice shown here, that means140 ensuring that the inequality $A < B < C$ is not violated, The calculation141 will not report an error, but the results may be not correct.142 143 .. _parallelepiped-orientation:144 145 .. figure:: img/parallelepiped_angle_definition.png146 147 Definition of the angles for oriented parallelepiped, shown with $A<B<C$.148 149 .. figure:: img/parallelepiped_angle_projection.png150 151 Examples of the angles for an oriented parallelepiped against the152 detector plane.153 154 .. Comment by Paul Butler155 I am commenting this section out as we are trying to minimize the amount of156 oritentational detail here and encourage the user to go to the full157 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 of160 theta" and "distribution of phi" parameters will appear. These are actually161 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 third164 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 to166 understand the 2d patterns fully as discussed in :ref:`orientation` .167 168 150 169 151 Validation … … 174 156 angles. 175 157 158 176 159 References 177 160 ---------- 178 161 179 .. [#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-854162 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-854 182 165 183 166 Authorship and Verification … … 186 169 * **Author:** NIST IGOR/DANSE **Date:** pre 2010 187 170 * **Last Modified by:** Paul Kienzle **Date:** April 05, 2017 188 * **Last Reviewed by:** Miguel Gonzales and Paul Butler **Date:** May 24, 189 2018 - documentation updated 171 * **Last Reviewed by:** Richard Heenan **Date:** April 06, 2017 190 172 """ 191 173 -
sasmodels/resolution.py
r0b9c6df r2d81cfe 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 JAC23 22 24 23 class Resolution(object): … … 66 65 *q_calc* is the list of points to calculate, or None if this should 67 66 be estimated from the *q* and *q_width*. 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): 67 """ 68 def __init__(self, q, q_width, q_calc=None, nsigma=3): 73 69 #*min_step* is the minimum point spacing to use when computing the 74 70 #underlying model. It should be on the order of … … 86 82 87 83 # Protect against models which are not defined for very low q. Limit 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. 84 # the smallest q value evaluated (in absolute) to 0.02*min 92 85 cutoff = MINIMUM_ABSOLUTE_Q*np.min(self.q) 93 self.q_calc = self.q_calc[ self.q_calc>= cutoff]86 self.q_calc = self.q_calc[abs(self.q_calc) >= cutoff] 94 87 95 88 # Build weight matrix from calculated q values 96 89 self.weight_matrix = pinhole_resolution( 97 self.q_calc, self.q, np.maximum(q_width, MINIMUM_RESOLUTION) ,98 nsigma=nsigma)90 self.q_calc, self.q, np.maximum(q_width, MINIMUM_RESOLUTION)) 91 self.q_calc = abs(self.q_calc) 99 92 100 93 def apply(self, theory): … … 108 101 *q* points at which the data is measured. 109 102 110 * qx_width* slit width in qx111 112 * qy_width* slit height in qy103 *dqx* slit width in qx 104 105 *dqy* slit height in qy 113 106 114 107 *q_calc* is the list of points to calculate, or None if this should … … 161 154 162 155 163 def pinhole_resolution(q_calc, q, q_width , nsigma=PINHOLE_N_SIGMA):164 r"""156 def pinhole_resolution(q_calc, q, q_width): 157 """ 165 158 Compute the convolution matrix *W* for pinhole resolution 1-D data. 166 159 … … 169 162 *W*, the resolution smearing can be computed using *dot(W,q)*. 170 163 171 Note that resolution is limited to $\pm 2.5 \sigma$.[1] The true resolution172 function is a broadened triangle, and does not extend over the entire173 range $(-\infty, +\infty)$. It is important to impose this limitation174 since some models fall so steeply that the weighted value in gaussian175 tails would otherwise dominate the integral.176 177 164 *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 Effects180 in Radially Symmetric Small-Angle Neutron Scattering by Numerical and181 Analytical Methods. Journal of Applied Crystallography 28 (2): 105--14.182 https://doi.org/10.1107/S0021889894010095.183 165 """ 184 166 # The current algorithm is a midpoint rectangle rule. In the test case, … … 188 170 cdf = erf((edges[:, None] - q[None, :]) / (sqrt(2.0)*q_width)[None, :]) 189 171 weights = cdf[1:] - cdf[:-1] 190 # Limit q range to +/- 2.5 sigma191 qhigh = q + nsigma*q_width192 #qlow = q - nsigma*q_width # linear limits193 qlow = q*q/qhigh # log limits194 weights[q_calc[:, None] < qlow[None, :]] = 0.195 weights[q_calc[:, None] > qhigh[None, :]] = 0.196 172 weights /= np.sum(weights, axis=0)[None, :] 197 173 return weights … … 518 494 519 495 520 def gaussian(q, q0, dq , nsigma=2.5):521 """ 522 Return the truncatedGaussian resolution function.496 def gaussian(q, q0, dq): 497 """ 498 Return the Gaussian resolution function. 523 499 524 500 *q0* is the center, *dq* is the width and *q* are the points to evaluate. 525 501 """ 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) 502 return exp(-0.5*((q-q0)/dq)**2)/(sqrt(2*pi)*dq) 530 503 531 504 … … 585 558 586 559 587 def romberg_pinhole_1d(q, q_width, form, pars, nsigma= 2.5):560 def romberg_pinhole_1d(q, q_width, form, pars, nsigma=5): 588 561 """ 589 562 Romberg integration for pinhole resolution. … … 705 678 np.testing.assert_equal(output, self.y) 706 679 707 # TODO: turn pinhole/slit demos into tests708 709 @unittest.skip("suppress comparison with old version; pinhole calc changed")710 680 def test_pinhole(self): 711 681 """ … … 716 686 theory = 12.0-1000.0*resolution.q_calc 717 687 output = resolution.apply(theory) 718 # Note: answer came from output of previous run. Non-integer719 # values at ends come from the fact that q_calc does not720 # extend beyond q, and so the weights don't balance.721 688 answer = [ 722 10.4 7037734, 9.86925860,723 9., 8., 7., 6., 5., 4.,724 3.13074140, 2.52962266,689 10.44785079, 9.84991299, 8.98101708, 690 7.99906585, 6.99998311, 6.00001689, 691 5.00093415, 4.01898292, 3.15008701, 2.55214921, 725 692 ] 726 693 np.testing.assert_allclose(output, answer, atol=1e-8) … … 765 732 self._compare(q, output, answer, 1e-6) 766 733 767 @unittest.skip("suppress comparison with old version; pinhole calc changed")768 734 def test_pinhole(self): 769 735 """ … … 780 746 self._compare(q, output, answer, 3e-4) 781 747 782 @unittest.skip("suppress comparison with old version; pinhole calc changed")783 748 def test_pinhole_romberg(self): 784 749 """ … … 796 761 # 2*np.pi/pars['radius']/200) 797 762 #tol = 0.001 798 ## The default 2.5sigma and no extra points gets 1%763 ## The default 3 sigma and no extra points gets 1% 799 764 q_calc = None # type: np.ndarray 800 765 tol = 0.01 … … 1115 1080 1116 1081 if isinstance(resolution, Slit1D): 1117 width, height = resolution. qx_width, resolution.qy_width1082 width, height = resolution.dqx, resolution.dqy 1118 1083 Iq_romb = romberg_slit_1d(resolution.q, width, height, model, pars) 1119 1084 else: -
sasmodels/rst2html.py
r1fbadb2 r2d81cfe 56 56 # others don't work properly with math_output! 57 57 if math_output == "mathjax": 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} 58 settings = {"math_output": math_output} 61 59 else: 62 60 settings = {"math-output": math_output} -
sasmodels/sasview_model.py
rd533590 r05df1de 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) -> None721 # type: (str, weights.Dispersion) -> Dict[str, Any] 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" 745 % parameter) 744 raise ValueError("%r is not a dispersity or orientation parameter") 746 745 747 746 def _dispersion_mesh(self):
Note: See TracChangeset
for help on using the changeset viewer.