Changeset 994d77f in sasmodels for sasmodels/gen.py


Ignore:
Timestamp:
Oct 30, 2014 10:33:53 AM (9 years ago)
Author:
Paul Kienzle <pkienzle@…>
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
Message:

Convert double to float rather than using real

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/gen.py

    r78356b31 r994d77f  
    3737order so that functions are defined before they are used. 
    3838 
    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.] 
     39Floating point values should be declared as *double*.  For single precision 
     40calculations, *double* will be replaced by *float*.  The single precision 
     41conversion will also tag floating point constants with "f" to make them 
     42single precision constants.  When using integral values in floating point 
     43expressions, they should be expressed as floating point values by including 
     44a decimal point.  This includes 0., 1. and 2. 
    4945 
    5046OpenCL has a *sincos* function which can improve performance when both 
     
    5248this function does not exist in C99, all use of *sincos* should be 
    5349replaced 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. 
     50previously declared *double* variables.  When compiled for systems without 
     51OpenCL, *SINCOS* will be replaced by *sin* and *cos* calls.   If *value* is 
     52an expression, it will appear twice in this case; whether or not it will be 
     53evaluated twice depends on the quality of the compiler. 
    5754 
    5855If the input parameters are invalid, the scattering calculator should 
     
    172169# TODO: identify model files which have changed since loading and reload them. 
    173170 
    174 __all__ = ["make, doc", "sources"] 
     171__all__ = ["make, doc", "sources", "use_single"] 
    175172 
    176173import sys 
    177174import os 
    178175import os.path 
     176import re 
    179177 
    180178import numpy as np 
     
    229227#ifndef USE_OPENCL 
    230228#  include <math.h> 
    231 #  define REAL(x) (x) 
    232 #  ifndef real 
    233 #      define real double 
    234 #  endif 
    235229#  define global 
    236230#  define local 
     
    253247// is not enabled on the card, which is why these constants may be missing 
    254248#ifndef M_PI 
    255 #  define M_PI REAL(3.141592653589793) 
     249#  define M_PI 3.141592653589793 
    256250#endif 
    257251#ifndef M_PI_2 
    258 #  define M_PI_2 REAL(1.570796326794897) 
     252#  define M_PI_2 1.570796326794897 
    259253#endif 
    260254#ifndef M_PI_4 
    261 #  define M_PI_4 REAL(0.7853981633974483) 
     255#  define M_PI_4 0.7853981633974483 
    262256#endif 
    263257 
    264258// Non-standard pi/180, used for converting between degrees and radians 
    265259#ifndef M_PI_180 
    266 #  define M_PI_180 REAL(0.017453292519943295) 
     260#  define M_PI_180 0.017453292519943295 
    267261#endif 
    268262""" 
     
    274268KERNEL_1D = { 
    275269    'fn': "Iq", 
    276     'q_par_decl': "global const real *q,", 
    277     'qinit': "const real qi = q[i];", 
     270    'q_par_decl': "global const double *q,", 
     271    'qinit': "const double qi = q[i];", 
    278272    'qcall': "qi", 
    279273    'qwork': ["q"], 
     
    282276KERNEL_2D = { 
    283277    'fn': "Iqxy", 
    284     'q_par_decl': "global const real *qx,\n    global const real *qy,", 
    285     'qinit': "const real qxi = qx[i];\n    const real qyi = 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];", 
    286280    'qcall': "qxi, qyi", 
    287281    'qwork': ["qx", "qy"], 
     
    296290kernel void %(name)s( 
    297291    %(q_par_decl)s 
    298     global real *result, 
     292    global double *result, 
    299293#ifdef USE_OPENCL 
    300     global real *loops_g, 
     294    global double *loops_g, 
    301295#else 
    302296    const int Nq, 
    303297#endif 
    304     local real *loops, 
    305     const real cutoff, 
     298    local double *loops, 
     299    const double cutoff, 
    306300    %(par_decl)s 
    307301    ) 
     
    324318  { 
    325319    %(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; 
    328322%(loops)s 
    329     if (vol*norm_vol != REAL(0.0)) { 
     323    if (vol*norm_vol != 0.0) { 
    330324      ret *= norm_vol/vol; 
    331325    } 
     
    340334LOOP_OPEN="""\ 
    341335for (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];\ 
    344338""" 
    345339 
     
    349343# it will also be added here. 
    350344LOOP_BODY="""\ 
    351 const real weight = %(weight_product)s; 
     345const double weight = %(weight_product)s; 
    352346if (weight > cutoff) { 
    353   const real I = %(fn)s(%(qcall)s, %(pcall)s); 
    354   if (I>=REAL(0.0)) { // scattering cannot be negative 
     347  const double I = %(fn)s(%(qcall)s, %(pcall)s); 
     348  if (I>=0.0) { // scattering cannot be negative 
    355349    ret += weight*I%(sasview_spherical)s; 
    356350    norm += weight; 
     
    365359SPHERICAL_CORRECTION="""\ 
    366360// 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));\ 
     361double spherical_correction = (Ntheta>1 ? fabs(sin(M_PI_180*theta)) : 1.0);\ 
    368362""" 
    369363# Use this to reproduce sasview behaviour 
    370364SASVIEW_SPHERICAL_CORRECTION="""\ 
    371365// 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));\ 
     366double spherical_correction = (Ntheta>1 ? fabs(cos(M_PI_180*theta))*M_PI_2 : 1.0);\ 
    373367""" 
    374368 
     
    377371# to call the form_volume function from the user supplied kernel, and accumulate 
    378372# a normalized weight. 
    379 VOLUME_NORM="""const real vol_weight = %(weight)s; 
     373VOLUME_NORM="""const double vol_weight = %(weight)s; 
    380374    vol += vol_weight*form_volume(%(pars)s); 
    381375    norm_vol += vol_weight;\ 
     
    384378# functions defined as strings in the .py module 
    385379WORK_FUNCTION="""\ 
    386 real %(name)s(%(pars)s); 
    387 real %(name)s(%(pars)s) 
     380double %(name)s(%(pars)s); 
     381double %(name)s(%(pars)s) 
    388382{ 
    389383%(body)s 
     
    422416    """ 
    423417    return info['name'] + "_" + ("Iqxy" if is_2D else "Iq") 
     418 
     419 
     420def 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 
    424429 
    425430 
     
    503508    # declarations for non-pd followed by pd pars 
    504509    # e.g., 
    505     #     const real sld, 
     510    #     const double sld, 
    506511    #     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) 
    508513    pd_par_decl = ",\n    ".join("const int N%s"%p for p in pd_pars) 
    509514    if fixed_par_decl and pd_par_decl: 
     
    518523        # kernel name is, e.g., cylinder_Iq 
    519524        'name': kernel_name(info, is_2D), 
    520         # to declare, e.g., global real q[], 
     525        # to declare, e.g., global double q[], 
    521526        'q_par_decl': q_pars['q_par_decl'], 
    522         # to declare, e.g., real sld, int Nradius, int Nlength 
     527        # to declare, e.g., double sld, int Nradius, int Nlength 
    523528        'par_decl': par_decl, 
    524529        # to copy global to local pd pars we need, e.g., Nradius+Nlength 
    525530        'pd_length': "+".join('N'+p for p in pd_pars), 
    526         # the q initializers, e.g., real qi = q[i]; 
     531        # the q initializers, e.g., double qi = q[i]; 
    527532        'qinit': q_pars['qinit'], 
    528533        # the actual polydispersity loop 
     
    537542        subst = { 
    538543            '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), 
    540545            'body': info[fn], 
    541546            } 
     
    607612        subst = { 
    608613            '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']), 
    610615            'body': info['form_volume'], 
    611616            } 
Note: See TracChangeset for help on using the changeset viewer.