Changeset 13bd583 in sasmodels for sasmodels


Ignore:
Timestamp:
Feb 12, 2018 8:24:15 PM (6 years ago)
Author:
GitHub <noreply@…>
Branches:
master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
a2ca6e5
Parents:
e309e23 (diff), f6fd413 (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.
git-author:
Paul Butler <butlerpd@…> (02/12/18 20:24:15)
git-committer:
GitHub <noreply@…> (02/12/18 20:24:15)
Message:

Merge pull request #62 from SasView?/pytest

add support for pytest and use it on travis/appveyor

Location:
sasmodels
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/compare.py

    r2a7e20e r3221de0  
    4040from . import core 
    4141from . import kerneldll 
     42from . import kernelcl 
    4243from .data import plot_theory, empty_data1D, empty_data2D, load_data 
    4344from .direct_model import DirectModel, get_mesh 
     
    623624 
    624625 
     626def time_calculation(calculator, pars, evals=1): 
     627    # type: (Calculator, ParameterSet, int) -> Tuple[np.ndarray, float] 
     628    """ 
     629    Compute the average calculation time over N evaluations. 
     630 
     631    An additional call is generated without polydispersity in order to 
     632    initialize the calculation engine, and make the average more stable. 
     633    """ 
     634    # initialize the code so time is more accurate 
     635    if evals > 1: 
     636        calculator(**suppress_pd(pars)) 
     637    toc = tic() 
     638    # make sure there is at least one eval 
     639    value = calculator(**pars) 
     640    for _ in range(evals-1): 
     641        value = calculator(**pars) 
     642    average_time = toc()*1000. / evals 
     643    #print("I(q)",value) 
     644    return value, average_time 
     645 
     646def make_data(opts): 
     647    # type: (Dict[str, Any]) -> Tuple[Data, np.ndarray] 
     648    """ 
     649    Generate an empty dataset, used with the model to set Q points 
     650    and resolution. 
     651 
     652    *opts* contains the options, with 'qmax', 'nq', 'res', 
     653    'accuracy', 'is2d' and 'view' parsed from the command line. 
     654    """ 
     655    qmin, qmax, nq, res = opts['qmin'], opts['qmax'], opts['nq'], opts['res'] 
     656    if opts['is2d']: 
     657        q = np.linspace(-qmax, qmax, nq)  # type: np.ndarray 
     658        data = empty_data2D(q, resolution=res) 
     659        data.accuracy = opts['accuracy'] 
     660        set_beam_stop(data, qmin) 
     661        index = ~data.mask 
     662    else: 
     663        if opts['view'] == 'log' and not opts['zero']: 
     664            q = np.logspace(math.log10(qmin), math.log10(qmax), nq) 
     665        else: 
     666            q = np.linspace(qmin, qmax, nq) 
     667        if opts['zero']: 
     668            q = np.hstack((0, q)) 
     669        data = empty_data1D(q, resolution=res) 
     670        index = slice(None, None) 
     671    return data, index 
     672 
    625673DTYPE_MAP = { 
    626674    'half': '16', 
     
    643691    Return a model calculator using the OpenCL calculation engine. 
    644692    """ 
    645     if not core.HAVE_OPENCL: 
    646         raise RuntimeError("OpenCL not available") 
    647     model = core.build_model(model_info, dtype=dtype, platform="ocl") 
    648     calculator = DirectModel(data, model, cutoff=cutoff) 
    649     calculator.engine = "OCL%s"%DTYPE_MAP[str(model.dtype)] 
    650     return calculator 
    651693 
    652694def eval_ctypes(model_info, data, dtype='double', cutoff=0.): 
     
    660702    return calculator 
    661703 
    662 def time_calculation(calculator, pars, evals=1): 
    663     # type: (Calculator, ParameterSet, int) -> Tuple[np.ndarray, float] 
    664     """ 
    665     Compute the average calculation time over N evaluations. 
    666  
    667     An additional call is generated without polydispersity in order to 
    668     initialize the calculation engine, and make the average more stable. 
    669     """ 
    670     # initialize the code so time is more accurate 
    671     if evals > 1: 
    672         calculator(**suppress_pd(pars)) 
    673     toc = tic() 
    674     # make sure there is at least one eval 
    675     value = calculator(**pars) 
    676     for _ in range(evals-1): 
    677         value = calculator(**pars) 
    678     average_time = toc()*1000. / evals 
    679     #print("I(q)",value) 
    680     return value, average_time 
    681  
    682 def make_data(opts): 
    683     # type: (Dict[str, Any]) -> Tuple[Data, np.ndarray] 
    684     """ 
    685     Generate an empty dataset, used with the model to set Q points 
    686     and resolution. 
    687  
    688     *opts* contains the options, with 'qmax', 'nq', 'res', 
    689     'accuracy', 'is2d' and 'view' parsed from the command line. 
    690     """ 
    691     qmin, qmax, nq, res = opts['qmin'], opts['qmax'], opts['nq'], opts['res'] 
    692     if opts['is2d']: 
    693         q = np.linspace(-qmax, qmax, nq)  # type: np.ndarray 
    694         data = empty_data2D(q, resolution=res) 
    695         data.accuracy = opts['accuracy'] 
    696         set_beam_stop(data, qmin) 
    697         index = ~data.mask 
    698     else: 
    699         if opts['view'] == 'log' and not opts['zero']: 
    700             q = np.logspace(math.log10(qmin), math.log10(qmax), nq) 
    701         else: 
    702             q = np.linspace(qmin, qmax, nq) 
    703         if opts['zero']: 
    704             q = np.hstack((0, q)) 
    705         data = empty_data1D(q, resolution=res) 
    706         index = slice(None, None) 
    707     return data, index 
    708  
    709704def make_engine(model_info, data, dtype, cutoff, ngauss=0): 
    710705    # type: (ModelInfo, Data, str, float) -> Calculator 
     
    718713        set_integration_size(model_info, ngauss) 
    719714 
    720     if dtype is None or not dtype.endswith('!'): 
    721         return eval_opencl(model_info, data, dtype=dtype, cutoff=cutoff) 
    722     else: 
    723         return eval_ctypes(model_info, data, dtype=dtype[:-1], cutoff=cutoff) 
     715    if dtype != "default" and not dtype.endswith('!') and not kernelcl.use_opencl(): 
     716        raise RuntimeError("OpenCL not available " + kernelcl.OPENCL_ERROR) 
     717 
     718    model = core.build_model(model_info, dtype=dtype, platform="ocl") 
     719    calculator = DirectModel(data, model, cutoff=cutoff) 
     720    engine_type = calculator._model.__class__.__name__.replace('Model','').upper() 
     721    bits = calculator._model.dtype.itemsize*8 
     722    precision = "fast" if getattr(calculator._model, 'fast', False) else str(bits) 
     723    calculator.engine = "%s[%s]" % (engine_type, precision) 
     724    return calculator 
    724725 
    725726def _show_invalid(data, theory): 
  • sasmodels/convert.py

    r2d81cfe ra69d8cd  
    633633def test_backward_forward(): 
    634634    from .core import list_models 
     635    L = lambda name: _check_one(name, seed=1) 
    635636    for name in list_models('all'): 
    636         L = lambda: _check_one(name, seed=1) 
    637         L.description = name 
    638         yield L 
     637        yield L, name 
  • sasmodels/core.py

    r7a516d0 r3221de0  
    2121from . import mixture 
    2222from . import kernelpy 
     23from . import kernelcl 
    2324from . import kerneldll 
    2425from . import custom 
    25  
    26 if os.environ.get("SAS_OPENCL", "").lower() == "none": 
    27     HAVE_OPENCL = False 
    28 else: 
    29     try: 
    30         from . import kernelcl 
    31         HAVE_OPENCL = True 
    32     except Exception: 
    33         HAVE_OPENCL = False 
    34  
    35 CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 
    36 if CUSTOM_MODEL_PATH == "": 
    37     CUSTOM_MODEL_PATH = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 
    38     if not os.path.isdir(CUSTOM_MODEL_PATH): 
    39         os.makedirs(CUSTOM_MODEL_PATH) 
    4026 
    4127# pylint: disable=unused-import 
     
    4733    pass 
    4834# pylint: enable=unused-import 
     35 
     36CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 
     37if CUSTOM_MODEL_PATH == "": 
     38    CUSTOM_MODEL_PATH = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 
     39    if not os.path.isdir(CUSTOM_MODEL_PATH): 
     40        os.makedirs(CUSTOM_MODEL_PATH) 
    4941 
    5042# TODO: refactor composite model support 
     
    276268    if platform is None: 
    277269        platform = "ocl" 
    278     if platform == "ocl" and not HAVE_OPENCL or not model_info.opencl: 
     270    if not kernelcl.use_opencl() or not model_info.opencl: 
    279271        platform = "dll" 
    280272 
     
    320312    print("\n".join(list_models(kind))) 
    321313 
    322 def test_load(): 
    323     # type: () -> None 
    324     """Check that model load works""" 
     314def test_composite_order(): 
    325315    def test_models(fst, snd): 
    326316        """Confirm that two models produce the same parameters""" 
    327317        fst = load_model(fst) 
    328318        snd = load_model(snd) 
    329         # Remove the upper case characters frin the parameters, since 
    330         # they lasndel the order of events and we specfically are 
    331         # changin that aspect 
     319        # Un-disambiguate parameter names so that we can check if the same 
     320        # parameters are in a pair of composite models. Since each parameter in 
     321        # the mixture model is tagged as e.g., A_sld, we ought to use a 
     322        # regex subsitution s/^[A-Z]+_/_/, but removing all uppercase letters 
     323        # is good enough. 
    332324        fst = [[x for x in p.name if x == x.lower()] for p in fst.info.parameters.kernel_parameters] 
    333325        snd = [[x for x in p.name if x == x.lower()] for p in snd.info.parameters.kernel_parameters] 
    334326        assert sorted(fst) == sorted(snd), "{} != {}".format(fst, snd) 
    335327 
    336  
    337     test_models( 
     328    def build_test(first, second): 
     329        test = lambda description: test_models(first, second) 
     330        description = first + " vs. " + second 
     331        return test, description 
     332 
     333    yield build_test( 
    338334        "cylinder+sphere", 
    339335        "sphere+cylinder") 
    340     test_models( 
     336    yield build_test( 
    341337        "cylinder*sphere", 
    342338        "sphere*cylinder") 
    343     test_models( 
     339    yield build_test( 
    344340        "cylinder@hardsphere*sphere", 
    345341        "sphere*cylinder@hardsphere") 
    346     test_models( 
     342    yield build_test( 
    347343        "barbell+sphere*cylinder@hardsphere", 
    348344        "sphere*cylinder@hardsphere+barbell") 
    349     test_models( 
     345    yield build_test( 
    350346        "barbell+cylinder@hardsphere*sphere", 
    351347        "cylinder@hardsphere*sphere+barbell") 
    352     test_models( 
     348    yield build_test( 
    353349        "barbell+sphere*cylinder@hardsphere", 
    354350        "barbell+cylinder@hardsphere*sphere") 
    355     test_models( 
     351    yield build_test( 
    356352        "sphere*cylinder@hardsphere+barbell", 
    357353        "cylinder@hardsphere*sphere+barbell") 
    358     test_models( 
     354    yield build_test( 
    359355        "barbell+sphere*cylinder@hardsphere", 
    360356        "cylinder@hardsphere*sphere+barbell") 
    361     test_models( 
     357    yield build_test( 
    362358        "barbell+cylinder@hardsphere*sphere", 
    363359        "sphere*cylinder@hardsphere+barbell") 
    364360 
     361def test_composite(): 
     362    # type: () -> None 
     363    """Check that model load works""" 
    365364    #Test the the model produces the parameters that we would expect 
    366365    model = load_model("cylinder@hardsphere*sphere") 
  • sasmodels/jitter.py

    r0d5a655 r42158d2  
    1313import argparse 
    1414 
    15 import mpl_toolkits.mplot3d   # Adds projection='3d' option to subplot 
     15try: # CRUFT: travis-ci does not support mpl_toolkits.mplot3d 
     16    import mpl_toolkits.mplot3d  # Adds projection='3d' option to subplot 
     17except ImportError: 
     18    pass 
     19 
    1620import matplotlib.pyplot as plt 
    1721from matplotlib.widgets import Slider, CheckButtons 
  • sasmodels/kernelcl.py

    r2d81cfe rb4272a2  
    5858import numpy as np  # type: ignore 
    5959 
     60 
     61# Attempt to setup opencl. This may fail if the opencl package is not 
     62# installed or if it is installed but there are no devices available. 
    6063try: 
    61     #raise NotImplementedError("OpenCL not yet implemented for new kernel template") 
    6264    import pyopencl as cl  # type: ignore 
     65    from pyopencl import mem_flags as mf 
     66    from pyopencl.characterize import get_fast_inaccurate_build_options 
    6367    # Ask OpenCL for the default context so that we know that one exists 
    6468    cl.create_some_context(interactive=False) 
     69    HAVE_OPENCL = True 
     70    OPENCL_ERROR = "" 
    6571except Exception as exc: 
    66     warnings.warn("OpenCL startup failed with ***" 
    67                   + str(exc) + "***; using C compiler instead") 
    68     raise RuntimeError("OpenCL not available") 
    69  
    70 from pyopencl import mem_flags as mf 
    71 from pyopencl.characterize import get_fast_inaccurate_build_options 
     72    HAVE_OPENCL = False 
     73    OPENCL_ERROR = str(exc) 
    7274 
    7375from . import generate 
     
    102104        cl._DEFAULT_INCLUDE_OPTIONS = [quote_path(v) for v in cl._DEFAULT_INCLUDE_OPTIONS] 
    103105 
    104 fix_pyopencl_include() 
    105  
     106if HAVE_OPENCL: 
     107    fix_pyopencl_include() 
    106108 
    107109# The max loops number is limited by the amount of local memory available 
     
    128130""" 
    129131 
     132def use_opencl(): 
     133    return HAVE_OPENCL and os.environ.get("SAS_OPENCL", "").lower() != "none" 
    130134 
    131135ENV = None 
     136def reset_environment(): 
     137    """ 
     138    Call to create a new OpenCL context, such as after a change to SAS_OPENCL. 
     139    """ 
     140    global ENV 
     141    ENV = GpuEnvironment() if use_opencl() else None 
     142 
    132143def environment(): 
    133144    # type: () -> "GpuEnvironment" 
     
    137148    This provides an OpenCL context and one queue per device. 
    138149    """ 
    139     global ENV 
    140150    if ENV is None: 
    141         ENV = GpuEnvironment() 
     151        if not HAVE_OPENCL: 
     152            raise RuntimeError("OpenCL startup failed with ***" 
     153                            + OPENCL_ERROR + "***; using C compiler instead") 
     154        reset_environment() 
     155        if ENV is None: 
     156            raise RuntimeError("SAS_OPENCL=None in environment") 
    142157    return ENV 
    143158 
  • sasmodels/model_test.py

    r2d81cfe r3221de0  
    6262from .exception import annotate_exception 
    6363from .modelinfo import expand_pars 
     64from .kernelcl import use_opencl 
    6465 
    6566# pylint: disable=unused-import 
     
    123124 
    124125        if is_py:  # kernel implemented in python 
    125             test_name = "Model: %s, Kernel: python"%model_name 
     126            test_name = "%s-python"%model_name 
    126127            test_method_name = "test_%s_python" % model_info.id 
    127128            test = ModelTestCase(test_name, model_info, 
     
    134135 
    135136            # test using dll if desired 
    136             if 'dll' in loaders or not core.HAVE_OPENCL: 
    137                 test_name = "Model: %s, Kernel: dll"%model_name 
     137            if 'dll' in loaders or not use_opencl(): 
     138                test_name = "%s-dll"%model_name 
    138139                test_method_name = "test_%s_dll" % model_info.id 
    139140                test = ModelTestCase(test_name, model_info, 
     
    145146 
    146147            # test using opencl if desired and available 
    147             if 'opencl' in loaders and core.HAVE_OPENCL: 
    148                 test_name = "Model: %s, Kernel: OpenCL"%model_name 
     148            if 'opencl' in loaders and use_opencl(): 
     149                test_name = "%s-opencl"%model_name 
    149150                test_method_name = "test_%s_opencl" % model_info.id 
    150151                # Using dtype=None so that the models that are only 
     
    160161 
    161162    return suite 
    162  
    163163 
    164164def _hide_model_case_from_nose(): 
     
    368368 
    369369    # Build a test suite containing just the model 
    370     loaders = ['opencl'] if core.HAVE_OPENCL else ['dll'] 
     370    loaders = ['opencl'] if use_opencl() else ['dll'] 
    371371    models = [model] 
    372372    try: 
     
    425425        verbosity = 1 
    426426    if models and models[0] == 'opencl': 
    427         if not core.HAVE_OPENCL: 
     427        if not use_opencl(): 
    428428            print("opencl is not available") 
    429429            return 1 
     
    435435        models = models[1:] 
    436436    elif models and models[0] == 'opencl_and_dll': 
    437         loaders = ['opencl', 'dll'] if core.HAVE_OPENCL else ['dll'] 
     437        loaders = ['opencl', 'dll'] if use_opencl() else ['dll'] 
    438438        models = models[1:] 
    439439    else: 
    440         loaders = ['opencl', 'dll'] if core.HAVE_OPENCL else ['dll'] 
     440        loaders = ['opencl', 'dll'] if use_opencl() else ['dll'] 
    441441    if not models: 
    442442        print("""\ 
     
    467467    Run "nosetests sasmodels" on the command line to invoke it. 
    468468    """ 
    469     loaders = ['opencl', 'dll'] if core.HAVE_OPENCL else ['dll'] 
     469    loaders = ['opencl', 'dll'] if use_opencl() else ['dll'] 
    470470    tests = make_suite(loaders, ['all']) 
    471     for test_i in tests: 
    472         # In order for nosetest to see the correct test name, need to set 
    473         # the description attribute of the returned function.  Since we 
    474         # can't do this for the returned instance, wrap it in a lambda and 
    475         # set the description on the lambda.  Otherwise we could just do: 
    476         #    yield test_i.run_all 
    477         L = lambda: test_i.run_all() 
    478         L.description = test_i.test_name 
    479         yield L 
     471    def build_test(test): 
     472        # In order for nosetest to show the test name, wrap the test.run_all 
     473        # instance in function that takes the test name as a parameter which 
     474        # will be displayed when the test is run.  Do this as a function so 
     475        # that it properly captures the context for tests that captured and 
     476        # run later.  If done directly in the for loop, then the looping 
     477        # variable test will be shared amongst all the tests, and we will be 
     478        # repeatedly testing vesicle. 
     479 
     480        # Note: in sasview sas.sasgui.perspectives.fitting.gpu_options 
     481        # requires that the test.description field be set. 
     482        wrap = lambda: test.run_all() 
     483        wrap.description = test.test_name 
     484        return wrap 
     485        # The following would work with nosetests and pytest: 
     486        #     return lambda name: test.run_all(), test.test_name 
     487 
     488    for test in tests: 
     489        yield build_test(test) 
    480490 
    481491 
  • sasmodels/sasview_model.py

    r2d81cfe r3221de0  
    665665 
    666666    def _calculate_Iq(self, qx, qy=None): 
    667         #core.HAVE_OPENCL = False 
    668667        if self._model is None: 
    669668            self._model = core.build_model(self._model_info) 
     
    859858    # type: () -> None 
    860859    """ 
    861     Load and run cylinder model from sas.models.CylinderModel 
     860    Load and run cylinder model as sas-models-CylinderModel 
    862861    """ 
    863862    if not SUPPORT_OLD_STYLE_PLUGINS: 
  • sasmodels/kernel_iq.c

    r924a119 raadec17  
    9393// Compute the magnetic sld 
    9494static double mag_sld( 
    95   const int xs, // 0=dd, 1=du real, 2=ud real, 3=uu, 4=du imag, 5=up imag 
     95  const unsigned int xs, // 0=dd, 1=du real, 2=ud real, 3=uu, 4=du imag, 5=up imag 
    9696  const double qx, const double qy, 
    9797  const double px, const double py, 
     
    103103    const double perp = qy*mx - qx*my; 
    104104    switch (xs) { 
     105      default: // keep compiler happy; condition ensures xs in [0,1,2,3] 
    105106      case 0: // uu => sld - D M_perpx 
    106107          return sld - px*perp; 
     
    659660 
    660661            // loop over uu, ud real, du real, dd, ud imag, du imag 
    661             for (int xs=0; xs<6; xs++) { 
     662            for (unsigned int xs=0; xs<6; xs++) { 
    662663              const double xs_weight = spins[xs]; 
    663664              if (xs_weight > 1.e-8) { 
  • sasmodels/weights.py

    r2d81cfe r3d58247  
    9797        return x, px 
    9898 
     99class UniformDispersion(Dispersion): 
     100    r""" 
     101    Uniform dispersion, with width $\sigma$. 
     102 
     103    .. math:: 
     104 
     105        w = 1 
     106    """ 
     107    type = "uniform" 
     108    default = dict(npts=35, width=0, nsigmas=None) 
     109    def _weights(self, center, sigma, lb, ub): 
     110        x = np.linspace(center-sigma, center+sigma, self.npts) 
     111        x = x[(x >= lb) & (x <= ub)] 
     112        return x, np.ones_like(x) 
    99113 
    100114class RectangleDispersion(Dispersion): 
     
    107121    """ 
    108122    type = "rectangle" 
    109     default = dict(npts=35, width=0, nsigmas=1.70325) 
    110     def _weights(self, center, sigma, lb, ub): 
    111         x = self._linspace(center, sigma, lb, ub) 
    112         x = x[np.fabs(x-center) <= np.fabs(sigma)*sqrt(3.0)] 
    113         return x, np.ones_like(x) 
    114  
     123    default = dict(npts=35, width=0, nsigmas=1.73205) 
     124    def _weights(self, center, sigma, lb, ub): 
     125         x = self._linspace(center, sigma, lb, ub) 
     126         x = x[np.fabs(x-center) <= np.fabs(sigma)*sqrt(3.0)] 
     127         return x, np.ones_like(x) 
    115128 
    116129class LogNormalDispersion(Dispersion): 
     
    190203        return x, px 
    191204 
     205class BoltzmannDispersion(Dispersion): 
     206    r""" 
     207    Boltzmann dispersion, with $\sigma=k T/E$. 
     208 
     209    .. math:: 
     210 
     211        w = \exp\left( -|x - c|/\sigma\right) 
     212    """ 
     213    type = "boltzmann" 
     214    default = dict(npts=35, width=0, nsigmas=3) 
     215    def _weights(self, center, sigma, lb, ub): 
     216        x = self._linspace(center, sigma, lb, ub) 
     217        px = np.exp(-np.abs(x-center) / np.abs(sigma)) 
     218        return x, px 
    192219 
    193220# dispersion name -> disperser lookup table. 
     
    196223MODELS = OrderedDict((d.type, d) for d in ( 
    197224    RectangleDispersion, 
     225    UniformDispersion, 
    198226    ArrayDispersion, 
    199227    LogNormalDispersion, 
    200228    GaussianDispersion, 
    201229    SchulzDispersion, 
     230    BoltzmannDispersion 
    202231)) 
    203232 
Note: See TracChangeset for help on using the changeset viewer.