Changeset b968fe8 in sasmodels


Ignore:
Timestamp:
Jan 22, 2016 3:55:40 AM (8 years ago)
Author:
piotr
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:
61fd21d
Parents:
55b283e8 (diff), 608e31e (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.
Message:

Merge remote-tracking branch 'origin/master'

Location:
sasmodels
Files:
3 added
4 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/compare.py

    r82c299f r608e31e  
    11#!/usr/bin/env python 
    22# -*- coding: utf-8 -*- 
     3""" 
     4Program to compare models using different compute engines. 
     5 
     6This program lets you compare results between OpenCL and DLL versions 
     7of the code and between precision (half, fast, single, double, quad), 
     8where fast precision is single precision using native functions for 
     9trig, etc., and may not be completely IEEE 754 compliant.  This lets 
     10make sure that the model calculations are stable, or if you need to 
     11tag the model as double precision only. 
     12 
     13Run using ./compare.sh (Linux, Mac) or compare.bat (Windows) in the 
     14sasmodels root to see the command line options. 
     15 
     16Note that there is no way within sasmodels to select between an 
     17OpenCL CPU device and a GPU device, but you can do so by setting the 
     18PYOPENCL_CTX environment variable ahead of time.  Start a python 
     19interpreter and enter:: 
     20 
     21    import pyopencl as cl 
     22    cl.create_some_context() 
     23 
     24This will prompt you to select from the available OpenCL devices 
     25and tell you which string to use for the PYOPENCL_CTX variable. 
     26On Windows you will need to remove the quotes. 
     27""" 
     28 
     29from __future__ import print_function 
     30 
     31USAGE = """ 
     32usage: compare.py model N1 N2 [options...] [key=val] 
     33 
     34Compare the speed and value for a model between the SasView original and the 
     35sasmodels rewrite. 
     36 
     37model is the name of the model to compare (see below). 
     38N1 is the number of times to run sasmodels (default=1). 
     39N2 is the number times to run sasview (default=1). 
     40 
     41Options (* for default): 
     42 
     43    -plot*/-noplot plots or suppress the plot of the model 
     44    -lowq*/-midq/-highq/-exq use q values up to 0.05, 0.2, 1.0, 10.0 
     45    -nq=128 sets the number of Q points in the data set 
     46    -1d*/-2d computes 1d or 2d data 
     47    -preset*/-random[=seed] preset or random parameters 
     48    -mono/-poly* force monodisperse/polydisperse 
     49    -cutoff=1e-5* cutoff value for including a point in polydispersity 
     50    -pars/-nopars* prints the parameter set or not 
     51    -abs/-rel* plot relative or absolute error 
     52    -linear/-log*/-q4 intensity scaling 
     53    -hist/-nohist* plot histogram of relative error 
     54    -res=0 sets the resolution width dQ/Q if calculating with resolution 
     55    -accuracy=Low accuracy of the resolution calculation Low, Mid, High, Xhigh 
     56    -edit starts the parameter explorer 
     57 
     58Any two calculation engines can be selected for comparison: 
     59 
     60    -single/-double/-half/-fast sets an OpenCL calculation engine 
     61    -single!/-double!/-quad! sets an OpenMP calculation engine 
     62    -sasview sets the sasview calculation engine 
     63 
     64The default is -single -sasview.  Note that the interpretation of quad 
     65precision depends on architecture, and may vary from 64-bit to 128-bit, 
     66with 80-bit floats being common (1e-19 precision). 
     67 
     68Key=value pairs allow you to set specific values for the model parameters. 
     69""" 
     70 
     71# Update docs with command line usage string.   This is separate from the usual 
     72# doc string so that we can display it at run time if there is an error. 
     73# lin 
     74__doc__ = __doc__ + """ 
     75Program description 
     76------------------- 
     77 
     78""" + USAGE 
     79 
     80 
    381 
    482import sys 
     
    25103# List of available models 
    26104MODELS = [basename(f)[:-3] 
    27           for f in sorted(glob.glob(joinpath(ROOT,"models","[a-zA-Z]*.py")))] 
     105          for f in sorted(glob.glob(joinpath(ROOT, "models", "[a-zA-Z]*.py")))] 
    28106 
    29107# CRUFT python 2.6 
     
    76154    elif p.endswith('_pd_type'): 
    77155        return v 
    78     elif any(s in p for s in ('theta','phi','psi')): 
     156    elif any(s in p for s in ('theta', 'phi', 'psi')): 
    79157        # orientation in [-180,180], orientation pd in [0,45] 
    80158        if p.endswith('_pd'): 
    81             return [0,45] 
     159            return [0, 45] 
    82160        else: 
    83161            return [-180, 180] 
     
    86164    elif p.endswith('_pd'): 
    87165        return [0, 1] 
    88     elif p in ['background', 'scale']: 
     166    elif p == 'background': 
     167        return [0, 10] 
     168    elif p == 'scale': 
    89169        return [0, 1e3] 
     170    elif p == 'case_num': 
     171        # RPA hack 
     172        return [0, 10] 
     173    elif v < 0: 
     174        # Kxy parameters in rpa model can be negative 
     175        return [2*v, -2*v] 
    90176    else: 
    91         return [0, (2*v if v>0 else 1)] 
     177        return [0, (2*v if v > 0 else 1)] 
    92178 
    93179def _randomize_one(p, v): 
    94180    """ 
    95     Randomizing parameter. 
    96     """ 
    97     if any(p.endswith(s) for s in ('_pd_n','_pd_nsigma','_pd_type')): 
     181    Randomize a single parameter. 
     182    """ 
     183    if any(p.endswith(s) for s in ('_pd_n', '_pd_nsigma', '_pd_type')): 
    98184        return v 
    99185    else: 
     
    101187 
    102188def randomize_pars(pars, seed=None): 
     189    """ 
     190    Generate random values for all of the parameters. 
     191 
     192    Valid ranges for the random number generator are guessed from the name of 
     193    the parameter; this will not account for constraints such as cap radius 
     194    greater than cylinder radius in the capped_cylinder model, so 
     195    :func:`constrain_pars` needs to be called afterward.. 
     196    """ 
    103197    np.random.seed(seed) 
    104198    # Note: the sort guarantees order `of calls to random number generator 
    105     pars = dict((p,_randomize_one(p,v)) 
    106                 for p,v in sorted(pars.items())) 
     199    pars = dict((p, _randomize_one(p, v)) 
     200                for p, v in sorted(pars.items())) 
    107201    return pars 
    108202 
     
    110204    """ 
    111205    Restrict parameters to valid values. 
     206 
     207    This includes model specific code for models such as capped_cylinder 
     208    which need to support within model constraints (cap radius more than 
     209    cylinder radius in this case). 
    112210    """ 
    113211    name = model_definition.name 
    114212    if name == 'capped_cylinder' and pars['cap_radius'] < pars['radius']: 
    115         pars['radius'],pars['cap_radius'] = pars['cap_radius'],pars['radius'] 
     213        pars['radius'], pars['cap_radius'] = pars['cap_radius'], pars['radius'] 
    116214    if name == 'barbell' and pars['bell_radius'] < pars['radius']: 
    117         pars['radius'],pars['bell_radius'] = pars['bell_radius'],pars['radius'] 
     215        pars['radius'], pars['bell_radius'] = pars['bell_radius'], pars['radius'] 
    118216 
    119217    # Limit guinier to an Rg such that Iq > 1e-30 (single precision cutoff) 
     
    122220        q_max = 1.0  # high q maximum 
    123221        rg_max = np.sqrt(90*np.log(10) + 3*np.log(pars['scale']))/q_max 
    124         pars['rg'] = min(pars['rg'],rg_max) 
     222        pars['rg'] = min(pars['rg'], rg_max) 
    125223 
    126224    if name == 'rpa': 
     
    136234 
    137235def parlist(pars): 
    138     return "\n".join("%s: %s"%(p,v) for p,v in sorted(pars.items())) 
     236    """ 
     237    Format the parameter list for printing. 
     238    """ 
     239    return "\n".join("%s: %s"%(p, v) for p, v in sorted(pars.items())) 
    139240 
    140241def suppress_pd(pars): 
     
    151252 
    152253def eval_sasview(model_definition, data): 
     254    """ 
     255    Return a model calculator using the SasView fitting engine. 
     256    """ 
    153257    # importing sas here so that the error message will be that sas failed to 
    154258    # import rather than the more obscure smear_selection not imported error 
     
    158262    # convert model parameters from sasmodel form to sasview form 
    159263    #print("old",sorted(pars.items())) 
    160     modelname, pars = revert_model(model_definition, {}) 
    161     #print("new",sorted(pars.items())) 
     264    modelname, _ = revert_model(model_definition, {}) 
     265    #print("new",sorted(_pars.items())) 
    162266    sas = __import__('sas.models.'+modelname) 
    163     ModelClass = getattr(getattr(sas.models,modelname,None),modelname,None) 
     267    ModelClass = getattr(getattr(sas.models, modelname, None), modelname, None) 
    164268    if ModelClass is None: 
    165269        raise ValueError("could not find model %r in sas.models"%modelname) 
     
    184288 
    185289    def calculator(**pars): 
     290        """ 
     291        Sasview calculator for model. 
     292        """ 
    186293        # paying for parameter conversion each time to keep life simple, if not fast 
    187294        _, pars = revert_model(model_definition, pars) 
    188         for k,v in pars.items(): 
     295        for k, v in pars.items(): 
    189296            parts = k.split('.')  # polydispersity components 
    190297            if len(parts) == 2: 
     
    209316} 
    210317def eval_opencl(model_definition, data, dtype='single', cutoff=0.): 
     318    """ 
     319    Return a model calculator using the OpenCL calculation engine. 
     320    """ 
    211321    try: 
    212322        model = core.load_model(model_definition, dtype=dtype, platform="ocl") 
     
    221331 
    222332def eval_ctypes(model_definition, data, dtype='double', cutoff=0.): 
    223     if dtype=='quad': 
     333    """ 
     334    Return a model calculator using the DLL calculation engine. 
     335    """ 
     336    if dtype == 'quad': 
    224337        dtype = 'longdouble' 
    225338    model = core.load_model(model_definition, dtype=dtype, platform="dll") 
     
    229342 
    230343def time_calculation(calculator, pars, Nevals=1): 
     344    """ 
     345    Compute the average calculation time over N evaluations. 
     346 
     347    An additional call is generated without polydispersity in order to 
     348    initialize the calculation engine, and make the average more stable. 
     349    """ 
    231350    # initialize the code so time is more accurate 
    232351    value = calculator(**suppress_pd(pars)) 
     
    238357 
    239358def make_data(opts): 
     359    """ 
     360    Generate an empty dataset, used with the model to set Q points 
     361    and resolution. 
     362 
     363    *opts* contains the options, with 'qmax', 'nq', 'res', 
     364    'accuracy', 'is2d' and 'view' parsed from the command line. 
     365    """ 
    240366    qmax, nq, res = opts['qmax'], opts['nq'], opts['res'] 
    241367    if opts['is2d']: 
     
    255381 
    256382def make_engine(model_definition, data, dtype, cutoff): 
     383    """ 
     384    Generate the appropriate calculation engine for the given datatype. 
     385 
     386    Datatypes with '!' appended are evaluated using external C DLLs rather 
     387    than OpenCL. 
     388    """ 
    257389    if dtype == 'sasview': 
    258390        return eval_sasview(model_definition, data) 
     
    265397 
    266398def compare(opts, limits=None): 
    267     Nbase, Ncomp = opts['N1'], opts['N2'] 
     399    """ 
     400    Preform a comparison using options from the command line. 
     401 
     402    *limits* are the limits on the values to use, either to set the y-axis 
     403    for 1D or to set the colormap scale for 2D.  If None, then they are 
     404    inferred from the data and returned. When exploring using Bumps, 
     405    the limits are set when the model is initially called, and maintained 
     406    as the values are adjusted, making it easier to see the effects of the 
     407    parameters. 
     408    """ 
     409    Nbase, Ncomp = opts['n1'], opts['n2'] 
    268410    pars = opts['pars'] 
    269411    data = opts['data'] 
     
    296438        resid = (base_value - comp_value) 
    297439        relerr = resid/comp_value 
    298         _print_stats("|%s - %s|"%(base.engine,comp.engine)+(" "*(3+len(comp.engine))), resid) 
    299         _print_stats("|(%s - %s) / %s|"%(base.engine,comp.engine,comp.engine), relerr) 
     440        _print_stats("|%s-%s|"%(base.engine, comp.engine) + (" "*(3+len(comp.engine))), 
     441                     resid) 
     442        _print_stats("|(%s-%s)/%s|"%(base.engine, comp.engine, comp.engine), 
     443                     relerr) 
    300444 
    301445    # Plot if requested 
     
    321465        if Nbase > 0: plt.subplot(132) 
    322466        plot_theory(data, comp_value, view=view, plot_data=False, limits=limits) 
    323         plt.title("%s t=%.1f ms"%(comp.engine,comp_time)) 
     467        plt.title("%s t=%.1f ms"%(comp.engine, comp_time)) 
    324468        #cbar_title = "log I" 
    325469    if Ncomp > 0 and Nbase > 0: 
    326470        plt.subplot(133) 
    327471        if '-abs' in opts: 
    328             err,errstr,errview = resid, "abs err", "linear" 
     472            err, errstr, errview = resid, "abs err", "linear" 
    329473        else: 
    330             err,errstr,errview = abs(relerr), "rel err", "log" 
     474            err, errstr, errview = abs(relerr), "rel err", "log" 
    331475        #err,errstr = base/comp,"ratio" 
    332476        plot_theory(data, None, resid=err, view=errview, plot_data=False) 
     
    340484        plt.figure() 
    341485        v = relerr 
    342         v[v==0] = 0.5*np.min(np.abs(v[v!=0])) 
    343         plt.hist(np.log10(np.abs(v)), normed=1, bins=50); 
    344         plt.xlabel('log10(err), err = |(%s - %s) / %s|'%(base.engine, comp.engine, comp.engine)); 
     486        v[v == 0] = 0.5*np.min(np.abs(v[v != 0])) 
     487        plt.hist(np.log10(np.abs(v)), normed=1, bins=50) 
     488        plt.xlabel('log10(err), err = |(%s - %s) / %s|' 
     489                   % (base.engine, comp.engine, comp.engine)) 
    345490        plt.ylabel('P(err)') 
    346491        plt.title('Distribution of relative error between calculation engines') 
     
    362507        "zero-offset:%+.3e"%np.mean(err), 
    363508        ] 
    364     print(label+"  ".join(data)) 
     509    print(label+"  "+"  ".join(data)) 
    365510 
    366511 
     
    368513# =========================================================================== 
    369514# 
    370 USAGE=""" 
    371 usage: compare.py model N1 N2 [options...] [key=val] 
    372  
    373 Compare the speed and value for a model between the SasView original and the 
    374 sasmodels rewrite. 
    375  
    376 model is the name of the model to compare (see below). 
    377 N1 is the number of times to run sasmodels (default=1). 
    378 N2 is the number times to run sasview (default=1). 
    379  
    380 Options (* for default): 
    381  
    382     -plot*/-noplot plots or suppress the plot of the model 
    383     -lowq*/-midq/-highq/-exq use q values up to 0.05, 0.2, 1.0, 10.0 
    384     -nq=128 sets the number of Q points in the data set 
    385     -1d*/-2d computes 1d or 2d data 
    386     -preset*/-random[=seed] preset or random parameters 
    387     -mono/-poly* force monodisperse/polydisperse 
    388     -cutoff=1e-5* cutoff value for including a point in polydispersity 
    389     -pars/-nopars* prints the parameter set or not 
    390     -abs/-rel* plot relative or absolute error 
    391     -linear/-log*/-q4 intensity scaling 
    392     -hist/-nohist* plot histogram of relative error 
    393     -res=0 sets the resolution width dQ/Q if calculating with resolution 
    394     -accuracy=Low accuracy of the resolution calculation Low, Mid, High, Xhigh 
    395     -edit starts the parameter explorer 
    396  
    397 Any two calculation engines can be selected for comparison: 
    398  
    399     -single/-double/-half/-fast sets an OpenCL calculation engine 
    400     -single!/-double!/-quad! sets an OpenMP calculation engine 
    401     -sasview sets the sasview calculation engine 
    402  
    403 The default is -single -sasview.  Note that the interpretation of quad 
    404 precision depends on architecture, and may vary from 64-bit to 128-bit, 
    405 with 80-bit floats being common (1e-19 precision). 
    406  
    407 Key=value pairs allow you to set specific values for the model parameters. 
    408  
    409 Available models: 
    410 """ 
    411  
    412  
    413515NAME_OPTIONS = set([ 
    414516    'plot', 'noplot', 
     
    431533 
    432534def columnize(L, indent="", width=79): 
     535    """ 
     536    Format a list of strings into columns for printing. 
     537    """ 
    433538    column_width = max(len(w) for w in L) + 1 
    434539    num_columns = (width - len(indent)) // column_width 
     
    443548 
    444549def get_demo_pars(model_definition): 
     550    """ 
     551    Extract demo parameters from the model definition. 
     552    """ 
    445553    info = generate.make_info(model_definition) 
    446554    # Get the default values for the parameters 
    447     pars = dict((p[0],p[2]) for p in info['parameters']) 
     555    pars = dict((p[0], p[2]) for p in info['parameters']) 
    448556 
    449557    # Fill in default values for the polydispersity parameters 
     
    460568 
    461569def parse_opts(): 
    462     flags = [arg for arg in sys.argv[1:] if arg.startswith('-')] 
    463     values = [arg for arg in sys.argv[1:] if not arg.startswith('-') and '=' in arg] 
    464     args = [arg for arg in sys.argv[1:] if not arg.startswith('-') and '=' not in arg] 
     570    """ 
     571    Parse command line options. 
     572    """ 
     573    flags = [arg for arg in sys.argv[1:] 
     574             if arg.startswith('-')] 
     575    values = [arg for arg in sys.argv[1:] 
     576              if not arg.startswith('-') and '=' in arg] 
     577    args = [arg for arg in sys.argv[1:] 
     578            if not arg.startswith('-') and '=' not in arg] 
    465579    models = "\n    ".join("%-15s"%v for v in MODELS) 
    466580    if len(args) == 0: 
    467581        print(USAGE) 
     582        print("\nAvailable models:") 
    468583        print(columnize(MODELS, indent="  ")) 
    469584        sys.exit(1) 
    470585    if args[0] not in MODELS: 
    471         print("Model %r not available. Use one of:\n    %s"%(args[0],models)) 
     586        print("Model %r not available. Use one of:\n    %s"%(args[0], models)) 
    472587        sys.exit(1) 
    473588    if len(args) > 3: 
    474         print("expected parameters: model Nopencl Nsasview") 
     589        print("expected parameters: model N1 N2") 
    475590 
    476591    invalid = [o[1:] for o in flags 
    477592               if o[1:] not in NAME_OPTIONS 
    478                   and not any(o.startswith('-%s='%t) for t in VALUE_OPTIONS)] 
     593                   and not any(o.startswith('-%s='%t) for t in VALUE_OPTIONS)] 
    479594    if invalid: 
    480595        print("Invalid options: %s"%(", ".join(invalid))) 
     
    538653 
    539654    if len(engines) == 0: 
    540         engines.extend(['single','sasview']) 
     655        engines.extend(['single', 'sasview']) 
    541656    elif len(engines) == 1: 
    542657        if engines[0][0] != 'sasview': 
     
    550665    model_definition = core.load_model_definition(name) 
    551666 
    552     N1 = int(args[1]) if len(args) > 1 else 1 
    553     N2 = int(args[2]) if len(args) > 2 else 1 
     667    n1 = int(args[1]) if len(args) > 1 else 1 
     668    n2 = int(args[2]) if len(args) > 2 else 1 
    554669 
    555670    # Get demo parameters from model definition, or use default parameters 
     
    583698    # Create the computational engines 
    584699    data, _index = make_data(opts) 
    585     if N1: 
     700    if n1: 
    586701        base = make_engine(model_definition, data, engines[0], opts['cutoff']) 
    587702    else: 
    588703        base = None 
    589     if N2: 
     704    if n2: 
    590705        comp = make_engine(model_definition, data, engines[1], opts['cutoff']) 
    591706    else: 
     
    596711        'name'      : name, 
    597712        'def'       : model_definition, 
    598         'N1'        : N1, 
    599         'N2'        : N2, 
     713        'n1'        : n1, 
     714        'n2'        : n2, 
    600715        'presets'   : presets, 
    601716        'pars'      : pars, 
     
    635750    def __init__(self, opts): 
    636751        from bumps.cli import config_matplotlib 
    637         import bumps_model 
     752        from . import bumps_model 
    638753        config_matplotlib() 
    639754        self.opts = opts 
     
    643758            active = [base + ext 
    644759                      for base in info['partype']['pd-1d'] 
    645                       for ext in ['','_pd','_pd_n','_pd_nsigma']] 
     760                      for ext in ['', '_pd', '_pd_n', '_pd_nsigma']] 
    646761            active.extend(info['partype']['fixed-1d']) 
    647762            for k in active: 
     
    658773    def numpoints(self): 
    659774        """ 
    660         Return the number of points 
     775        Return the number of points. 
    661776        """ 
    662777        return len(self.pars) + 1  # so dof is 1 
     
    664779    def parameters(self): 
    665780        """ 
    666         Return a dictionary of parameters 
     781        Return a dictionary of parameters. 
    667782        """ 
    668783        return self.pars 
    669784 
    670785    def nllf(self): 
     786        """ 
     787        Return cost. 
     788        """ 
    671789        return 0.  # No nllf 
    672790 
     
    675793        Plot the data and residuals. 
    676794        """ 
    677         pars = dict((k, v.value) for k,v in self.pars.items()) 
     795        pars = dict((k, v.value) for k, v in self.pars.items()) 
    678796        pars.update(self.pd_types) 
    679797        self.opts['pars'] = pars 
  • sasmodels/models/hollow_cylinder.c

    r07e72e6 r0420af7  
    114114        double norm,volume;     //final calculation variables 
    115115         
     116        if (core_radius >= radius || radius >= length) { 
     117                return NAN; 
     118        } 
     119         
    116120        delrho = solvent_sld - sld; 
    117121        lower = 0.0; 
     
    132136} 
    133137 
    134 //FIXME: Factor of two difference 
    135138double Iqxy(double qx, double qy, double radius, double core_radius, double length, double sld, 
    136139        double solvent_sld, double theta, double phi) 
  • sasmodels/models/hollow_cylinder.py

    rd18f8a8 r0420af7  
    6464""" 
    6565 
    66 from numpy import inf 
     66from numpy import pi, inf 
    6767 
    6868name = "hollow_cylinder" 
     
    9292source = ["lib/J1.c", "lib/gauss76.c", "hollow_cylinder.c"] 
    9393 
     94def ER(radius, core_radius, length): 
     95    if radius == 0 or length == 0: 
     96        return 0.0 
     97    len1 = radius 
     98    len2 = length/2.0 
     99    term1 = len1*len1*2.0*len2/2.0 
     100    term2 = 1.0 + (len2/len1)*(1.0 + 1/len2/2.0)*(1.0 + pi*len1/len2/2.0) 
     101    ddd = 3.0*term1*term2 
     102    diam = pow(ddd, (1.0/3.0)) 
     103    return diam 
     104 
     105def VR(radius, core_radius, length): 
     106    vol_core = pi*core_radius*core_radius*length 
     107    vol_total = pi*radius*radius*length 
     108    vol_shell = vol_total - vol_core 
     109    return vol_shell, vol_total 
     110 
    94111# parameters for demo 
    95112demo = dict(scale=1.0,background=0.0,length=400.0,radius=30.0,core_radius=20.0, 
     
    97114            radius_pd=.2, radius_pd_n=9, 
    98115            length_pd=.2, length_pd_n=10, 
     116            core_radius_pd=.2, core_radius_pd_n=9, 
    99117            theta_pd=10, theta_pd_n=5, 
    100118            ) 
     
    109127# Parameters for unit tests 
    110128tests = [ 
    111          [{"radius" : 30.0},0.00005,1764.926] 
     129         [{"radius" : 30.0},0.00005,1764.926], 
     130         [{},'VR',1.8], 
     131         [{},0.001,1756.76] 
    112132         ] 
  • sasmodels/models/lib/Si.c

    rdcef2ee r7d256c8  
    1 int factorial(int f); 
    21double Si(double x); 
    32 
    4 // integral of sin(x)/x: approximated to w/i 1% 
     3// integral of sin(x)/x Taylor series approximated to w/i 0.1% 
    54double Si(double x) 
    65{ 
    7         int i; 
    8         int nmax=6; 
    96        double out; 
    10         long power; 
    117        double pi = 4.0*atan(1.0); 
    128 
     
    1612                out = pi/2.0; 
    1713 
    18                 for (i=0; i<nmax-2; i+=1) { 
    19                         out_cos += pow(-1.0, i) * (double)factorial(2*i) / pow(x, 2*i+1); 
    20                         out_sin += pow(-1.0, i) * (double)factorial(2*i+1) / pow(x, 2*i+2); 
    21                 } 
     14                // Explicitly writing factorial values triples the speed of the calculation 
     15                out_cos = 1/x - 2/pow(x,3) + 24/pow(x,5) - 720/pow(x,7) + 40320/pow(x,9); 
     16                out_sin = 1/x - 6/pow(x,4) + 120/pow(x,6) - 5040/pow(x,8) + 362880/pow(x,10); 
    2217 
    2318                out -= cos(x) * out_cos; 
     
    2621        } 
    2722 
    28         out = 0.0; 
     23        // Explicitly writing factorial values triples the speed of the calculation 
     24        out = x - pow(x, 3)/18 + pow(x,5)/600 - pow(x,7)/35280 + pow(x,9)/3265920; 
    2925 
    30         for (i=0; i<nmax; i+=1) { 
    31                 if (i==0) { 
    32                         out += x; 
    33                         continue; 
    34                 } 
    35  
    36                 power = pow(x,(2 * i + 1)); 
    37                 out += pow(-1.0, i) * power / ((2.0 * (double)i + 1.0) * (double)factorial(2 * i + 1)); 
    38  
    39                 //printf ("Si=%g %g %d\n", x, out, i); 
    40         } 
    41  
     26        //printf ("Si=%g %g\n", x, out); 
    4227        return out; 
    4328} 
    44  
    45 int factorial(int f) 
    46 { 
    47     if ( f == 0 )  
    48         return 1; 
    49     return(f * factorial(f - 1)); 
    50 } 
Note: See TracChangeset for help on using the changeset viewer.