Changeset 7e923c2 in sasmodels
- Timestamp:
- Aug 7, 2018 10:45:52 PM (6 years ago)
- Branches:
- master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- 1a7ddc9
- Parents:
- c036ddb (diff), e9b17b18 (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:
-
- 46 added
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/guide/gpu_setup.rst
r59485a4 r63602b1 139 139 the compiler. 140 140 141 On Windows, set *SAS COMPILER=tinycc* for the tinycc compiler,142 *SAS COMPILER=msvc* for the Microsoft Visual C compiler,143 or *SAS COMPILER=mingw* for the MinGW compiler. If TinyCC is available141 On Windows, set *SAS_COMPILER=tinycc* for the tinycc compiler, 142 *SAS_COMPILER=msvc* for the Microsoft Visual C compiler, 143 or *SAS_COMPILER=mingw* for the MinGW compiler. If TinyCC is available 144 144 on the python path (it is provided with SasView), that will be the 145 145 default. If you want one of the other compilers, be sure to have it -
example/model_ellipsoid_hayter_msa.py
r8a5f021 r9a99993 16 16 17 17 # DEFINE THE MODEL 18 kernel = load_model('ellipsoid *hayter_msa')18 kernel = load_model('ellipsoid@hayter_msa') 19 19 20 20 pars = dict(scale=6.4, background=0.06, sld=0.33, sld_solvent=2.15, radius_polar=14.0, -
sasmodels/compare.py
r1e7b202a raf7a97c 112 112 -edit starts the parameter explorer 113 113 -help/-html shows the model docs instead of running the model 114 115 === environment variables === 116 -DSAS_MODELPATH=path sets directory containing custom models 117 -DSAS_OPENCL=vendor:device|none sets the target OpenCL device 118 -DXDG_CACHE_HOME=~/.cache sets the pyopencl cache root (linux only) 119 -DSAS_COMPILER=tinycc|msvc|mingw|unix sets the DLL compiler 120 -DSAS_OPENMP=1 turns on OpenMP for the DLLs 121 -DSAS_DLL_PATH=path sets the path to the compiled modules 114 122 115 123 The interpretation of quad precision depends on architecture, and may … … 1113 1121 1114 1122 invalid = [o[1:] for o in flags 1115 if o[1:] not in NAME_OPTIONS 1116 and not any(o.startswith('-%s='%t) for t in VALUE_OPTIONS)] 1123 if not (o[1:] in NAME_OPTIONS 1124 or any(o.startswith('-%s='%t) for t in VALUE_OPTIONS) 1125 or o.startswith('-D'))] 1117 1126 if invalid: 1118 1127 print("Invalid options: %s"%(", ".join(invalid))) … … 1215 1224 elif arg == '-html': opts['html'] = True 1216 1225 elif arg == '-help': opts['html'] = True 1226 elif arg.startswith('-D'): 1227 var, val = arg[2:].split('=') 1228 os.environ[var] = val 1217 1229 # pylint: enable=bad-whitespace,C0321 1218 1230 -
sasmodels/core.py
rc036ddb r7e923c2 37 37 if CUSTOM_MODEL_PATH == "": 38 38 CUSTOM_MODEL_PATH = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 39 if not os.path.isdir(CUSTOM_MODEL_PATH):40 os.makedirs(CUSTOM_MODEL_PATH)39 #if not os.path.isdir(CUSTOM_MODEL_PATH): 40 # os.makedirs(CUSTOM_MODEL_PATH) 41 41 42 42 # TODO: refactor composite model support … … 232 232 if not callable(model_info.Iq): 233 233 source = generate.make_source(model_info)['dll'] 234 old_path = kerneldll. DLL_PATH234 old_path = kerneldll.SAS_DLL_PATH 235 235 try: 236 kerneldll. DLL_PATH = path236 kerneldll.SAS_DLL_PATH = path 237 237 dll = kerneldll.make_dll(source, model_info, dtype=numpy_dtype) 238 238 finally: 239 kerneldll. DLL_PATH = old_path239 kerneldll.SAS_DLL_PATH = old_path 240 240 compiled_dlls.append(dll) 241 241 return compiled_dlls -
sasmodels/data.py
rc036ddb r7e923c2 502 502 # Note: masks merge, so any masked theory points will stay masked, 503 503 # and the data mask will be added to it. 504 mtheory = masked_array(theory, data.mask.copy()) 504 #mtheory = masked_array(theory, data.mask.copy()) 505 theory_x = data.x[data.mask == 0] 506 mtheory = masked_array(theory) 505 507 mtheory[~np.isfinite(mtheory)] = masked 506 508 if view is 'log': 507 509 mtheory[mtheory <= 0] = masked 508 plt.plot( data.x, scale*mtheory, '-')510 plt.plot(theory_x, scale*mtheory, '-') 509 511 all_positive = all_positive and (mtheory > 0).all() 510 512 some_present = some_present or (mtheory.count() > 0) … … 542 544 543 545 if use_resid: 544 mresid = masked_array(resid, data.mask.copy()) 546 theory_x = data.x[data.mask == 0] 547 mresid = masked_array(resid) 545 548 mresid[~np.isfinite(mresid)] = masked 546 549 some_present = (mresid.count() > 0) … … 548 551 if num_plots > 1: 549 552 plt.subplot(1, num_plots, use_calc + 2) 550 plt.plot( data.x, mresid, '.')553 plt.plot(theory_x, mresid, '.') 551 554 plt.xlabel("$q$/A$^{-1}$") 552 555 plt.ylabel('residuals') -
sasmodels/direct_model.py
rd18d6dd r1a8c11c 250 250 qmax = getattr(data, 'qmax', np.inf) 251 251 accuracy = getattr(data, 'accuracy', 'Low') 252 index = ~data.mask& (q >= qmin) & (q <= qmax)252 index = (data.mask == 0) & (q >= qmin) & (q <= qmax) 253 253 if data.data is not None: 254 254 index &= ~np.isnan(data.data) … … 263 263 elif self.data_type == 'Iq': 264 264 index = (data.x >= data.qmin) & (data.x <= data.qmax) 265 mask = getattr(data, 'mask', None) 266 if mask is not None: 267 index &= (mask == 0) 265 268 if data.y is not None: 266 269 index &= ~np.isnan(data.y) -
sasmodels/kerneldll.py
rc036ddb r7e923c2 99 99 pass 100 100 # pylint: enable=unused-import 101 102 if "SAS_DLL_PATH" in os.environ: 103 SAS_DLL_PATH = os.environ["SAS_DLL_PATH"] 104 else: 105 # Assume the default location of module DLLs is in .sasmodels/compiled_models. 106 SAS_DLL_PATH = os.path.join(os.path.expanduser("~"), ".sasmodels", "compiled_models") 101 107 102 108 if "SAS_COMPILER" in os.environ: … … 161 167 return CC + [source, "-o", output, "-lm"] 162 168 163 # Assume the default location of module DLLs is in .sasmodels/compiled_models.164 DLL_PATH = os.path.join(os.path.expanduser("~"), ".sasmodels", "compiled_models")165 166 169 ALLOW_SINGLE_PRECISION_DLLS = True 167 170 … … 200 203 return path 201 204 202 return joinpath( DLL_PATH, basename)205 return joinpath(SAS_DLL_PATH, basename) 203 206 204 207 … … 209 212 exist yet if it hasn't been compiled. 210 213 """ 211 return os.path.join( DLL_PATH, dll_name(model_info, dtype))214 return os.path.join(SAS_DLL_PATH, dll_name(model_info, dtype)) 212 215 213 216 … … 228 231 models are not allowed as DLLs. 229 232 230 Set *sasmodels.kerneldll.DLL_PATH* to the compiled dll output path. 233 Set *sasmodels.kerneldll.SAS_DLL_PATH* to the compiled dll output path. 234 Alternatively, set the environment variable *SAS_DLL_PATH*. 231 235 The default is in ~/.sasmodels/compiled_models. 232 236 """ … … 247 251 if need_recompile: 248 252 # Make sure the DLL path exists 249 if not os.path.exists( DLL_PATH):250 os.makedirs( DLL_PATH)253 if not os.path.exists(SAS_DLL_PATH): 254 os.makedirs(SAS_DLL_PATH) 251 255 basename = splitext(os.path.basename(dll))[0] + "_" 252 256 system_fd, filename = tempfile.mkstemp(suffix=".c", prefix=basename) -
sasmodels/model_test.py
- Property mode changed from 100644 to 100755
r3221de0 r012cd34 376 376 stream.writeln(traceback.format_exc()) 377 377 return 378 # Run the test suite379 suite.run(result)380 381 # Print the failures and errors382 for _, tb in result.errors:383 stream.writeln(tb)384 for _, tb in result.failures:385 stream.writeln(tb)386 378 387 379 # Warn if there are no user defined tests. … … 393 385 # iterator since we don't have direct access to the list of tests in the 394 386 # test suite. 387 # In Qt5 suite.run() will clear all tests in the suite after running 388 # with no way of retaining them for the test below, so let's check 389 # for user tests before running the suite. 395 390 for test in suite: 396 391 if not test.info.tests: … … 399 394 else: 400 395 stream.writeln("Note: no test suite created --- this should never happen") 396 397 # Run the test suite 398 suite.run(result) 399 400 # Print the failures and errors 401 for _, tb in result.errors: 402 stream.writeln(tb) 403 for _, tb in result.failures: 404 stream.writeln(tb) 401 405 402 406 output = stream.getvalue() -
sasmodels/modelinfo.py
rc036ddb r7e923c2 585 585 Parameter('up:frac_f', '', 0., [0., 1.], 586 586 'magnetic', 'fraction of spin up final'), 587 Parameter('up:angle', 'degre ss', 0., [0., 360.],587 Parameter('up:angle', 'degrees', 0., [0., 360.], 588 588 'magnetic', 'spin up angle'), 589 589 ]) -
sasmodels/models/guinier.py
r2d81cfe rc9fc873 7 7 .. math:: 8 8 9 I(q) = \text{scale} \cdot \exp{\left[ \frac{-Q^2 R_g^2}{3} \right]}9 I(q) = \text{scale} \cdot \exp{\left[ \frac{-Q^2 R_g^2 }{3} \right]} 10 10 + \text{background} 11 11 … … 19 19 20 20 .. math:: q = \sqrt{q_x^2 + q_y^2} 21 22 In scattering, the radius of gyration $R_g$ quantifies the objects's 23 distribution of SLD (not mass density, as in mechanics) from the objects's 24 SLD centre of mass. It is defined by 25 26 .. math:: R_g^2 = \frac{\sum_i\rho_i\left(r_i-r_0\right)^2}{\sum_i\rho_i} 27 28 where $r_0$ denotes the object's SLD centre of mass and $\rho_i$ is the SLD at 29 a point $i$. 30 31 Notice that $R_g^2$ may be negative (since SLD can be negative), which happens 32 when a form factor $P(Q)$ is increasing with $Q$ rather than decreasing. This 33 can occur for core/shell particles, hollow particles, or for composite 34 particles with domains of different SLDs in a solvent with an SLD close to the 35 average match point. (Alternatively, this might be regarded as there being an 36 internal inter-domain "structure factor" within a single particle which gives 37 rise to a peak in the scattering). 38 39 To specify a negative value of $R_g^2$ in SasView, simply give $R_g$ a negative 40 value ($R_g^2$ will be evaluated as $R_g |R_g|$). Note that the physical radius 41 of gyration, of the exterior of the particle, will still be large and positive. 42 It is only the apparent size from the small $Q$ data that will give a small or 43 negative value of $R_g^2$. 21 44 22 45 References … … 42 65 43 66 # ["name", "units", default, [lower, upper], "type","description"], 44 parameters = [["rg", "Ang", 60.0, [ 0, inf], "", "Radius of Gyration"]]67 parameters = [["rg", "Ang", 60.0, [-inf, inf], "", "Radius of Gyration"]] 45 68 46 69 Iq = """ 47 double exponent = rg*rg*q*q/3.0;70 double exponent = fabs(rg)*rg*q*q/3.0; 48 71 double value = exp(-exponent); 49 72 return value; … … 66 89 67 90 # parameters for demo 68 demo = dict(scale=1.0, rg=60.0)91 demo = dict(scale=1.0, background=0.001, rg=60.0 ) 69 92 70 93 # parameters for unit tests -
sasmodels/resolution.py
r0b9c6df r9e7837a 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 # According to (Barker & Pedersen 1995 JAC), 2.5 sigma is a good limit. 23 # According to simulations with github.com:scattering/sansresolution.git 24 # it is better to use asymmetric bounds (2.5, 3.0) 25 PINHOLE_N_SIGMA = (2.5, 3.0) 23 26 24 27 class Resolution(object): … … 90 93 # from the geometry, they may appear since we are using a truncated 91 94 # gaussian to represent resolution rather than a skew distribution. 92 cutoff = MINIMUM_ABSOLUTE_Q*np.min(self.q)93 self.q_calc = self.q_calc[self.q_calc >= cutoff]95 #cutoff = MINIMUM_ABSOLUTE_Q*np.min(self.q) 96 #self.q_calc = self.q_calc[self.q_calc >= cutoff] 94 97 95 98 # Build weight matrix from calculated q values … … 188 191 cdf = erf((edges[:, None] - q[None, :]) / (sqrt(2.0)*q_width)[None, :]) 189 192 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 193 # Limit q range to (-2.5,+3) sigma 194 try: 195 nsigma_low, nsigma_high = nsigma 196 except TypeError: 197 nsigma_low = nsigma_high = nsigma 198 qhigh = q + nsigma_high*q_width 199 qlow = q - nsigma_low*q_width # linear limits 200 ##qlow = q*q/qhigh # log limits 194 201 weights[q_calc[:, None] < qlow[None, :]] = 0. 195 202 weights[q_calc[:, None] > qhigh[None, :]] = 0. … … 365 372 366 373 367 def pinhole_extend_q(q, q_width, nsigma= 3):374 def pinhole_extend_q(q, q_width, nsigma=PINHOLE_N_SIGMA): 368 375 """ 369 376 Given *q* and *q_width*, find a set of sampling points *q_calc* so … … 371 378 function. 372 379 """ 373 q_min, q_max = np.min(q - nsigma*q_width), np.max(q + nsigma*q_width) 380 try: 381 nsigma_low, nsigma_high = nsigma 382 except TypeError: 383 nsigma_low = nsigma_high = nsigma 384 q_min, q_max = np.min(q - nsigma_low*q_width), np.max(q + nsigma_high*q_width) 374 385 return linear_extrapolation(q, q_min, q_max) 375 386 -
sasmodels/generate.py
rd86f0fc rc036ddb 671 671 672 672 673 # type in IQXY pattern could be single, float, double, long double, ... 674 _IQXY_PATTERN = re.compile(r"(^|\s)double\s+I(?P<mode>q(ab?c|xy))\s*[(]", 673 _IQXY_PATTERN = re.compile(r"(^|\s)double\s+I(?P<mode>q(ac|abc|xy))\s*[(]", 675 674 flags=re.MULTILINE) 676 675 def find_xy_mode(source): … … 701 700 return 'qa' 702 701 702 # Note: not presently used. Need to know whether Fq is available before 703 # trying to compile the source. Leave the code here in case we decide that 704 # define have_Fq for each form factor is too tedious and error prone. 705 _FQ_PATTERN = re.compile(r"(^|\s)void\s+Fq[(]", flags=re.MULTILINE) 706 def contains_Fq(source): 707 # type: (List[str]) -> bool 708 """ 709 Return True if C source defines "void Fq(". 710 """ 711 for code in source: 712 m = _FQ_PATTERN.search(code) 713 if m is not None: 714 return True 715 return False 703 716 704 717 def _add_source(source, code, path, lineno=1): … … 730 743 # dispersion. Need to be careful that necessary parameters are available 731 744 # for computing volume even if we allow non-disperse volume parameters. 732 733 745 partable = model_info.parameters 734 746 … … 743 755 for path, code in user_code: 744 756 _add_source(source, code, path) 745 746 757 if model_info.c_code: 747 758 _add_source(source, model_info.c_code, model_info.filename, … … 751 762 q, qx, qy, qab, qa, qb, qc \ 752 763 = [Parameter(name=v) for v in 'q qx qy qab qa qb qc'.split()] 764 753 765 # Generate form_volume function, etc. from body only 754 766 if isinstance(model_info.form_volume, str): … … 789 801 source.append("\\\n".join(p.as_definition() 790 802 for p in partable.kernel_parameters)) 791 792 803 # Define the function calls 793 804 if partable.form_volume_parameters: … … 800 811 call_volume = "#define CALL_VOLUME(v) 1.0" 801 812 source.append(call_volume) 802 803 813 model_refs = _call_pars("_v.", partable.iq_parameters) 804 pars = ",".join(["_q"] + model_refs) 805 call_iq = "#define CALL_IQ(_q, _v) Iq(%s)" % pars 814 815 if model_info.have_Fq: 816 pars = ",".join(["_q", "&_F1", "&_F2",] + model_refs) 817 call_iq = "#define CALL_FQ(_q, _F1, _F2, _v) Fq(%s)" % pars 818 clear_iq = "#undef CALL_FQ" 819 else: 820 pars = ",".join(["_q"] + model_refs) 821 call_iq = "#define CALL_IQ(_q, _v) Iq(%s)" % pars 822 clear_iq = "#undef CALL_IQ" 806 823 if xy_mode == 'qabc': 807 824 pars = ",".join(["_qa", "_qb", "_qc"] + model_refs) … … 812 829 call_iqxy = "#define CALL_IQ_AC(_qa,_qc,_v) Iqac(%s)" % pars 813 830 clear_iqxy = "#undef CALL_IQ_AC" 814 elif xy_mode == 'qa' :831 elif xy_mode == 'qa' and not model_info.have_Fq: 815 832 pars = ",".join(["_qa"] + model_refs) 816 833 call_iqxy = "#define CALL_IQ_A(_qa,_v) Iq(%s)" % pars 817 834 clear_iqxy = "#undef CALL_IQ_A" 835 elif xy_mode == 'qa' and model_info.have_Fq: 836 pars = ",".join(["_qa", "&_F1", "&_F2",] + model_refs) 837 # Note: uses rare C construction (expr1, expr2) which computes 838 # expr1 then expr2 and evaluates to expr2. This allows us to 839 # leave it looking like a function even though it is returning 840 # its values by reference. 841 call_iqxy = "#define CALL_FQ_A(_qa,_F1,_F2,_v) (Fq(%s),_F2)" % pars 842 clear_iqxy = "#undef CALL_FQ_A" 818 843 elif xy_mode == 'qxy': 819 844 orientation_refs = _call_pars("_v.", partable.orientation_parameters) … … 831 856 magpars = [k-2 for k, p in enumerate(partable.call_parameters) 832 857 if p.type == 'sld'] 833 834 858 # Fill in definitions for numbers of parameters 835 859 source.append("#define MAX_PD %s"%partable.max_pd) … … 839 863 source.append("#define MAGNETIC_PARS %s"%",".join(str(k) for k in magpars)) 840 864 source.append("#define PROJECTION %d"%PROJECTION) 841 842 865 # TODO: allow mixed python/opencl kernels? 843 844 ocl = _kernels(kernel_code, call_iq, call_iqxy, clear_iqxy, model_info.name)845 dll = _kernels(kernel_code, call_iq, call_iqxy, clear_iqxy, model_info.name) 866 ocl = _kernels(kernel_code, call_iq, clear_iq, call_iqxy, clear_iqxy, model_info.name) 867 dll = _kernels(kernel_code, call_iq, clear_iq, call_iqxy, clear_iqxy, model_info.name) 868 846 869 result = { 847 870 'dll': '\n'.join(source+dll[0]+dll[1]+dll[2]), 848 871 'opencl': '\n'.join(source+ocl[0]+ocl[1]+ocl[2]), 849 872 } 850 851 873 return result 852 874 853 875 854 def _kernels(kernel, call_iq, c all_iqxy, clear_iqxy, name):876 def _kernels(kernel, call_iq, clear_iq, call_iqxy, clear_iqxy, name): 855 877 # type: ([str,str], str, str, str) -> List[str] 856 878 code = kernel[0] … … 862 884 '#line 1 "%s Iq"' % path, 863 885 code, 864 "#undef CALL_IQ",886 clear_iq, 865 887 "#undef KERNEL_NAME", 866 888 ] -
sasmodels/kernel_iq.c
r7c35fda rc036ddb 29 29 // CALL_IQ(q, table) : call the Iq function for 1D calcs. 30 30 // CALL_IQ_A(q, table) : call the Iq function with |q| for 2D data. 31 // CALL_FQ(q, F1, F2, table) : call the Fq function for 1D calcs. 32 // CALL_FQ_A(q, F1, F2, table) : call the Iq function with |q| for 2D data. 31 33 // CALL_IQ_AC(qa, qc, table) : call the Iqxy function for symmetric shapes 32 34 // CALL_IQ_ABC(qa, qc, table) : call the Iqxy function for asymmetric shapes … … 36 38 // PROJECTION : equirectangular=1, sinusoidal=2 37 39 // see explore/jitter.py for definitions. 40 38 41 39 42 #ifndef _PAR_BLOCK_ // protected block so we can include this code twice. … … 84 87 out_spin = clip(out_spin, 0.0, 1.0); 85 88 // Previous version of this function took the square root of the weights, 86 // under the assumption that 89 // under the assumption that 87 90 // 88 91 // w*I(q, rho1, rho2, ...) = I(q, sqrt(w)*rho1, sqrt(w)*rho2, ...) … … 270 273 #endif // _QABC_SECTION 271 274 272 273 275 // ==================== KERNEL CODE ======================== 274 276 #define COMPUTE_F1_F2 defined(CALL_FQ) 275 277 kernel 276 278 void KERNEL_NAME( … … 341 343 // version must loop over all q. 342 344 #ifdef USE_OPENCL 343 double pd_norm = (pd_start == 0 ? 0.0 : result[nq]); 344 double this_result = (pd_start == 0 ? 0.0 : result[q_index]); 345 #if COMPUTE_F1_F2 346 double pd_norm = (pd_start == 0 ? 0.0 : result[nq]); 347 double weight_norm = (pd_start == 0 ? 0.0 : result[2*nq+1]); 348 double this_F2 = (pd_start == 0 ? 0.0 : result[q_index]); 349 double this_F1 = (pd_start == 0 ? 0.0 : result[q_index+nq+1]); 350 #else 351 double pd_norm = (pd_start == 0 ? 0.0 : result[nq]); 352 double this_result = (pd_start == 0 ? 0.0 : result[q_index]); 353 #endif 345 354 #else // !USE_OPENCL 346 double pd_norm = (pd_start == 0 ? 0.0 : result[nq]); 355 #if COMPUTE_F1_F2 356 double pd_norm = (pd_start == 0 ? 0.0 : result[nq]); 357 double weight_norm = (pd_start == 0 ? 0.0 : result[2*nq+1]); 358 #else 359 double pd_norm = (pd_start == 0 ? 0.0 : result[nq]); 360 #endif 347 361 if (pd_start == 0) { 348 362 #ifdef USE_OPENMP 349 363 #pragma omp parallel for 350 364 #endif 351 for (int q_index=0; q_index < nq; q_index++) result[q_index] = 0.0; 365 #if COMPUTE_F1_F2 366 for (int q_index=0; q_index < 2*nq+2; q_index++) result[q_index] = 0.0; 367 #else 368 for (int q_index=0; q_index < nq; q_index++) result[q_index] = 0.0; 369 #endif 352 370 } 353 371 //if (q_index==0) printf("start %d %g %g\n", pd_start, pd_norm, result[0]); … … 442 460 // inner loop and defines the macros that use them. 443 461 444 #if defined(CALL_IQ) 445 // unoriented 1D 462 463 #if defined(CALL_FQ) // COMPUTE_F1_F2 is true 464 // unoriented 1D returning <F> and <F^2> 465 double qk; 466 double F1, F2; 467 #define FETCH_Q() do { qk = q[q_index]; } while (0) 468 #define BUILD_ROTATION() do {} while(0) 469 #define APPLY_ROTATION() do {} while(0) 470 #define CALL_KERNEL() CALL_FQ(qk,F1,F2,local_values.table) 471 472 #elif defined(CALL_FQ_A) 473 // unoriented 2D return <F> and <F^2> 474 double qx, qy; 475 double F1, F2; 476 #define FETCH_Q() do { qx = q[2*q_index]; qy = q[2*q_index+1]; } while (0) 477 #define BUILD_ROTATION() do {} while(0) 478 #define APPLY_ROTATION() do {} while(0) 479 #define CALL_KERNEL() CALL_FQ_A(sqrt(qx*qx+qy*qy),F1,F2,local_values.table) 480 481 #elif defined(CALL_IQ) 482 // unoriented 1D return <F^2> 446 483 double qk; 447 484 #define FETCH_Q() do { qk = q[q_index]; } while (0) 448 485 #define BUILD_ROTATION() do {} while(0) 449 486 #define APPLY_ROTATION() do {} while(0) 450 #define CALL_KERNEL() CALL_IQ(qk, 487 #define CALL_KERNEL() CALL_IQ(qk,local_values.table) 451 488 452 489 #elif defined(CALL_IQ_A) … … 482 519 #define APPLY_ROTATION() qabc_apply(&rotation, qx, qy, &qa, &qb, &qc) 483 520 #define CALL_KERNEL() CALL_IQ_ABC(qa, qb, qc, local_values.table) 521 484 522 #elif defined(CALL_IQ_XY) 485 523 // direct call to qx,qy calculator … … 495 533 // logic in the IQ_AC and IQ_ABC branches. This will become more important 496 534 // if we implement more projections, or more complicated projections. 497 #if defined(CALL_IQ) || defined(CALL_IQ_A) // no orientation 535 #if defined(CALL_IQ) || defined(CALL_IQ_A) || defined(CALL_FQ) || defined(CALL_FQ_A) 536 // no orientation 498 537 #define APPLY_PROJECTION() const double weight=weight0 499 538 #elif defined(CALL_IQ_XY) // pass orientation to the model … … 646 685 if (weight > cutoff) { 647 686 pd_norm += weight * CALL_VOLUME(local_values.table); 687 #if COMPUTE_F1_F2 688 weight_norm += weight; 689 #endif 648 690 BUILD_ROTATION(); 649 691 … … 693 735 } 694 736 #else // !MAGNETIC 695 const double scattering = CALL_KERNEL(); 737 #if COMPUTE_F1_F2 738 CALL_KERNEL(); // sets F1 and F2 by reference 739 #else 740 const double scattering = CALL_KERNEL(); 741 #endif 696 742 #endif // !MAGNETIC 697 743 //printf("q_index:%d %g %g %g %g\n", q_index, scattering, weight0); 698 744 699 745 #ifdef USE_OPENCL 700 this_result += weight * scattering; 746 #if COMPUTE_F1_F2 747 this_F1 += weight * F1; 748 this_F2 += weight * F2; 749 #else 750 this_result += weight * scattering; 751 #endif 701 752 #else // !USE_OPENCL 702 result[q_index] += weight * scattering; 753 #if COMPUTE_F1_F2 754 result[q_index] += weight * F2; 755 result[q_index+nq+1] += weight * F1; 756 #else 757 result[q_index] += weight * scattering; 758 #endif 703 759 #endif // !USE_OPENCL 704 760 } 705 761 } 706 762 } 707 708 763 // close nested loops 709 764 ++step; … … 726 781 // Remember the current result and the updated norm. 727 782 #ifdef USE_OPENCL 728 result[q_index] = this_result; 729 if (q_index == 0) result[nq] = pd_norm; 783 #if COMPUTE_F1_F2 784 result[q_index] = this_F2; 785 result[q_index+nq+1] = this_F1; 786 if (q_index == 0) { 787 result[nq] = pd_norm; 788 result[2*nq+1] = weight_norm; 789 } 790 #else 791 result[q_index] = this_result; 792 if (q_index == 0) result[nq] = pd_norm; 793 #endif 794 730 795 //if (q_index == 0) printf("res: %g/%g\n", result[0], pd_norm); 731 796 #else // !USE_OPENCL 732 result[nq] = pd_norm; 797 #if COMPUTE_F1_F2 798 result[nq] = pd_norm; 799 result[2*nq+1] = weight_norm; 800 #else 801 result[nq] = pd_norm; 802 #endif 733 803 //printf("res: %g/%g\n", result[0], pd_norm); 734 804 #endif // !USE_OPENCL 735 805 736 806 // ** clear the macros in preparation for the next kernel ** 807 #undef COMPUTE_F1_F2 737 808 #undef PD_INIT 738 809 #undef PD_OPEN -
sasmodels/kernelcl.py
rd86f0fc rc036ddb 538 538 self.dtype = dtype 539 539 self.dim = '2d' if q_input.is_2d else '1d' 540 # plus three for the normalization values 541 self.result = np.empty(q_input.nq+1, dtype) 540 # leave room for f1/f2 results in case we need to compute beta for 1d models 541 num_returns = 1 if self.dim == '2d' else 2 # 542 # plus 1 for the normalization value 543 self.result = np.empty((q_input.nq+1)*num_returns, dtype) 542 544 543 545 # Inputs and outputs for each kernel call … … 547 549 548 550 self.result_b = cl.Buffer(self.queue.context, mf.READ_WRITE, 549 q_input.global_size[0] * dtype.itemsize)551 q_input.global_size[0] * num_returns * dtype.itemsize) 550 552 self.q_input = q_input # allocated by GpuInput above 551 553 … … 556 558 else np.float32) # will never get here, so use np.float32 557 559 558 def __call__(self, call_details, values, cutoff, magnetic): 560 def Iq(self, call_details, values, cutoff, magnetic): 561 # type: (CallDetails, np.ndarray, np.ndarray, float, bool) -> np.ndarray 562 self._call_kernel(call_details, values, cutoff, magnetic) 563 #print("returned",self.q_input.q, self.result) 564 pd_norm = self.result[self.q_input.nq] 565 scale = values[0]/(pd_norm if pd_norm != 0.0 else 1.0) 566 background = values[1] 567 #print("scale",scale,background) 568 return scale*self.result[:self.q_input.nq] + background 569 __call__ = Iq 570 571 def beta(self, call_details, values, cutoff, magnetic): 572 # type: (CallDetails, np.ndarray, np.ndarray, float, bool) -> np.ndarray 573 if self.dim == '2d': 574 raise NotImplementedError("beta not yet supported for 2D") 575 self._call_kernel(call_details, values, cutoff, magnetic) 576 w_norm = self.result[2*self.q_input.nq + 1] 577 pd_norm = self.result[self.q_input.nq] 578 if w_norm == 0.: 579 w_norm = 1. 580 F2 = self.result[:self.q_input.nq]/w_norm 581 F1 = self.result[self.q_input.nq+1:2*self.q_input.nq+1]/w_norm 582 volume_avg = pd_norm/w_norm 583 return F1, F2, volume_avg 584 585 def _call_kernel(self, call_details, values, cutoff, magnetic): 559 586 # type: (CallDetails, np.ndarray, np.ndarray, float, bool) -> np.ndarray 560 587 context = self.queue.context … … 573 600 #print("Calling OpenCL") 574 601 #call_details.show(values) 575 # 602 #Call kernel and retrieve results 576 603 wait_for = None 577 604 last_nap = time.clock() … … 598 625 v.release() 599 626 600 pd_norm = self.result[self.q_input.nq]601 scale = values[0]/(pd_norm if pd_norm != 0.0 else 1.0)602 background = values[1]603 #print("scale",scale,values[0],self.result[self.q_input.nq],background)604 return scale*self.result[:self.q_input.nq] + background605 # return self.result[:self.q_input.nq]606 607 627 def release(self): 608 628 # type: () -> None -
sasmodels/models/core_shell_sphere.py
rdc76240 rc036ddb 58 58 title = "Form factor for a monodisperse spherical particle with particle with a core-shell structure." 59 59 description = """ 60 F ^2(q) = 3/V_s [V_c (sld_core-sld_shell)(sin(q*radius)-q*radius*cos(q*radius))/(q*radius)^361 + V_s (sld_shell-sld_solvent)(sin(q*r_s)-q*r_s*cos(q*r_s))/(q*r_s)^3]60 F(q) = [V_c (sld_core-sld_shell) 3 (sin(q*radius)-q*radius*cos(q*radius))/(q*radius)^3 61 + V_s (sld_shell-sld_solvent) 3 (sin(q*r_s)-q*r_s*cos(q*r_s))/(q*r_s)^3] 62 62 63 63 V_s: Volume of the sphere shell -
sasmodels/models/ellipsoid.c
r108e70e rc036ddb 5 5 } 6 6 7 /* Fq overrides Iq 7 8 static double 8 9 Iq(double q, … … 37 38 return 1.0e-4 * s * s * form; 38 39 } 40 */ 41 42 static void 43 Fq(double q, 44 double *F1, 45 double *F2, 46 double sld, 47 double sld_solvent, 48 double radius_polar, 49 double radius_equatorial) 50 { 51 // Using ratio v = Rp/Re, we can implement the form given in Guinier (1955) 52 // i(h) = int_0^pi/2 Phi^2(h a sqrt(cos^2 + v^2 sin^2) cos dT 53 // = int_0^pi/2 Phi^2(h a sqrt((1-sin^2) + v^2 sin^2) cos dT 54 // = int_0^pi/2 Phi^2(h a sqrt(1 + sin^2(v^2-1)) cos dT 55 // u-substitution of 56 // u = sin, du = cos dT 57 // i(h) = int_0^1 Phi^2(h a sqrt(1 + u^2(v^2-1)) du 58 const double v_square_minus_one = square(radius_polar/radius_equatorial) - 1.0; 59 // translate a point in [-1,1] to a point in [0, 1] 60 // const double u = GAUSS_Z[i]*(upper-lower)/2 + (upper+lower)/2; 61 const double zm = 0.5; 62 const double zb = 0.5; 63 double total_F2 = 0.0; 64 double total_F1 = 0.0; 65 for (int i=0;i<GAUSS_N;i++) { 66 const double u = GAUSS_Z[i]*zm + zb; 67 const double r = radius_equatorial*sqrt(1.0 + u*u*v_square_minus_one); 68 const double f = sas_3j1x_x(q*r); 69 total_F2 += GAUSS_W[i] * f * f; 70 total_F1 += GAUSS_W[i] * f; 71 } 72 // translate dx in [-1,1] to dx in [lower,upper] 73 const double form_squared_avg = total_F2*zm; 74 const double form_avg = total_F1*zm; 75 const double s = (sld - sld_solvent) * form_volume(radius_polar, radius_equatorial); 76 *F1 = 1e-2 * s * form_avg; 77 *F2 = 1e-4 * s * s * form_squared_avg; 78 } 39 79 40 80 static double -
sasmodels/models/ellipsoid.py
r2d81cfe rc036ddb 161 161 ] 162 162 163 163 164 source = ["lib/sas_3j1x_x.c", "lib/gauss76.c", "ellipsoid.c"] 165 166 have_Fq = True 164 167 165 168 def ER(radius_polar, radius_equatorial): -
sasmodels/models/lib/sphere_form.c
r925ad6e r01c8d9e 13 13 return 1.0e-4*square(contrast * fq); 14 14 } 15 -
sasmodels/models/sphere.py
ref07e95 rc036ddb 69 69 source = ["lib/sas_3j1x_x.c", "lib/sphere_form.c"] 70 70 71 # No volume normalization despite having a volume parameter 72 # This should perhaps be volume normalized? 73 form_volume = """ 71 c_code = """ 72 static double form_volume(double radius) 73 { 74 74 return sphere_volume(radius); 75 """ 75 } 76 76 77 Iq = """ 78 return sphere_form(q, radius, sld, sld_solvent); 79 """ 77 static void Fq(double q, double *F1,double *F2, double sld, double solvent_sld, double radius) 78 { 79 const double fq = sas_3j1x_x(q*radius); 80 const double contrast = (sld - solvent_sld); 81 const double form = 1e-2 * contrast * sphere_volume(radius) * fq; 82 *F1 = form; 83 *F2 = form*form; 84 } 85 """ 86 87 # TODO: figure this out by inspection 88 have_Fq = True 80 89 81 90 def ER(radius): -
sasmodels/product.py
r2d81cfe rc036ddb 16 16 import numpy as np # type: ignore 17 17 18 from .modelinfo import ParameterTable, ModelInfo 18 from .modelinfo import ParameterTable, ModelInfo, Parameter 19 19 from .kernel import KernelModel, Kernel 20 20 from .details import make_details, dispersion_mesh … … 37 37 ER_ID = "radius_effective" 38 38 VF_ID = "volfraction" 39 BETA_DEFINITION = ("beta_mode", "", 0, [["P*S"],["P*(1+beta*(S-1))"]], "", 40 "Structure factor dispersion calculation mode") 39 41 40 42 # TODO: core_shell_sphere model has suppressed the volume ratio calculation … … 74 76 translate_name = dict((old.id, new.id) for old, new 75 77 in zip(s_pars.kernel_parameters[1:], s_list)) 76 combined_pars = p_pars.kernel_parameters + s_list 78 beta = [Parameter(*BETA_DEFINITION)] if p_info.have_Fq else [] 79 combined_pars = p_pars.kernel_parameters + s_list + beta 77 80 parameters = ParameterTable(combined_pars) 78 81 parameters.max_pd = p_pars.max_pd + s_pars.max_pd … … 151 154 #: Structure factor modelling interaction between particles. 152 155 self.S = S 156 153 157 #: Model precision. This is not really relevant, since it is the 154 158 #: individual P and S models that control the effective dtype, … … 168 172 # in opencl; or both in opencl, but one in single precision and the 169 173 # other in double precision). 174 170 175 p_kernel = self.P.make_kernel(q_vectors) 171 176 s_kernel = self.S.make_kernel(q_vectors) … … 211 216 p_offset = call_details.offset[:p_npars] 212 217 p_details = make_details(p_info, p_length, p_offset, nweights) 218 213 219 # Set p scale to the volume fraction in s, which is the first of the 214 220 # 'S' parameters in the parameter list, or 2+np in 0-origin. … … 254 260 s_values = np.hstack(s_values).astype(self.s_kernel.dtype) 255 261 262 # beta mode is the first parameter after the structure factor pars 263 beta_index = 2+p_npars+s_npars 264 beta_mode = values[beta_index] 265 256 266 # Call the kernels 257 p_result = self.p_kernel(p_details, p_values, cutoff, magnetic) 258 s_result = self.s_kernel(s_details, s_values, cutoff, False) 259 260 #print("p_npars",p_npars,s_npars,p_er,s_vr,values[2+p_npars+1:2+p_npars+s_npars]) 267 s_result = self.s_kernel.Iq(s_details, s_values, cutoff, False) 268 scale, background = values[0], values[1] 269 if beta_mode: 270 F1, F2, volume_avg = self.p_kernel.beta(p_details, p_values, cutoff, magnetic) 271 combined_scale = scale*volfrac/volume_avg 272 # Define lazy results based on intermediate values. 273 # The return value for the calculation should be an ordered 274 # dictionary containing any result the user might want to see 275 # at the end of the calculation, including scalars, strings, 276 # and plottable data. Don't want to build this structure during 277 # fits, only when displaying the final result (or a one-off 278 # computation which asks for it). 279 # Do not use the current hack of storing the intermediate values 280 # in self.results since that leads to awkward threading issues. 281 # Instead return the function along with the bundle of inputs. 282 # P and Q may themselves have intermediate results they want to 283 # include, such as A and B if P = A + B. Might use this mechanism 284 # to return the computed effective radius as well. 285 #def lazy_results(Q, S, F1, F2, scale): 286 # """ 287 # beta = F1**2 / F2 # what about divide by zero errors? 288 # return { 289 # 'P' : Data1D(Q, scale*F2), 290 # 'beta': Data1D(Q, beta), 291 # 'S' : Data1D(Q, S), 292 # 'Seff': Data1D(Q, 1 + beta*(S-1)), 293 # 'I' : Data1D(Q, scale*(F2 + (F1**2)*(S-1)) + background), 294 # } 295 #lazy_pars = s_result, F1, F2, combined_scale 296 self.results = [F2, s_result] 297 final_result = combined_scale*(F2 + (F1**2)*(s_result - 1)) + background 298 else: 299 p_result = self.p_kernel.Iq(p_details, p_values, cutoff, magnetic) 300 # remember the parts for plotting later 301 self.results = [p_result, s_result] 302 final_result = scale*(p_result*s_result) + background 303 261 304 #call_details.show(values) 262 305 #print("values", values) … … 265 308 #s_details.show(s_values) 266 309 #print("=>", s_result) 267 268 # remember the parts for plotting later269 self.results = [p_result, s_result]270 271 310 #import pylab as plt 272 311 #plt.subplot(211); plt.loglog(self.p_kernel.q_input.q, p_result, '-') 273 312 #plt.subplot(212); plt.loglog(self.s_kernel.q_input.q, s_result, '-') 274 313 #plt.figure() 275 276 return values[0]*(p_result*s_result) + values[1] 314 return final_result 277 315 278 316 def release(self):
Note: See TracChangeset
for help on using the changeset viewer.