Changeset 5d4777d in sasmodels
- Timestamp:
- Sep 2, 2014 1:24:38 AM (11 years ago)
- Branches:
- master, core_shell_microgels, costrafo411, magnetic_model, release_v0.94, release_v0.95, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- f4cf580
- Parents:
- ff7119b
- Files:
-
- 10 added
- 1 deleted
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
compare-new.py
rff7119b r5d4777d 7 7 8 8 from sasmodels.bumps_model import BumpsModel, plot_data, tic 9 from sasmodels.gen import opencl_model, dll_model 9 from sasmodels import gpu, dll 10 from sasmodels.convert import revert_model 11 10 12 11 13 def sasview_model(modelname, **pars): … … 13 15 Load a sasview model given the model name. 14 16 """ 15 modelname = modelname +"Model"17 modelname = modelname 16 18 sans = __import__('sans.models.'+modelname) 17 19 ModelClass = getattr(getattr(sans.models,modelname,None),modelname,None) … … 36 38 sasmodels = __import__('sasmodels.models.'+modelname) 37 39 module = getattr(sasmodels.models, modelname, None) 38 kernel = opencl_model(module, dtype=dtype)40 kernel = gpu.load_model(module, dtype=dtype) 39 41 return kernel 40 42 … … 42 44 sasmodels = __import__('sasmodels.models.'+modelname) 43 45 module = getattr(sasmodels.models, modelname, None) 44 kernel = dll _model(module, dtype=dtype)46 kernel = dll.load_model(module, dtype=dtype) 45 47 return kernel 46 48 47 48 def compare(Ncpu, cpuname, cpupars, Ngpu, gpuname, gpupars): 49 49 def randomize(p, v): 50 """ 51 Randomizing pars using sasview names. 52 Guessing at angles and slds. 53 """ 54 if any(p.endswith(s) for s in ('_pd_n','_pd_nsigmas','_pd_type')): 55 return v 56 if any(s in p for s in ('theta','phi','psi')): 57 return np.random.randint(0,90) 58 elif any(s in p for s in ('sld','Sld','SLD')): 59 return np.random.rand()*1e-5 60 else: 61 return np.random.rand() 62 63 def parlist(pars): 64 return "\n".join("%s: %s"%(p,v) for p,v in sorted(pars.items())) 65 66 def suppress_pd(pars): 67 """ 68 Suppress theta_pd for now until the normalization is resolved. 69 70 May also suppress complete polydispersity of the model to test 71 models more quickly. 72 """ 73 for p in pars: 74 if p.endswith("_pd"): pars[p] = 0 75 76 def compare(gpuname, gpupars, Ncpu, Ngpu, opts): 50 77 #from sasmodels.core import load_data 51 78 #data = load_data('December/DEC07098.DAT') 52 from sasmodels.core import empty_data1D 53 data = empty_data1D(np.logspace(-4, -1, 128)) 54 #from sasmodels.core import empty_2D, set_beam_stop 55 #data = empty_data2D(np.linspace(-0.05, 0.05, 128)) 56 #set_beam_stop(data, 0.004) 79 qmax = 1.0 if '-highq' in opts else (0.2 if '-midq' in opts else 0.05) 80 if "-1d" in opts: 81 from sasmodels.bumps_model import empty_data1D 82 qmax = np.log10(qmax) 83 data = empty_data1D(np.logspace(qmax-3, qmax, 128)) 84 else: 85 from sasmodels.bumps_model import empty_data2D, set_beam_stop 86 data = empty_data2D(np.linspace(-qmax, qmax, 128)) 87 set_beam_stop(data, 0.004) 57 88 is2D = hasattr(data, 'qx_data') 58 89 dtype = 'double' if '-double' in opts else 'single' 90 cutoff_opts = [s for s in opts if s.startswith('-cutoff')] 91 cutoff = float(cutoff_opts[0].split('=')[1]) if cutoff_opts else 1e-5 92 93 94 if '-random' in opts: 95 gpupars = dict((p,randomize(p,v)) for p,v in gpupars.items()) 96 if '-pars' in opts: print "pars",parlist(gpupars) 97 if '-mono' in opts: suppress_pd(gpupars) 98 99 cpuname, cpupars = revert_model(gpuname, gpupars) 100 101 try: 102 gpumodel = load_opencl(gpuname, dtype=dtype) 103 except Exception,exc: 104 print exc 105 print "... trying again with single precision" 106 gpumodel = load_opencl(gpuname, dtype='single') 107 model = BumpsModel(data, gpumodel, cutoff=cutoff, **gpupars) 59 108 if Ngpu > 0: 60 gpumodel = load_opencl(gpuname, dtype='single')61 model = BumpsModel(data, gpumodel, **gpupars)62 109 toc = tic() 63 110 for i in range(Ngpu): … … 66 113 gpu = model.theory() 67 114 gpu_time = toc()*1000./Ngpu 68 print "ocl t=%.1f ms, intensity=% .0f"%(gpu_time, sum(gpu[~np.isnan(gpu)]))115 print "ocl t=%.1f ms, intensity=%f"%(gpu_time, sum(gpu[~np.isnan(gpu)])) 69 116 #print max(gpu), min(gpu) 70 117 71 if 0 and Ncpu > 0: # Hack to compare ctypes vs. opencl 72 dllmodel = load_dll(gpuname, dtype='double') 73 model = BumpsModel(data, dllmodel, **gpupars) 118 comp = None 119 if Ncpu > 0 and "-dll" in opts: 120 dllmodel = load_dll(gpuname, dtype=dtype) 121 model = BumpsModel(data, dllmodel, cutoff=cutoff, **gpupars) 74 122 toc = tic() 75 123 for i in range(Ncpu): … … 77 125 cpu = model.theory() 78 126 cpu_time = toc()*1000./Ncpu 79 print "dll t=%.1f ms"%cpu_time127 comp = "dll" 80 128 81 129 elif 0: # Hack to check new vs old for GpuCylinder 82 130 from Models.code_cylinder_f import GpuCylinder as oldgpu 83 131 from sasmodel import SasModel 84 oldmodel = SasModel(data, oldgpu, dtype= 'single', **cpupars)132 oldmodel = SasModel(data, oldgpu, dtype=dtype, **cpupars) 85 133 toc = tic() 86 134 for i in range(Ngpu): … … 88 136 cpu = oldmodel.theory() 89 137 cpu_time = toc()*1000./Ngpu 90 print "old t=%.1f ms"%cpu_time138 comp = "old" 91 139 92 140 elif Ncpu > 0: … … 100 148 cpu = cpumodel.evalDistribution(data.x) 101 149 cpu_time = toc()*1000./Ncpu 102 print "sasview t=%.1f ms, intensity=%.0f"%(cpu_time, sum(cpu[model.index])) 103 150 comp = 'sasview' 151 #print "sasview t=%.1f ms, intensity=%.0f"%(cpu_time, sum(cpu[model.index])) 152 153 if comp: 154 print "%s t=%.1f ms, intensity=%f"%(comp, cpu_time, sum(cpu[model.index])) 104 155 if Ngpu > 0 and Ncpu > 0: 105 print "gpu/cpu", max(abs(gpu/cpu)), "%.15g"%max(abs(gpu)), "%.15g"%max(abs(cpu)) 156 #print "speedup %.2g"%(cpu_time/gpu_time) 157 #print "max |gpu/cpu|", max(abs(gpu/cpu)), "%.15g"%max(abs(gpu)), "%.15g"%max(abs(cpu)) 106 158 #cpu *= max(gpu/cpu) 107 abserr = (gpu - cpu) 108 relerr = (gpu - cpu)/cpu 109 print "max(|ocl-omp|)", max(abs(abserr[model.index])) 110 print "max(|(ocl-omp)/ocl|)", max(abs(relerr[model.index])) 111 #return 159 resid, relerr = np.zeros_like(gpu), np.zeros_like(gpu) 160 resid[model.index] = (gpu - cpu)[model.index] 161 relerr[model.index] = resid[model.index]/cpu[model.index] 162 print "max(|ocl-%s|)"%comp, max(abs(resid[model.index])) 163 print "max(|(ocl-%s)/ocl|)"%comp, max(abs(relerr[model.index])) 164 165 if '-noplot' in opts: return 112 166 113 167 import matplotlib.pyplot as plt … … 115 169 if Ngpu > 0: plt.subplot(131) 116 170 plot_data(data, cpu, scale='log') 117 plt.title(" omp t=%.1f ms"%cpu_time)171 plt.title("%s t=%.1f ms"%(comp,cpu_time)) 118 172 if Ngpu > 0: 119 173 if Ncpu > 0: plt.subplot(132) … … 122 176 if Ncpu > 0 and Ngpu > 0: 123 177 plt.subplot(133) 124 plot_data(data, 1e8*relerr, scale='linear') 125 plt.title("max rel err = %.3g"%max(abs(relerr))) 178 err = resid if '-abs' in opts else relerr 179 plot_data(data, err, scale='linear') 180 plt.title("max rel err = %.3g"%max(abs(err[model.index]))) 126 181 if is2D: plt.colorbar() 127 182 plt.show() 128 183 129 def rename(pars, **names):130 newpars = pars.copy()131 for new,old in names.items():132 for variant in ("", "_pd", "_pd_n", "_pd_nsigma"):133 if old+variant in newpars:134 newpars[new+variant] = pars[old+variant]135 del newpars[old+variant]136 return newpars137 138 def rescale_sld(pars, names):139 newpars = pars.copy()140 for p in names:141 newpars[p] *= 1e6142 return newpars143 144 184 # =========================================================================== 145 185 # 186 USAGE=""" 187 usage: compare.py model [Nopencl] [Nsasview] [options...] [key=val] 188 189 Compare the speed and value for a model between the SasView original and the 190 OpenCL rewrite. 191 192 model is the name of the model to compare (see below). 193 Nopencl is the number of times to run the OpenCL model (default=5) 194 Nsasview is the number of times to run the Sasview model (default=1) 195 196 Options (* for default): 197 198 -plot*/-noplot plots or suppress the plot of the model 199 -single/-double uses double precision for comparison 200 -lowq/-midq/-highq use q values up to 0.05, 0.2 or 1.0 201 -2d/-1d uses 1d or 2d random data 202 -preset/-random randomizes the parameters 203 -poly/-mono force monodisperse/polydisperse 204 -sasview/-dll whether cpu is tested using sasview or dll 205 -cutoff=1e-5/value cutoff for including a point in polydispersity 206 -nopars/-pars prints the parameter set or not 207 -rel/-abs plot relative or absolute error 208 209 Key=value pairs allow you to set specific values to any of the model 210 parameters. 211 212 Available models: 213 214 %s 215 """ 216 217 def main(): 218 opts = [arg for arg in sys.argv[1:] if arg.startswith('-')] 219 args = [arg for arg in sys.argv[1:] if not arg.startswith('-')] 220 models = "\n ".join("%-7s: %s"%(k,v.__name__.replace('_',' ')) 221 for k,v in sorted(MODELS.items())) 222 if len(args) == 0: 223 print(USAGE%models) 224 sys.exit(1) 225 if args[0] not in MODELS: 226 print "Model %r not available. Use one of:\n %s"%(args[0],models) 227 sys.exit(1) 228 229 name, pars = MODELS[args[0]]() 230 Nopencl = int(args[1]) if len(args) > 1 else 5 231 Nsasview = int(args[2]) if len(args) > 3 else 1 232 233 # Fill in default polydispersity parameters 234 pds = set(p.split('_pd')[0] for p in pars if p.endswith('_pd')) 235 for p in pds: 236 if p+"_pd_nsigma" not in pars: pars[p+"_pd_nsigma"] = 3 237 if p+"_pd_type" not in pars: pars[p+"_pd_type"] = "gaussian" 238 239 # Fill in parameters given on the command line 240 for arg in args[3:]: 241 k,v = arg.split('=') 242 if k not in pars: 243 # extract base name without distribution 244 # style may be either a.d or a_pd_d 245 s = set((p.split('_pd')[0]).split('.')[0] for p in pars) 246 print "%r invalid; parameters are: %s"%(k,", ".join(sorted(s))) 247 sys.exit(1) 248 pars[k] = float(v) if not v.endswith('type') else v 249 250 compare(name, pars, Nsasview, Nopencl, opts) 251 252 # =========================================================================== 253 # 254 146 255 MODELS = {} 147 256 def model(name): … … 151 260 return gather_function 152 261 153 USAGE="""154 usage: compare model [Nopencl] [Nsasview]155 156 Compare the speed and value for a model between the SasView original and the157 OpenCL rewrite.158 159 * Nopencl is the number of times to run the OpenCL model (default=5)160 161 * Nsasview is the number of times to run the Sasview model (default=1)162 163 * model is the name of the model to compare:164 165 %s166 """167 168 def main():169 if len(sys.argv) == 1:170 models = "\n ".join("%-7s: %s"%(k,v.__name__.replace('_',' '))171 for k,v in sorted(MODELS.items()))172 print(USAGE%models)173 sys.exit(1)174 175 cpuname, cpupars, gpuname, gpupars = MODELS[sys.argv[1]]()176 Nopencl = int(sys.argv[2]) if len(sys.argv) > 2 else 5177 Nsasview = int(sys.argv[3]) if len(sys.argv) > 3 else 1178 179 compare(Nsasview, cpuname, cpupars, Nopencl, gpuname, gpupars)180 262 181 263 @model('cyl') 182 264 def cylinder(): 183 cpupars = dict( 184 scale=.003, background=.1, 185 sldCyl=.291e-6, sldSolv=5.77e-6, 186 radius=264.1, length=66.96, 187 cyl_theta=85, cyl_phi=0, 188 radius_pd=0.1, radius_pd_n=10, radius_pd_nsigma=3, 189 length_pd=0.1,length_pd_n=1, length_pd_nsigma=3, 190 cyl_theta_pd=45, cyl_theta_pd_n=50, cyl_theta_pd_nsigma=3, 191 cyl_phi_pd=0.1, cyl_phi_pd_n=5, cyl_phi_pd_nsigma=3, 192 ) 193 cpuname = 'Cylinder' 194 195 gpupars = rename(cpupars, theta='cyl_theta', phi='cyl_phi', sld='sldCyl', solvent_sld='sldSolv') 196 gpupars = rescale_sld(gpupars, ['sld', 'solvent_sld']) 197 gpuname = 'cylinder' 198 return cpuname, cpupars, gpuname, gpupars 265 pars = dict( 266 scale=1, background=0, 267 sld=6e-6, solvent_sld=1e-6, 268 #radius=5, length=20, 269 radius=260, length=290, 270 theta=30, phi=15, 271 radius_pd=.2, radius_pd_n=1, 272 length_pd=.2,length_pd_n=1, 273 theta_pd=15, theta_pd_n=25, 274 phi_pd=15, phi_pd_n=1, 275 ) 276 return 'cylinder', pars 277 278 @model('capcyl') 279 def capped_cylinder(): 280 pars = dict( 281 scale=1, background=0, 282 sld=6e-6, solvent_sld=1e-6, 283 radius=260, cap_radius=80000, length=290, 284 theta=30, phi=15, 285 radius_pd=.2, radius_pd_n=1, 286 cap_radius_pd=.2, cap_radius_pd_n=1, 287 length_pd=.2, length_pd_n=1, 288 theta_pd=15, theta_pd_n=25, 289 phi_pd=15, phi_pd_n=1, 290 ) 291 return 'capped_cylinder', pars 292 293 294 @model('cscyl') 295 def core_shell_cylinder(): 296 pars = dict( 297 scale=1, background=0, 298 core_sld=6e-6, shell_sld=8e-6, solvent_sld=1e-6, 299 radius=325, thickness=25, length=34.2709, 300 theta=90, phi=0, 301 radius_pd=0.1, radius_pd_n=10, 302 length_pd=0.1, length_pd_n=5, 303 thickness_pd=0.1, thickness_pd_n=5, 304 theta_pd=15.8, theta_pd_n=5, 305 phi_pd=0.0008748, phi_pd_n=5, 306 ) 307 return 'core_shell_cylinder', pars 199 308 200 309 201 310 @model('ell') 202 def ellipse(): 203 pars = dict( 204 scale=.027, background=4.9, 205 sldEll=.297e-6, sldSolv=5.773e-6, 206 radius_a=60, radius_b=180, 207 axis_theta=0, axis_phi=90, 208 radius_a_pd=0.1, radius_a_pd_n=10, radius_a_pd_nsigma=3, 209 radius_b_pd=0.1, radius_b_pd_n=10, radius_b_pd_nsigma=3, 210 axis_theta_pd=0.1, axis_theta_pd_n=6, axis_theta_pd_nsigma=3, 211 axis_phi_pd=0.1, axis_phi_pd_n=6, axis_phi_pd_nsigma=3, 212 ) 213 214 from Models.code_ellipse import GpuEllipse as gpumodel 215 model = sasview_model('Ellipsoid', **pars) 216 217 pars = rename(pars, theta='axis_theta', phi='axis_phi', sld='sldEll', solvent_sld='sldSolv') 218 pars = rescale_sld(pars, ['sld', 'solvent_sld']) 219 return model, gpumodel, pars 220 221 222 @model('cscyl') 223 def core_shell_cylinder(N=1): 224 pars = dict( 225 scale= 1.77881e-06, background=223.827, 226 core_sld=1e-6, shell_sld=.291e-6, solvent_sld=7.105e-6, 227 radius=325, thickness=25, length=34.2709, 228 axis_theta=90, axis_phi=0, 229 radius_pd=0.1, radius_pd_n=10, radius_pd_nsigma=3, 230 length_pd=0.1, length_pd_n=10, length_pd_nsigma=3, 231 thickness_pd=0.1, thickness_pd_n=5, thickness_pd_nsigma=3, 232 axis_theta_pd=15.8, axis_theta_pd_n=20, axis_theta_pd_nsigma=5, 233 axis_phi_pd=0.0008748, axis_phi_pd_n=5, axis_phi_pd_nsigma=3, 234 ) 235 236 model = sasview_model('CoreShellCylinder', **pars) 237 from Models.code_coreshellcyl_f import GpuCoreShellCylinder as gpumodel 238 239 pars = rename(pars, theta='axis_theta', phi='axis_phi') 240 pars = rescale_sld(pars, ['core_sld', 'shell_sld', 'solvent_sld']) 241 return model, gpumodel, pars 311 def ellipsoid(): 312 pars = dict( 313 scale=1, background=0, 314 sld=6e-6, solvent_sld=1e-6, 315 rpolar=50, requatorial=30, 316 theta=0, phi=0, 317 rpolar_pd=0.3, rpolar_pd_n=10, 318 requatorial_pd=0, requatorial_pd_n=10, 319 theta_pd=0, theta_pd_n=45, 320 phi_pd=0, phi_pd_n=45, 321 ) 322 return 'ellipsoid', pars 242 323 243 324 244 325 @model('ell3') 245 def triaxial_ellipse(N=1): 246 pars = dict( 247 scale=0.08, background=5, 248 sldEll=7.105e-6, sldSolv=.291e-6, 249 axis_theta=0, axis_phi=0, axis_psi=0, 250 semi_axisA=15, semi_axisB=20, semi_axisC=500, 251 axis_theta_pd=20, axis_theta_pd_n=10, axis_theta_pd_nsigma=3, 252 axis_phi_pd=.1, axis_phi_pd_n=10, axis_phi_pd_nsigma=3, 253 axis_psi_pd=30, axis_psi_pd_n=5, axis_psi_pd_nsigma=3, 254 semi_axisA_pd=.1, semi_axisA_pd_n=5, semi_axisA_pd_nsigma=3, 255 semi_axisB_pd=.1, semi_axisB_pd_n=5, semi_axisB_pd_nsigma=3, 256 semi_axisC_pd=.1, semi_axisC_pd_n=5, semi_axisC_pd_nsigma=3, 257 ) 258 259 model = sasview_model('TriaxialEllipsoid', **pars) 260 from Models.code_triaxialellipse import GpuTriEllipse as gpumodel 261 262 pars = rename(pars, 263 theta='axis_theta', phi='axis_phi', psi='axis_psi', 264 sld='sldEll', solvent_sld='sldSolv', 265 radius_a='semi_axisA', radius_b='semi_axisB', 266 radius_c='semi_axisC', 267 ) 268 pars = rescale_sld(pars, ['sld', 'solvent_sld']) 269 return model, gpumodel, pars 326 def triaxial_ellipsoid(): 327 pars = dict( 328 scale=1, background=0, 329 sld=6e-6, solvent_sld=1e-6, 330 theta=0, phi=0, psi=0, 331 req_minor=25, req_major=36, rpolar=50, 332 theta_pd=0, theta_pd_n=5, 333 phi_pd=0, phi_pd_n=5, 334 psi_pd=0, psi_pd_n=5, 335 req_minor_pd=0, req_minor_pd_n=5, 336 req_major_pd=0, req_major_pd_n=5, 337 rpolar_pd=.3, rpolar_pd_n=25, 338 ) 339 return 'triaxial_ellipsoid', pars 340 341 @model('sph') 342 def sphere(): 343 pars = dict( 344 scale=1, background=0, 345 sld=6e-6, solvent_sld=1e-6, 346 radius=120, 347 radius_pd=.3, radius_pd_n=5, 348 ) 349 return 'sphere', pars 270 350 271 351 @model('lam') 272 def lamellar(N=1): 273 pars = dict( 274 scale=0.08, background=0.003, 275 sld_bi=5.38e-6,sld_sol=7.105e-6, 276 bi_thick=19.2946, 277 bi_thick_pd= 0.37765, bi_thick_pd_n=40, bi_thick_pd_nsigma=3, 278 ) 279 280 model = sasview_model('Lamellar', **pars) 281 from Models.code_lamellar import GpuLamellar as gpumodel 282 283 pars = rename(pars, sld='sld_bi', solvent_sld='sld_sol', thickness='bi_thick') 284 pars = rescale_sld(pars, ['sld', 'solvent_sld']) 285 return model, gpumodel, pars 286 287 @model('capcyl') 288 def capped_cylinder(N=1): 289 pars = dict( 290 scale=.08, background=0, 291 sld_capcyl=1e-6, sld_solv=6.3e-6, 292 rad_cyl=20, rad_cap=40, len_cyl=400, 293 theta=0, phi=0, 294 rad_cyl_pd=.1, rad_cyl_pd_n=10, rad_cyl_pd_nsigma=3, 295 rad_cap_pd=.1, rad_cap_pd_n=10, rad_cap_pd_nsigma=3, 296 len_cyl_pd=.1, len_cyl_pd_n=3, len_cyl_pd_nsigma=3, 297 theta_pd=.1, theta_pd_n=3, theta_pd_nsigma=3, 298 phi_pd=.1, phi_pd_n=3, phi_pd_nsigma=3, 299 ) 300 301 302 model = sasview_model('CappedCylinder', **pars) 303 from Models.code_capcyl import GpuCapCylinder as gpumodel 304 305 pars = rename(pars, 306 sld='sld_capcyl', solvent_sld='sld_solv', 307 length='len_cyl', radius='rad_cyl', 308 cap_radius='rad_cap') 309 pars = rescale_sld(pars, ['sld', 'solvent_sld']) 310 return model, gpumodel, pars 311 352 def lamellar(): 353 pars = dict( 354 scale=1, background=0, 355 sld=6e-6, solvent_sld=1e-6, 356 thickness=40, 357 thickness_pd= 0.3, thickness_pd_n=40, 358 ) 359 return 'lamellar', pars 312 360 313 361 if __name__ == "__main__": -
sasmodels/bumps_model.py
rff7119b r5d4777d 140 140 import matplotlib.pyplot as plt 141 141 if hasattr(data, 'qx_data'): 142 img = masked_array(iq, data.mask) 142 iq = iq[:] 143 valid = np.isfinite(iq) 143 144 if scale == 'log': 144 img[(img <= 0) | ~np.isfinite(img)] = masked 145 img = np.log10(img) 145 valid[valid] = (iq[valid] > 0) 146 iq[valid] = np.log10(iq[valid]) 147 iq[~valid|data.mask] = 0 148 #plottable = iq 149 plottable = masked_array(iq, ~valid|data.mask) 146 150 xmin, xmax = min(data.qx_data), max(data.qx_data) 147 151 ymin, ymax = min(data.qy_data), max(data.qy_data) 148 plt.imshow( img.reshape(128,128),152 plt.imshow(plottable.reshape(128,128), 149 153 interpolation='nearest', aspect=1, origin='upper', 150 154 extent=[xmin, xmax, ymin, ymax], vmin=vmin, vmax=vmax) … … 154 158 plt.plot(data.x[idx], iq[idx]) 155 159 else: 156 idx = np.isfinite(iq) & (iq>0) 160 idx = np.isfinite(iq) 161 idx[idx] = (iq[idx]>0) 157 162 plt.loglog(data.x[idx], iq[idx]) 158 163 … … 228 233 def __init__(self, data, model, cutoff=1e-5, **kw): 229 234 from bumps.names import Parameter 235 partype = model.info['partype'] 230 236 231 237 # interpret data … … 236 242 self.diq = data.err_data[self.index] 237 243 self._theory = np.zeros_like(data.data) 238 q_vectors = [data.qx_data, data.qy_data] 244 if not partype['orientation'] and not partype['magnetic']: 245 q_vectors = [np.sqrt(data.qx_data**2+data.qy_data**2)] 246 else: 247 q_vectors = [data.qx_data, data.qy_data] 239 248 else: 240 249 self.index = (data.x>=data.qmin) & (data.x<=data.qmax) & ~np.isnan(data.y) … … 258 267 setattr(self, name, Parameter.default(value, name=name, limits=limits)) 259 268 pars.append(name) 260 for name in model.info['partype']['pd-2d']:269 for name in partype['pd-2d']: 261 270 for xpart,xdefault,xlimits in [ 262 271 ('_pd', 0, limits), -
sasmodels/dll.py
rff7119b r5d4777d 2 2 C types wrapper for sasview models. 3 3 """ 4 4 import sys 5 import os 5 6 import ctypes as ct 6 7 from ctypes import c_void_p, c_int, c_double … … 11 12 12 13 from .gen import F32, F64 14 # Compiler platform details 15 if sys.platform == 'darwin': 16 COMPILE = "gcc-mp-4.7 -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm -lgomp" 17 elif os.name == 'nt': 18 COMPILE = "gcc -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm" 19 else: 20 COMPILE = "cc -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm" 21 DLL_PATH = "/tmp" 22 23 24 def dll_path(info): 25 """ 26 Path to the compiled model defined by *info*. 27 """ 28 from os.path import join as joinpath, split as splitpath, splitext 29 basename = splitext(splitpath(info['filename'])[1])[0] 30 return joinpath(DLL_PATH, basename+'.so') 31 32 33 def load_model(kernel_module, dtype=None): 34 """ 35 Load the compiled model defined by *kernel_module*. 36 37 Recompile if any files are newer than the model file. 38 39 *dtype* is ignored. Compiled files are always double. 40 41 The DLL is not loaded until the kernel is called so models an 42 be defined without using too many resources. 43 """ 44 import tempfile 45 46 source, info = gen.make(kernel_module) 47 source_files = gen.sources(info) + [info['filename']] 48 newest = max(os.path.getmtime(f) for f in source_files) 49 dllpath = dll_path(info) 50 if not os.path.exists(dllpath) or os.path.getmtime(dllpath)<newest: 51 # Replace with a proper temp file 52 fid, filename = tempfile.mkstemp(suffix=".c",prefix="sas_"+info['name']) 53 os.fdopen(fid,"w").write(source) 54 status = os.system(COMPILE%(filename, dllpath)) 55 if status != 0: 56 print "compile failed. File is in %r"%filename 57 else: 58 ## uncomment the following to keep the generated c file 59 #os.unlink(filename); print "saving compiled file in %r"%filename 60 pass 61 return DllModel(dllpath, info) 62 13 63 14 64 IQ_ARGS = [c_void_p, c_void_p, c_int, c_void_p, c_double] … … 124 174 """ 125 175 def __init__(self, kernel, info, input): 176 self.info = info 126 177 self.input = input 127 178 self.kernel = kernel -
sasmodels/gen.py
rff7119b r5d4777d 28 28 29 29 *Iq*, *Iqxy*, *Imagnetic* and *form_volume* should be stylized C-99 30 functions written for OpenCL. Floating point values should be 31 declared as *real*. Depending on how the function is called, a macro 32 will replace *real* with *float* or *double*. Unfortunately, MacOSX 33 is picky about floating point constants, which should be defined with 34 value + 'f' if they are of type *float* or just a bare value if they 35 are of type *double*. The solution is a macro *REAL(value)* which 36 adds the 'f' if compiling for single precision floating point. This 37 does make the code ugly, and may someday be replaced by a clever 38 regular expression which does the same job. OpenCL has a *sincos* 39 function which can improve performance when both the *sin* and *cos* 40 values are needed for a particular argument. Since this function 41 does not exist in C-99, all use of *sincos* should be replaced by the 42 macro *SINCOS(value,sn,cn)* where *sn* and *cn* are previously declared 43 *real* values. *value* may be an expression. When compiled for systems 44 without OpenCL, *SINCOS* will be replaced by *sin* and *cos* calls. All 45 functions need prototype declarations even if the are defined before they 46 are used -- another present from MacOSX. OpenCL does not support 47 *#include* preprocessor directives; instead the includes must be listed 48 in the kernel metadata, with functions defined before they are used. 49 The included files should be listed using relative path to the kernel 50 source file, or if using one of the standard models, relative to the 51 sasmodels source files. 30 functions written for OpenCL. All functions need prototype declarations 31 even if the are defined before they are used. OpenCL does not support 32 *#include* preprocessor directives, so instead the list of includes needs 33 to be given as part of the metadata in the kernel module definition. 34 The included files should be listed using a path relative to the kernel 35 module, or if using "lib/file.c" if it is one of the standard includes 36 provided with the sasmodels source. The includes need to be listed in 37 order so that functions are defined before they are used. 38 39 Floating point values should be declared as *real*. Depending on how the 40 function is called, a macro will replace *real* with *float* or *double*. 41 Unfortunately, MacOSX is picky about floating point constants, which 42 should be defined with value + 'f' if they are of type *float* or just 43 a bare value if they are of type *double*. The solution is a macro 44 *REAL(value)* which adds the 'f' if compiling for single precision 45 floating point. [Note: we could write a clever regular expression 46 which automatically detects real-valued constants. If we wanted to 47 make our code more C-like, we could define variables with double but 48 replace them with float before compiling for single precision.] 49 50 OpenCL has a *sincos* function which can improve performance when both 51 the *sin* and *cos* values are needed for a particular argument. Since 52 this function does not exist in C99, all use of *sincos* should be 53 replaced by the macro *SINCOS(value,sn,cn)* where *sn* and *cn* are 54 previously declared *real* values. *value* may be an expression. When 55 compiled for systems without OpenCL, *SINCOS* will be replaced by 56 *sin* and *cos* calls. 57 58 If the input parameters are invalid, the scattering calculator should 59 return a negative number. Particularly with polydispersity, there are 60 some sets of shape parameters which lead to nonsensical forms, such 61 as a capped cylinder where the cap radius is smaller than the 62 cylinder radius. The polydispersity calculation will ignore these points, 63 effectively chopping the parameter weight distributions at the boundary 64 of the infeasible region. The resulting scattering will be set to 65 background. This will work correctly even when polydispersity is off. 52 66 53 67 *ER* and *VR* are python functions which operate on parameter vectors. … … 112 126 *VR* is a python function defining the volume ratio. If it is not 113 127 present, the volume ratio is 1. 128 129 *form_volume*, *Iq*, *Iqxy*, *Imagnetic* are strings containing the 130 C source code for the body of the volume, Iq, and Iqxy functions 131 respectively. These can also be defined in the last source file. 114 132 115 133 An *info* dictionary is constructed from the kernel meta data and … … 156 174 __all__ = ["make, doc", "sources"] 157 175 176 import sys 177 import os 158 178 import os.path 159 179 … … 226 246 #endif 227 247 228 // Standard mathematical constants , prefixed with M_:229 // E, LOG2E, LOG10E, LN2, LN10, PI, PI_2, PI_4, 1_PI, 2_PI,230 // 2_SQRTPI, SQRT2, SQRT1_2248 // Standard mathematical constants: 249 // M_E, M_LOG2E, M_LOG10E, M_LN2, M_LN10, M_PI, M_PI_2=pi/2, M_PI_4=pi/4, 250 // M_1_PI=1/pi, M_2_PI=2/pi, M_2_SQRTPI=2/sqrt(pi), SQRT2, SQRT1_2=sqrt(1/2) 231 251 // OpenCL defines M_constant_F for float constants, and nothing if double 232 252 // is not enabled on the card, which is why these constants may be missing … … 256 276 'qinit': "const real qi = q[i];", 257 277 'qcall': "qi", 278 'qwork': ["q"], 258 279 } 259 280 … … 263 284 'qinit': "const real qxi = qx[i];\n const real qyi = qy[i];", 264 285 'qcall': "qxi, qyi", 286 'qwork': ["qx", "qy"], 265 287 } 266 288 … … 318 340 for (int %(name)s_i=0; %(name)s_i < N%(name)s; %(name)s_i++) { 319 341 const real %(name)s = loops[2*(%(name)s_i%(offset)s)]; 320 const real %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];""" 342 const real %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\ 343 """ 321 344 322 345 # Polydispersity loop body. … … 327 350 const real weight = %(weight_product)s; 328 351 if (weight > cutoff) { 329 ret += weight*%(fn)s(%(qcall)s, %(pcall)s); 330 norm += weight; 331 %(volume_norm)s 332 }""" 352 const real I = %(fn)s(%(qcall)s, %(pcall)s); 353 if (I>=REAL(0.0)) { // scattering cannot be negative 354 ret += weight*I; 355 norm += weight; 356 %(volume_norm)s 357 } 358 //else { printf("exclude qx,qy,I:%%g,%%g,%%g\\n",%(qcall)s,I); } 359 } 360 //else { printf("exclude weight:%%g\\n",weight); }\ 361 """ 362 363 # Use this when integrating over orientation 364 SPHERICAL_CORRECTION="""\ 365 // Correction factor for spherical integration p(theta) I(q) sin(theta) dtheta 366 real spherical_correction = (Ntheta>1 ? fabs(cos(M_PI_180*phi)) : REAL(1.0));\ 367 """ 333 368 334 369 # Volume normalization. … … 337 372 # a normalized weight. 338 373 VOLUME_NORM="""const real vol_weight = %(weight)s; 339 vol += vol_weight*form_volume(%(pars)s); 340 norm_vol += vol_weight;""" 374 vol += vol_weight*form_volume(%(pars)s); 375 norm_vol += vol_weight;\ 376 """ 377 378 # functions defined as strings in the .py module 379 WORK_FUNCTION="""\ 380 real %(name)s(%(pars)s); 381 real %(name)s(%(pars)s) 382 { 383 %(body)s 384 }\ 385 """ 341 386 342 387 # Documentation header for the module, giving the model name, its short … … 393 438 vol_pars = info['partype']['volume'] 394 439 q_pars = KERNEL_2D if is_2D else KERNEL_1D 440 fn = q_pars['fn'] 395 441 396 442 # Build polydispersity loops … … 426 472 fq_pars = [p[0] for p in info['parameters'][len(COMMON_PARAMETERS):] 427 473 if p[0] in set(fixed_pars+pd_pars)] 474 if False and "phi" in pd_pars: 475 spherical_correction = [indent(SPHERICAL_CORRECTION, depth)] 476 weights = [p+"_w" for p in pd_pars]+['spherical_correction'] 477 else: 478 spherical_correction = [] 479 weights = [p+"_w" for p in pd_pars] 428 480 subst = { 429 'weight_product': "*".join( p+"_w" for p in pd_pars),481 'weight_product': "*".join(weights), 430 482 'volume_norm': volume_norm, 431 'fn': q_pars['fn'],483 'fn': fn, 432 484 'qcall': q_pars['qcall'], 433 485 'pcall': ", ".join(fq_pars), # skip scale and background 434 486 } 435 487 loop_body = [indent(LOOP_BODY%subst, depth)] 436 loops = "\n".join(loop_head+ loop_body+loop_end)488 loops = "\n".join(loop_head+spherical_correction+loop_body+loop_end) 437 489 438 490 # declarations for non-pd followed by pd pars … … 465 517 } 466 518 kernel = KERNEL_TEMPLATE%subst 519 520 # If the working function is defined in the kernel metadata as a 521 # string, translate the string to an actual function definition 522 # and put it before the kernel. 523 if info[fn]: 524 subst = { 525 'name': fn, 526 'pars': ", ".join("real "+p for p in q_pars['qwork']+fq_pars), 527 'body': info[fn], 528 } 529 kernel = "\n".join((WORK_FUNCTION%subst, kernel)) 467 530 return kernel 468 531 … … 514 577 search_path = [ dirname(info['filename']), 515 578 abspath(joinpath(dirname(__file__),'models')) ] 516 return [_search(search_path ) for f in info['source']]579 return [_search(search_path, f) for f in info['source']] 517 580 518 581 def make_model(info): … … 521 584 found in the given search path. 522 585 """ 586 source = [open(f).read() for f in sources(info)] 587 # If the form volume is defined as a string, then wrap it in a 588 # function definition and place it after the external sources but 589 # before the kernel functions. If the kernel functions are strings, 590 # they will be translated in the make_kernel call. 591 if info['form_volume']: 592 subst = { 593 'name': "form_volume", 594 'pars': ", ".join("real "+p for p in info['partype']['volume']), 595 'body': info['form_volume'], 596 } 597 source.append(WORK_FUNCTION%subst) 523 598 kernel_Iq = make_kernel(info, is_2D=False) 524 599 kernel_Iqxy = make_kernel(info, is_2D=True) 525 source = [open(f).read() for f in sources(info)]526 600 kernel = "\n\n".join([KERNEL_HEADER]+source+[kernel_Iq, kernel_Iqxy]) 527 601 return kernel … … 573 647 name = kernel_module.name, 574 648 title = kernel_module.title, 575 source = kernel_module.source,576 649 description = kernel_module.description, 577 650 parameters = COMMON_PARAMETERS + kernel_module.parameters, 578 ER = getattr(kernel_module, 'ER', None), 579 VR = getattr(kernel_module, 'VR', None), 651 source = getattr(kernel_module, 'source', []), 580 652 ) 653 # Fill in attributes which default to None 654 info.update((k,getattr(kernel_module, k, None)) 655 for k in ('ER', 'VR', 'form_volume', 'Iq', 'Iqxy')) 656 # Fill in the derived attributes 581 657 info['limits'] = dict((p[0],p[3]) for p in info['parameters']) 582 658 info['partype'] = categorize_parameters(info['parameters']) … … 596 672 return DOC_HEADER%subst 597 673 598 # Compiler platform details599 if sys.platform == 'darwin':600 COMPILE = "gcc-mp-4.7 -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm -lgomp"601 elif os.name == 'nt':602 COMPILE = "gcc -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm"603 else:604 COMPILE = "cc -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm"605 DLL_PATH = "/tmp"606 607 608 def dll_path(info):609 """610 Path to the compiled model defined by *info*.611 """612 from os.path import join as joinpath, split as splitpath, splitext613 basename = splitext(splitpath(info['filename'])[1])[0]614 return joinpath(DLL_PATH, basename+'.so')615 616 617 def dll_model(kernel_module, dtype=None):618 """619 Load the compiled model defined by *kernel_module*.620 621 Recompile if any files are newer than the model file.622 623 *dtype* is ignored. Compiled files are always double.624 625 The DLL is not loaded until the kernel is called so models an626 be defined without using too many resources.627 """628 import tempfile629 from sasmodels import dll630 631 source, info = make(kernel_module)632 source_files = sources(info) + [info['filename']]633 newest = max(os.path.getmtime(f) for f in source_files)634 dllpath = dll_path(info)635 if not os.path.exists(dllpath) or os.path.getmtime(dllpath)<newest:636 # Replace with a proper temp file637 srcfile = tempfile.mkstemp(suffix=".c",prefix="sas_"+info['name'])638 open(srcfile, 'w').write(source)639 os.system(COMPILE%(srcfile, dllpath))640 ## comment the following to keep the generated c file641 os.unlink(srcfile)642 return dll.DllModel(dllpath, info)643 644 645 def opencl_model(kernel_module, dtype="single"):646 """647 Load the OpenCL model defined by *kernel_module*.648 649 Access to the OpenCL device is delayed until the kernel is called650 so models can be defined without using too many resources.651 """652 from sasmodels import gpu653 654 source, info = make(kernel_module)655 ## for debugging, save source to a .cl file, edit it, and reload as model656 #open(modelname+'.cl','w').write(source)657 #source = open(modelname+'.cl','r').read()658 return gpu.GpuModel(source, info, dtype)659 674 660 675 -
sasmodels/gpu.py
rff7119b r5d4777d 25 25 26 26 """ 27 import warnings28 29 27 import numpy as np 30 28 import pyopencl as cl … … 32 30 33 31 from . import gen 34 35 from .gen import F32, F6436 32 37 33 F32_DEFS = """\ … … 52 48 # larger than necessary given that cost grows as npts^k where k is the number 53 49 # of polydisperse parameters. 54 MAX_LOOPS = 1024 50 MAX_LOOPS = 2048 51 52 def load_model(kernel_module, dtype="single"): 53 """ 54 Load the OpenCL model defined by *kernel_module*. 55 56 Access to the OpenCL device is delayed until the kernel is called 57 so models can be defined without using too many resources. 58 """ 59 source, info = gen.make(kernel_module) 60 ## for debugging, save source to a .cl file, edit it, and reload as model 61 open(info['name']+'.cl','w').write(source) 62 #source = open(info['name']+'.cl','r').read() 63 return GpuModel(source, info, dtype) 55 64 56 65 ENV = None … … 103 112 """ 104 113 dtype = np.dtype(dtype) 105 if dtype== F64 and not all(has_double(d) for d in context.devices):114 if dtype==gen.F64 and not all(has_double(d) for d in context.devices): 106 115 raise RuntimeError("Double precision not supported for devices") 107 116 108 header = F64_DEFS if dtype == F64 else F32_DEFS117 header = F64_DEFS if dtype == gen.F64 else F32_DEFS 109 118 # Note: USE_SINCOS makes the intel cpu slower under opencl 110 119 if context.devices[0].type == cl.device_type.GPU: … … 158 167 is an optional extension which may not be available on all devices. 159 168 """ 160 def __init__(self, source, info, dtype= F32):169 def __init__(self, source, info, dtype=gen.F32): 161 170 self.info = info 162 171 self.source = source … … 221 230 buffer will be released when the data object is freed. 222 231 """ 223 def __init__(self, q_vectors, dtype= F32):232 def __init__(self, q_vectors, dtype=gen.F32): 224 233 env = environment() 225 234 self.nq = q_vectors[0].size … … 273 282 env = environment() 274 283 self.loops_b = [cl.Buffer(env.context, mf.READ_WRITE, 275 MAX_LOOPS*input.dtype.itemsize)284 2*MAX_LOOPS*input.dtype.itemsize) 276 285 for _ in env.queues] 277 286 self.res_b = [cl.Buffer(env.context, mf.READ_WRITE, … … 281 290 282 291 def __call__(self, pars, pd_pars, cutoff=1e-5): 283 real = np.float32 if self.input.dtype == F32 else np.float64292 real = np.float32 if self.input.dtype == gen.F32 else np.float64 284 293 fixed = [real(p) for p in pars] 285 294 cutoff = real(cutoff) 286 295 loops = np.hstack(pd_pars) 287 296 loops = np.ascontiguousarray(loops.T, self.input.dtype).flatten() 288 loops_N = [np.uint32(len(p[0])) for p in pd_pars] 297 Nloops = [np.uint32(len(p[0])) for p in pd_pars] 298 #print "loops",Nloops, loops 289 299 290 300 #import sys; print >>sys.stderr,"opencl eval",pars 291 301 #print "opencl eval",pars 292 if len(loops) > MAX_LOOPS:302 if len(loops) > 2*MAX_LOOPS: 293 303 raise ValueError("too many polydispersity points") 294 304 device_num = 0 … … 300 310 #ctx = environment().context 301 311 #loops_bi = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=loops) 302 args = self.input.q_buffers + [res_bi,loops_bi,loops_l,cutoff] + fixed + loops_N312 args = self.input.q_buffers + [res_bi,loops_bi,loops_l,cutoff] + fixed + Nloops 303 313 self.kernel(queuei, self.input.global_size, None, *args) 304 314 cl.enqueue_copy(queuei, self.res, res_bi) -
sasmodels/models/README.rst
rff7119b r5d4777d 7 7 model. 8 8 9 cylindermodel_onefile.py is cylinder.c+cylinder.py merged into one file. 10 This doesn't actually run yet since sasmodels.gen has not been updated 11 to support it. It exists as a proposal. Note that the function declaration 12 has been removed since there is enough information in the parameter 13 definitions to generate it automatically. Note also that "source" which 14 used to be all the source has been renamed "includes". 15 16 One-file models could coexist with the py+c file models by checking for the 17 existence of c_blah and creating the appropriate function wrappers. These 18 would be appended after any include files. You shouldn't mix the two forms 19 within a single model since form_volume needs to be defined before 20 Iq/Iqxy but after the libraries. 9 lamellar.py is an example of a single file model with embedded C code. 21 10 22 11 Note: may want to rename form_volume to calc_volume and Iq/Iqxy to 23 calc_Iq/calc_Iqxy in both the c+py and the one file forms so that the 24 names are more predictable. Similarly ER/VR go to calc_ER/calc_VR. 12 calc_Iq/calc_Iqxy. Similarly ER/VR go to calc_ER/calc_VR. 25 13 26 14 Note: It is possible to translate python code automatically to opencl, using 27 something like numba, clyther, shedskin or pypy. 15 something like numba, clyther, shedskin or pypy, so maybe the kernel functions 16 could be implemented without any C syntax. 28 17 29 18 Magnetism hasn't been implemented yet. We may want a separate Imagnetic … … 35 24 36 25 Need to write code to generate the polydispersity loops in python for 37 kernels that are only implemented in python. 26 kernels that are only implemented in python. Also, need to implement 27 an example kernel directly in python. -
sasmodels/models/cylinder.c
rff7119b r5d4777d 1 1 real form_volume(real radius, real length); 2 2 real Iq(real q, real sld, real solvent_sld, real radius, real length); 3 real Iqxy(real qx, real qy, real sld, real solvent_sld, real radius, real length, real theta, real phi); 3 real Iqxy(real qx, real qy, real sld, real solvent_sld, 4 real radius, real length, real theta, real phi); 5 6 // twovd = 2 * volume * delta_rho 7 // besarg = q * R * sin(alpha) 8 // siarg = q * L/2 * cos(alpha) 9 real _cyl(real twovd, real besarg, real siarg); 10 real _cyl(real twovd, real besarg, real siarg) 11 { 12 const real bj = (besarg == REAL(0.0) ? REAL(0.5) : J1(besarg)/besarg); 13 const real si = (siarg == REAL(0.0) ? REAL(1.0) : sin(siarg)/siarg); 14 return twovd*si*bj; 15 } 4 16 5 17 real form_volume(real radius, real length) … … 14 26 real length) 15 27 { 16 const real halflength = REAL(0.5)*length; 17 real summ = REAL(0.0); 28 const real qr = q*radius; 29 const real qh = q*REAL(0.5)*length; 30 const real twovd = REAL(2.0)*(sld-solvent_sld)*form_volume(radius, length); 31 real total = REAL(0.0); 18 32 // real lower=0, upper=M_PI_2; 19 33 for (int i=0; i<76 ;i++) { 20 34 // translate a point in [-1,1] to a point in [lower,upper] 21 //const real zi = ( Gauss76Z[i]*(upper-lower) + upper + lower )/2.0; 22 const real zi = REAL(0.5)*(Gauss76Z[i]*M_PI_2 + M_PI_2); 23 summ += Gauss76Wt[i] * CylKernel(q, radius, halflength, zi); 35 //const real alpha = ( Gauss76Z[i]*(upper-lower) + upper + lower )/2.0; 36 const real alpha = REAL(0.5)*(Gauss76Z[i]*M_PI_2 + M_PI_2); 37 real sn, cn; 38 SINCOS(alpha, sn, cn); 39 const real fq = _cyl(twovd, qr*sn, qh*cn); 40 total += Gauss76Wt[i] * fq * fq * sn; 24 41 } 25 42 // translate dx in [-1,1] to dx in [lower,upper] 26 //const real form = (upper-lower)/2.0*summ; 27 const real form = summ * M_PI_4; 28 29 // Multiply by contrast^2, normalize by cylinder volume and convert to cm-1 30 // NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl 31 // The additional volume factor is for polydisperse volume normalization. 32 const real s = (sld - solvent_sld) * form_volume(radius, length); 33 return REAL(1.0e-4) * form * s * s; 43 //const real form = (upper-lower)/2.0*total; 44 return REAL(1.0e-4) * total * M_PI_4; 34 45 } 35 46 … … 43 54 real phi) 44 55 { 56 // TODO: check that radius<0 and length<0 give zero scattering. 57 // This should be the case since the polydispersity weight vector should 58 // be zero length, and this function never called. 45 59 real sn, cn; // slots to hold sincos function output 46 60 … … 54 68 const real alpha = acos(cos_val); 55 69 56 // The following is CylKernel() / sin(alpha), but we are doing it in place 57 // to avoid sin(alpha)/sin(alpha) for alpha = 0. It is also a teensy bit 58 // faster since we don't mulitply and divide sin(alpha). 70 const real twovd = REAL(2.0)*(sld-solvent_sld)*form_volume(radius, length); 59 71 SINCOS(alpha, sn, cn); 60 const real besarg = q*radius*sn; 61 const real siarg = REAL(0.5)*q*length*cn; 62 // lim_{x->0} J1(x)/x = 1/2, lim_{x->0} sin(x)/x = 1 63 const real bj = (besarg == REAL(0.0) ? REAL(0.5) : J1(besarg)/besarg); 64 const real si = (siarg == REAL(0.0) ? REAL(1.0) : sin(siarg)/siarg); 65 const real form = REAL(4.0)*bj*bj*si*si; 66 67 // Multiply by contrast^2, normalize by cylinder volume and convert to cm-1 68 // NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl 69 // The additional volume factor is for polydisperse volume normalization. 70 const real s = (sld - solvent_sld) * form_volume(radius, length); 71 return REAL(1.0e-4) * form * s * s; // * correction; 72 const real fq = _cyl(twovd, q*radius*sn, q*REAL(0.5)*length*cn); 73 return REAL(1.0e-4) * fq * fq; // * correction; 72 74 } -
sasmodels/models/cylinder.py
ra7684e5 r5d4777d 1 # cylinder model 1 2 # Note: model title and parameter table are inserted automatically 2 3 r""" … … 31 32 To provide easy access to the orientation of the cylinder, we define the 32 33 axis of the cylinder using two angles $\theta$ and $\phi$. Those angles 33 are defined in Figure :num:`figure # CylinderModel-orientation`.34 are defined in Figure :num:`figure #cylinder-orientation`. 34 35 35 .. _ CylinderModel-orientation:36 .. _cylinder-orientation: 36 37 37 38 .. figure:: img/image061.JPG (should be img/cylinder-1.jpg, or img/cylinder-orientation.jpg) … … 64 65 Validation of our code was done by comparing the output of the 1D model 65 66 to the output of the software provided by the NIST (Kline, 2006). 66 Figure :num:`figure # CylinderModel-compare` shows a comparison of67 Figure :num:`figure #cylinder-compare` shows a comparison of 67 68 the 1D output of our model and the output of the NIST software. 68 69 69 .. _ CylinderModel-compare:70 .. _cylinder-compare: 70 71 71 72 .. figure:: img/image065.JPG … … 91 92 the intensity for fully oriented cylinders, we can compare the result of 92 93 averaging our 2D output using a uniform distribution $p(\theta, \phi) = 1.0$. 93 Figure :num:`figure # CylinderModel-crosscheck` shows the result of94 Figure :num:`figure #cylinder-crosscheck` shows the result of 94 95 such a cross-check. 95 96 96 .. _ CylinderModel-crosscheck:97 .. _cylinder-crosscheck: 97 98 98 99 .. figure:: img/image066.JPG … … 111 112 title = "Right circular cylinder with uniform scattering length density." 112 113 description = """ 113 f(q)= 2*(sldCyl - sldSolv)*V*sin(qLcos(alpha/2))114 P(q)= 2*(sld - solvent_sld)*V*sin(qLcos(alpha/2)) 114 115 /[qLcos(alpha/2)]*J1(qRsin(alpha/2))/[qRsin(alpha)] 115 116 … … 119 120 L: Length of the cylinder 120 121 J1: The bessel function 121 alpha: angle between the axis of the122 alpha: angle between the axis of the 122 123 cylinder and the q-vector for 1D 123 124 :the ouput is P(q)=scale/V*integral 124 125 from pi/2 to zero of... 125 f(q)^(2)*sin(alpha)*dalpha + bkg126 126 f(q)^(2)*sin(alpha)*dalpha + background 127 """ 127 128 128 129 parameters = [ … … 143 144 ] 144 145 145 source = [ "lib/J1.c", "lib/gauss76.c", " lib/cylkernel.c", "cylinder.c"]146 source = [ "lib/J1.c", "lib/gauss76.c", "cylinder.c" ] 146 147 147 148 def ER(radius, length): -
sasmodels/models/cylinder_clone.c
rff7119b r5d4777d 2 2 real Iq(real q, real sld, real solvent_sld, real radius, real length); 3 3 real Iqxy(real qx, real qy, real sld, real solvent_sld, real radius, real length, real theta, real phi); 4 5 6 // twovd = 2 * volume * delta_rho 7 // besarg = q * R * sin(alpha) 8 // siarg = q * L/2 * cos(alpha) 9 real _cyl(real twovd, real besarg, real siarg, real alpha); 10 real _cyl(real twovd, real besarg, real siarg, real alpha) 11 { 12 const real bj = (besarg == REAL(0.0) ? REAL(0.5) : J1(besarg)/besarg); 13 const real si = (siarg == REAL(0.0) ? REAL(1.0) : sin(siarg)/siarg); 14 return twovd*si*bj; 15 } 4 16 5 17 real form_volume(real radius, real length) … … 7 19 return M_PI*radius*radius*length; 8 20 } 9 10 21 real Iq(real q, 11 22 real sldCyl, … … 14 25 real length) 15 26 { 16 const real h = REAL(0.5)*length; 17 real summ = REAL(0.0); 27 const real qr = q*radius; 28 const real qh = q*REAL(0.5)*length; 29 const real twovd = REAL(2.0)*(sldCyl-sldSolv)*form_volume(radius, length); 30 real total = REAL(0.0); 31 // real lower=0, upper=M_PI_2; 18 32 for (int i=0; i<76 ;i++) { 19 //const real zi = ( Gauss76Z[i]*(uplim-lolim) + uplim + lolim )/2.0; 20 const real zi = REAL(0.5)*(Gauss76Z[i]*M_PI_2 + M_PI_2); 21 summ += Gauss76Wt[i] * CylKernel(q, radius, h, zi); 33 // translate a point in [-1,1] to a point in [lower,upper] 34 //const real alpha = ( Gauss76Z[i]*(upper-lower) + upper + lower )/2.0; 35 const real alpha = REAL(0.5)*(Gauss76Z[i]*M_PI_2 + M_PI_2); 36 real sn, cn; 37 SINCOS(alpha, sn, cn); 38 const real fq = _cyl(twovd, qr*sn, qh*cn, alpha); 39 total += Gauss76Wt[i] * fq * fq * sn; 22 40 } 23 //const real form = (uplim-lolim)/2.0*summ; 24 const real form = summ * M_PI_4; 25 26 // Multiply by contrast^2, normalize by cylinder volume and convert to cm-1 27 // NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl 28 // The additional volume factor is for polydisperse volume normalization. 29 const real s = (sldCyl - sldSolv) * form_volume(radius, length); 30 return REAL(1.0e8) * form * s * s; 41 // translate dx in [-1,1] to dx in [lower,upper] 42 //const real form = (upper-lower)/2.0*total; 43 return REAL(1.0e8) * total * M_PI_4; 31 44 } 32 45 … … 50 63 const real alpha = acos(cos_val); 51 64 52 // The following is CylKernel() / sin(alpha), but we are doing it in place53 // to avoid sin(alpha)/sin(alpha) for alpha = 0. It is also a teensy bit54 // faster since we don't mulitply and divide sin(alpha).65 const real qr = q*radius; 66 const real qh = q*REAL(0.5)*length; 67 const real twovd = REAL(2.0)*(sldCyl-sldSolv)*form_volume(radius, length); 55 68 SINCOS(alpha, sn, cn); 56 const real besarg = q*radius*sn; 57 const real siarg = REAL(0.5)*q*length*cn; 58 // lim_{x->0} J1(x)/x = 1/2, lim_{x->0} sin(x)/x = 1 59 const real bj = (besarg == REAL(0.0) ? REAL(0.5) : J1(besarg)/besarg); 60 const real si = (siarg == REAL(0.0) ? REAL(1.0) : sin(siarg)/siarg); 61 const real form = REAL(4.0)*bj*bj*si*si; 62 63 // Multiply by contrast^2, normalize by cylinder volume and convert to cm-1 64 // NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl 65 // The additional volume factor is for polydisperse volume normalization. 66 const real s = (sldCyl - sldSolv) * form_volume(radius, length); 67 return REAL(1.0e8) * form * s * s * spherical_integration; 69 const real fq = _cyl(twovd, qr*sn, qh*cn, alpha); 70 return REAL(1.0e8) * fq * fq * spherical_integration; 68 71 } -
sasmodels/models/cylinder_clone.py
ra7684e5 r5d4777d 150 150 "Out of plane angle" ], 151 151 ] 152 source = [ "lib/J1.c", "lib/gauss76.c", " lib/cylkernel.c", "cylinder_clone.c"]152 source = [ "lib/J1.c", "lib/gauss76.c", "cylinder_clone.c" ] 153 153 154 154 def ER(radius, length): -
sasmodels/models/cylinder_onefile.py
ra7684e5 r5d4777d 154 154 source = [ "lib/J1.c", "lib/gauss76.c", "lib/cylkernel.c" ] 155 155 156 c_form_volume = """156 form_volume = """ 157 157 return M_PI*radius*radius*length; 158 """159 160 c_Iq = """158 """ 159 160 Iq = """ 161 161 const real h = REAL(0.5)*length; 162 162 real summ = REAL(0.0); … … 174 174 const real s = (sld - solvent_sld) * form_volume(radius, length); 175 175 return REAL(1.0e-4) * form * s * s; 176 """177 178 c_Iqxy = """176 """ 177 178 Iqxy = """ 179 179 real sn, cn; // slots to hold sincos function output 180 180 … … 204 204 const real s = (sld - solvent_sld) * form_volume(radius, length); 205 205 return REAL(1.0e-4) * form * s * s; // * correction; 206 """206 """ 207 207 208 208 def ER(radius, length): -
sasmodels/models/ellipsoid.c
rce27e21 r5d4777d 1 /* PARAMETERS 1 real form_volume(real rpolar, real requatorial); 2 real Iq(real q, real sld, real solvent_sld, real rpolar, real requatorial); 3 real Iqxy(real qx, real qy, real sld, real solvent_sld, 4 real rpolar, real requatorial, real theta, real phi); 5 6 real _ellipsoid_kernel(real q, real rpolar, real requatorial, real cos_alpha); 7 real _ellipsoid_kernel(real q, real rpolar, real requatorial, real cos_alpha) 2 8 { 3 name: "ellipsoid", 4 title: "Ellipsoid with uniform scattering length density", 5 include: [ "lib/gauss76.c" ], 6 parameters: [ 7 // [ "name", "units", default, [lower, upper], "type", "description" ], 8 [ "sld", "1e-6/Ang^2", 4, [-Infinity,Infinity], "", 9 "Cylinder scattering length density" ], 10 [ "solvent_sld", "1e-6/Ang^2", 1, [-Infinity,Infinity], "", 11 "Solvent scattering length density" ], 12 [ "a", "Ang", 20, [0, Infinity], "volume", 13 "Cylinder radius" ], 14 [ "b", "Ang", 20, [0, Infinity], "volume", 15 "Cylinder length" ], 16 [ "theta", "degrees", 60, [-Infinity, Infinity], "orientation", 17 "In plane angle" ], 18 [ "phi", "degrees", 60, [-Infinity, Infinity], "orientation", 19 "Out of plane angle" ], 20 ], 21 } 22 PARAMETERS END 23 24 DOCUMENTATION 25 .. _EllipseModel: 26 27 DOCUMENTATION END 28 */ 29 30 real form_volume(real a, real b); 31 real Iq(real qx, real qy, real sld, real solvent_sld, real a, real b); 32 real Iqxy(real qx, real qy, real sld, real solvent_sld, real a, real b, real theta, real phi); 33 34 real form_volume(real a, real b) 35 { 36 return REAL(1.333333333333333)*M_PI_2*a*b*b; 9 real sn, cn; 10 real ratio = rpolar/requatorial; 11 const real u = q*requatorial*sqrt(REAL(1.0) 12 + cos_alpha*cos_alpha*(ratio*ratio - REAL(1.0))); 13 SINCOS(u, sn, cn); 14 const real f = ( u==REAL(0.0) ? REAL(1.0) : REAL(3.0)*(sn-u*cn)/(u*u*u) ); 15 return f*f; 37 16 } 38 17 39 real ellipsoid_kernel(double q, double b, double a, double dum)18 real form_volume(real rpolar, real requatorial) 40 19 { 41 real sn, cn; 42 const real nu = a/b; 43 const real arg = q * b * sqrt(REAL(1.0)+(dum*dum*(nu*nu--REAL(1.0)))); 44 SINCOS(arg, sn, cn); 45 const real f = (arg==REAL(0.0) ? REAL(1.0) : REAL(3.0)*(sn-arg*cn)/(arg*arg*arg); 46 return f*f; 20 return REAL(1.333333333333333)*M_PI*rpolar*requatorial*requatorial; 47 21 } 48 22 … … 50 24 real sld, 51 25 real solvent_sld, 52 real a,53 real b)26 real rpolar, 27 real requatorial) 54 28 { 55 real summ = REAL(0.0); 29 //const real lower = REAL(0.0); 30 //const real upper = REAL(1.0); 31 real total = REAL(0.0); 56 32 for (int i=0;i<76;i++) { 57 //const real zi = ( Gauss76Z[i]*(uplim-lolim) + uplim + lolim )/2.0;58 zi = ( Gauss76Z[i] + REAL(1.0))/REAL(2.0);59 summ += Gauss76Wt[i] * ellipsoid_kernel(q, b, a, zi);33 //const real cos_alpha = (Gauss76Z[i]*(upper-lower) + upper + lower)/2; 34 const real cos_alpha = REAL(0.5)*(Gauss76Z[i] + REAL(1.0)); 35 total += Gauss76Wt[i] * _ellipsoid_kernel(q, rpolar, requatorial, cos_alpha); 60 36 } 61 //const real form = (up lim-lolim)/2.0*summ;62 const real form = REAL(0.5)* summ63 const real s = (sld - s ld_solvent) * form_volume(a, b);37 //const real form = (upper-lower)/2*total; 38 const real form = REAL(0.5)*total; 39 const real s = (sld - solvent_sld) * form_volume(rpolar, requatorial); 64 40 return REAL(1.0e-4) * form * s * s; 65 41 } … … 68 44 real sld, 69 45 real solvent_sld, 70 real a,71 real b,46 real rpolar, 47 real requatorial, 72 48 real theta, 73 49 real phi) … … 77 53 const real q = sqrt(qx*qx + qy*qy); 78 54 SINCOS(theta*M_PI_180, sn, cn); 79 const real cos_ val= cn*cos(phi*M_PI_180)*(qx/q) + sn*(qy/q);80 const real form = ellipsoid_kernel(q, b, a, cos_val);81 const real s = (sld - solvent_sld) * form_volume( a, b);55 const real cos_alpha = cn*cos(phi*M_PI_180)*(qx/q) + sn*(qy/q); 56 const real form = _ellipsoid_kernel(q, rpolar, requatorial, cos_alpha); 57 const real s = (sld - solvent_sld) * form_volume(rpolar, requatorial); 82 58 83 59 return REAL(1.0e-4) * form * s * s; -
sasmodels/sasview_model.py
rff7119b r5d4777d 6 6 7 7 try: 8 import pyopencl9 from .gen import opencl_model as load_model 10 except ImportError: 8 from .gpu import load_model 9 except ImportError,exc: 10 warnings.warn(str(exc)) 11 11 warnings.warn("OpenCL not available --- using ctypes instead") 12 from . gen import dll_model asload_model12 from .dll import load_model 13 13 14 14 … … 246 246 # Check whether we have a list of ndarrays [qx,qy] 247 247 qx, qy = qdist 248 return self.calculate_Iq(qx, qy) 248 partype = self._model.info['partype'] 249 if not partype['orientation'] and not partype['magnetic']: 250 return self.calculate_Iq(np.sqrt(qx**2+qy**2)) 251 else: 252 return self.calculate_Iq(qx, qy) 249 253 250 254 elif isinstance(qdist, np.ndarray): -
sasmodels/weights.py
rff7119b r5d4777d 42 42 """ 43 43 sigma = self.width * center if relative else self.width 44 if sigma == 0: 45 return np.array([center], 'd'), np.array([1.], 'd') 44 if sigma == 0 or self.npts < 2: 45 if lb <= center <= ub: 46 return np.array([center], 'd'), np.array([1.], 'd') 47 else: 48 return np.array([], 'd'), np.array([], 'd') 46 49 return self._weights(center, sigma, lb, ub) 47 50
Note: See TracChangeset
for help on using the changeset viewer.