Changeset 994d77f in sasmodels for sasmodels/gen.py
- Timestamp:
- Oct 30, 2014 10:33:53 AM (9 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:
- ef2861b
- Parents:
- d087487b
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/gen.py
r78356b31 r994d77f 37 37 order so that functions are defined before they are used. 38 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.] 39 Floating point values should be declared as *double*. For single precision 40 calculations, *double* will be replaced by *float*. The single precision 41 conversion will also tag floating point constants with "f" to make them 42 single precision constants. When using integral values in floating point 43 expressions, they should be expressed as floating point values by including 44 a decimal point. This includes 0., 1. and 2. 49 45 50 46 OpenCL has a *sincos* function which can improve performance when both … … 52 48 this function does not exist in C99, all use of *sincos* should be 53 49 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. 50 previously declared *double* variables. When compiled for systems without 51 OpenCL, *SINCOS* will be replaced by *sin* and *cos* calls. If *value* is 52 an expression, it will appear twice in this case; whether or not it will be 53 evaluated twice depends on the quality of the compiler. 57 54 58 55 If the input parameters are invalid, the scattering calculator should … … 172 169 # TODO: identify model files which have changed since loading and reload them. 173 170 174 __all__ = ["make, doc", "sources" ]171 __all__ = ["make, doc", "sources", "use_single"] 175 172 176 173 import sys 177 174 import os 178 175 import os.path 176 import re 179 177 180 178 import numpy as np … … 229 227 #ifndef USE_OPENCL 230 228 # include <math.h> 231 # define REAL(x) (x)232 # ifndef real233 # define real double234 # endif235 229 # define global 236 230 # define local … … 253 247 // is not enabled on the card, which is why these constants may be missing 254 248 #ifndef M_PI 255 # define M_PI REAL(3.141592653589793)249 # define M_PI 3.141592653589793 256 250 #endif 257 251 #ifndef M_PI_2 258 # define M_PI_2 REAL(1.570796326794897)252 # define M_PI_2 1.570796326794897 259 253 #endif 260 254 #ifndef M_PI_4 261 # define M_PI_4 REAL(0.7853981633974483)255 # define M_PI_4 0.7853981633974483 262 256 #endif 263 257 264 258 // Non-standard pi/180, used for converting between degrees and radians 265 259 #ifndef M_PI_180 266 # define M_PI_180 REAL(0.017453292519943295)260 # define M_PI_180 0.017453292519943295 267 261 #endif 268 262 """ … … 274 268 KERNEL_1D = { 275 269 'fn': "Iq", 276 'q_par_decl': "global const real*q,",277 'qinit': "const realqi = q[i];",270 'q_par_decl': "global const double *q,", 271 'qinit': "const double qi = q[i];", 278 272 'qcall': "qi", 279 273 'qwork': ["q"], … … 282 276 KERNEL_2D = { 283 277 'fn': "Iqxy", 284 'q_par_decl': "global const real *qx,\n global const real*qy,",285 'qinit': "const real qxi = qx[i];\n const realqyi = qy[i];",278 'q_par_decl': "global const double *qx,\n global const double *qy,", 279 'qinit': "const double qxi = qx[i];\n const double qyi = qy[i];", 286 280 'qcall': "qxi, qyi", 287 281 'qwork': ["qx", "qy"], … … 296 290 kernel void %(name)s( 297 291 %(q_par_decl)s 298 global real*result,292 global double *result, 299 293 #ifdef USE_OPENCL 300 global real*loops_g,294 global double *loops_g, 301 295 #else 302 296 const int Nq, 303 297 #endif 304 local real*loops,305 const realcutoff,298 local double *loops, 299 const double cutoff, 306 300 %(par_decl)s 307 301 ) … … 324 318 { 325 319 %(qinit)s 326 real ret=REAL(0.0), norm=REAL(0.0);327 real vol=REAL(0.0), norm_vol=REAL(0.0);320 double ret=0.0, norm=0.0; 321 double vol=0.0, norm_vol=0.0; 328 322 %(loops)s 329 if (vol*norm_vol != REAL(0.0)) {323 if (vol*norm_vol != 0.0) { 330 324 ret *= norm_vol/vol; 331 325 } … … 340 334 LOOP_OPEN="""\ 341 335 for (int %(name)s_i=0; %(name)s_i < N%(name)s; %(name)s_i++) { 342 const real%(name)s = loops[2*(%(name)s_i%(offset)s)];343 const real%(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\336 const double %(name)s = loops[2*(%(name)s_i%(offset)s)]; 337 const double %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\ 344 338 """ 345 339 … … 349 343 # it will also be added here. 350 344 LOOP_BODY="""\ 351 const realweight = %(weight_product)s;345 const double weight = %(weight_product)s; 352 346 if (weight > cutoff) { 353 const realI = %(fn)s(%(qcall)s, %(pcall)s);354 if (I>= REAL(0.0)) { // scattering cannot be negative347 const double I = %(fn)s(%(qcall)s, %(pcall)s); 348 if (I>=0.0) { // scattering cannot be negative 355 349 ret += weight*I%(sasview_spherical)s; 356 350 norm += weight; … … 365 359 SPHERICAL_CORRECTION="""\ 366 360 // Correction factor for spherical integration p(theta) I(q) sin(theta) dtheta 367 real spherical_correction = (Ntheta>1 ? fabs(sin(M_PI_180*theta)) : REAL(1.0));\361 double spherical_correction = (Ntheta>1 ? fabs(sin(M_PI_180*theta)) : 1.0);\ 368 362 """ 369 363 # Use this to reproduce sasview behaviour 370 364 SASVIEW_SPHERICAL_CORRECTION="""\ 371 365 // Correction factor for spherical integration p(theta) I(q) sin(theta) dtheta 372 real spherical_correction = (Ntheta>1 ? fabs(cos(M_PI_180*theta))*M_PI_2 : REAL(1.0));\366 double spherical_correction = (Ntheta>1 ? fabs(cos(M_PI_180*theta))*M_PI_2 : 1.0);\ 373 367 """ 374 368 … … 377 371 # to call the form_volume function from the user supplied kernel, and accumulate 378 372 # a normalized weight. 379 VOLUME_NORM="""const realvol_weight = %(weight)s;373 VOLUME_NORM="""const double vol_weight = %(weight)s; 380 374 vol += vol_weight*form_volume(%(pars)s); 381 375 norm_vol += vol_weight;\ … … 384 378 # functions defined as strings in the .py module 385 379 WORK_FUNCTION="""\ 386 real%(name)s(%(pars)s);387 real%(name)s(%(pars)s)380 double %(name)s(%(pars)s); 381 double %(name)s(%(pars)s) 388 382 { 389 383 %(body)s … … 422 416 """ 423 417 return info['name'] + "_" + ("Iqxy" if is_2D else "Iq") 418 419 420 def use_single(source): 421 """ 422 Convert code from double precision to single precision. 423 """ 424 source = re.sub(r'(^|[^a-zA-Z0-9_])double($|[^a-zA-Z0-9_])', 425 r'\1float\2', source) 426 source = re.sub(r'[^a-zA-Z_](\d*[.]\d+|\d+[.]\d*)([eE][+-]?\d+)?', 427 r'\g<0>f', source) 428 return source 424 429 425 430 … … 503 508 # declarations for non-pd followed by pd pars 504 509 # e.g., 505 # const realsld,510 # const double sld, 506 511 # const int Nradius 507 fixed_par_decl = ",\n ".join("const real%s"%p for p in fixed_pars)512 fixed_par_decl = ",\n ".join("const double %s"%p for p in fixed_pars) 508 513 pd_par_decl = ",\n ".join("const int N%s"%p for p in pd_pars) 509 514 if fixed_par_decl and pd_par_decl: … … 518 523 # kernel name is, e.g., cylinder_Iq 519 524 'name': kernel_name(info, is_2D), 520 # to declare, e.g., global realq[],525 # to declare, e.g., global double q[], 521 526 'q_par_decl': q_pars['q_par_decl'], 522 # to declare, e.g., realsld, int Nradius, int Nlength527 # to declare, e.g., double sld, int Nradius, int Nlength 523 528 'par_decl': par_decl, 524 529 # to copy global to local pd pars we need, e.g., Nradius+Nlength 525 530 'pd_length': "+".join('N'+p for p in pd_pars), 526 # the q initializers, e.g., realqi = q[i];531 # the q initializers, e.g., double qi = q[i]; 527 532 'qinit': q_pars['qinit'], 528 533 # the actual polydispersity loop … … 537 542 subst = { 538 543 'name': fn, 539 'pars': ", ".join(" real"+p for p in q_pars['qwork']+fq_pars),544 'pars': ", ".join("double "+p for p in q_pars['qwork']+fq_pars), 540 545 'body': info[fn], 541 546 } … … 607 612 subst = { 608 613 'name': "form_volume", 609 'pars': ", ".join(" real"+p for p in info['partype']['volume']),614 'pars': ", ".join("double "+p for p in info['partype']['volume']), 610 615 'body': info['form_volume'], 611 616 }
Note: See TracChangeset
for help on using the changeset viewer.