Changeset ad79f49 in sasmodels


Ignore:
Timestamp:
Sep 12, 2017 11:29:02 AM (7 years ago)
Author:
Paul Kienzle <pkienzle@…>
Branches:
master, core_shell_microgels, costrafo411, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
946c8d27
Parents:
a57a902 (diff), b18e650 (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 branch 'master' into ticket-741

Files:
11 edited

Legend:

Unmodified
Added
Removed
  • .travis.yml

    r947a61e rce8c388  
    2424    packages: opencl-headers 
    2525before_install: 
    26 - openssl aes-256-cbc -K $encrypted_fe6026add10a_key -iv $encrypted_fe6026add10a_iv 
    27   -in .travis/travis_rsa.enc -out .travis/travis_rsa -d 
     26- if [[ $encrypted_cb04388797b6_iv ]]; then openssl aes-256-cbc -K $encrypted_cb04388797b6_key -iv $encrypted_cb04388797b6_iv 
     27  -in .travis/travis_rsa.enc -out .travis/travis_rsa -d; fi; 
    2828- echo $TRAVIS_OS_NAME 
    2929- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh 
     
    4646- echo -e "Host danse.chem.utk.edu\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 
    4747deploy: 
    48     skip_cleanup: true 
    49     provider: script 
    50     script: /bin/sh -ex ./deploy.sh 
    51     on: 
    52         branch: master 
     48  skip_cleanup: true 
     49  provider: script 
     50  script: "/bin/sh -ex ./deploy.sh" 
     51  on: 
     52    branch: master 
    5353notifications: 
    5454  slack: 
  • deploy.sh

    rd9d77b0 r9d1e3e4  
    1 eval "$(ssh-agent -s)" 
    2 chmod 600 .travis/travis_rsa 
    3 ssh-add .travis/travis_rsa 
    4 git remote add deploy git@danse.chem.utk.edu:/home/git/sasmodels 
    5 git push deploy master 
     1if [[ $encrypted_cb04388797b6_iv ]] 
     2then 
     3    eval "$(ssh-agent -s)" 
     4    chmod 600 .travis/travis_rsa 
     5    ssh-add .travis/travis_rsa 
     6    git remote add deploy git@danse.chem.utk.edu:/home/git/sasmodels 
     7    git push deploy master 
     8fi 
  • sasmodels/compare.py

    rd9ec8f9 r765eb0e  
    8888    -magnetic/-nonmagnetic* suppress magnetism 
    8989    -accuracy=Low accuracy of the resolution calculation Low, Mid, High, Xhigh 
     90    -neval=1 sets the number of evals for more accurate timing 
    9091 
    9192    === precision options === 
     
    10131014    'magnetic', 'nonmagnetic', 
    10141015    'accuracy=', 
     1016    'neval=',  # for timing... 
    10151017 
    10161018    # Precision options 
     
    10181020    'half', 'fast', 'single', 'double', 'single!', 'double!', 'quad!', 
    10191021    'sasview',  # TODO: remove sasview 3.x support 
    1020     'timing=', 
    10211022 
    10221023    # Output options 
     
    10681069 
    10691070    # Plug in values given in demo 
    1070     if use_demo: 
     1071    if use_demo and model_info.demo: 
    10711072        pars.update(model_info.demo) 
    10721073    return pars 
  • sasmodels/core.py

    r142a8e2 ra85a569  
    1010 
    1111import os 
     12import re 
    1213from os.path import basename, dirname, join as joinpath 
    1314from glob import glob 
     
    2122from . import kernelpy 
    2223from . import kerneldll 
     24from . import custom 
    2325 
    2426if os.environ.get("SAS_OPENCL", "").lower() == "none": 
     
    3032    except Exception: 
    3133        HAVE_OPENCL = False 
     34 
     35CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 
     36if CUSTOM_MODEL_PATH == "": 
     37    path = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 
     38    if not os.path.isdir(path): 
     39        os.makedirs(path) 
     40    CUSTOM_MODEL_PATH = path 
    3241 
    3342try: 
     
    125134                       dtype=dtype, platform=platform) 
    126135 
    127  
    128 def load_model_info(model_name): 
     136def load_model_info(model_string): 
    129137    # type: (str) -> modelinfo.ModelInfo 
    130138    """ 
    131139    Load a model definition given the model name. 
    132140 
    133     *model_name* is the name of the model, or perhaps a model expression 
    134     such as sphere*hardsphere or sphere+cylinder. 
     141    *model_string* is the name of the model, or perhaps a model expression 
     142    such as sphere*cylinder or sphere+cylinder. Use '@' for a structure 
     143    factor product, e.g. sphere@hardsphere. Custom models can be specified by 
     144    prefixing the model name with 'custom.', e.g. 'custom.MyModel+sphere'. 
    135145 
    136146    This returns a handle to the module defining the model.  This can be 
    137147    used with functions in generate to build the docs or extract model info. 
    138148    """ 
    139     parts = model_name.split('+') 
    140     if len(parts) > 1: 
    141         model_info_list = [load_model_info(p) for p in parts] 
    142         return mixture.make_mixture_info(model_info_list) 
    143  
    144     parts = model_name.split('*') 
    145     if len(parts) > 1: 
    146         if len(parts) > 2: 
    147             raise ValueError("use P*S to apply structure factor S to model P") 
    148         P_info, Q_info = [load_model_info(p) for p in parts] 
     149    if '@' in model_string: 
     150        parts = model_string.split('@') 
     151        if len(parts) != 2: 
     152            raise ValueError("Use P@S to apply a structure factor S to model P") 
     153        P_info, Q_info = [load_model_info(part) for part in parts] 
    149154        return product.make_product_info(P_info, Q_info) 
    150155 
    151     kernel_module = generate.load_kernel_module(model_name) 
    152     return modelinfo.make_model_info(kernel_module) 
     156    product_parts = [] 
     157    addition_parts = [] 
     158 
     159    addition_parts_names = model_string.split('+') 
     160    if len(addition_parts_names) >= 2: 
     161        addition_parts = [load_model_info(part) for part in addition_parts_names] 
     162    elif len(addition_parts_names) == 1: 
     163        product_parts_names = model_string.split('*') 
     164        if len(product_parts_names) >= 2: 
     165            product_parts = [load_model_info(part) for part in product_parts_names] 
     166        elif len(product_parts_names) == 1: 
     167            if "custom." in product_parts_names[0]: 
     168                # Extract ModelName from "custom.ModelName" 
     169                pattern = "custom.([A-Za-z0-9_-]+)" 
     170                result = re.match(pattern, product_parts_names[0]) 
     171                if result is None: 
     172                    raise ValueError("Model name in invalid format: " + product_parts_names[0]) 
     173                model_name = result.group(1) 
     174                # Use ModelName to find the path to the custom model file 
     175                model_path = joinpath(CUSTOM_MODEL_PATH, model_name + ".py") 
     176                if not os.path.isfile(model_path): 
     177                    raise ValueError("The model file {} doesn't exist".format(model_path)) 
     178                kernel_module = custom.load_custom_kernel_module(model_path) 
     179                return modelinfo.make_model_info(kernel_module) 
     180            # Model is a core model 
     181            kernel_module = generate.load_kernel_module(product_parts_names[0]) 
     182            return modelinfo.make_model_info(kernel_module) 
     183 
     184    model = None 
     185    if len(product_parts) > 1: 
     186        model = mixture.make_mixture_info(product_parts, operation='*') 
     187    if len(addition_parts) > 1: 
     188        if model is not None: 
     189            addition_parts.append(model) 
     190        model = mixture.make_mixture_info(addition_parts, operation='+') 
     191    return model 
    153192 
    154193 
  • sasmodels/mixture.py

    r6dc78e4 r0edb6c1  
    2525    pass 
    2626 
    27 def make_mixture_info(parts): 
     27def make_mixture_info(parts, operation='+'): 
    2828    # type: (List[ModelInfo]) -> ModelInfo 
    2929    """ 
    3030    Create info block for mixture model. 
    3131    """ 
    32     flatten = [] 
    33     for part in parts: 
    34         if part.composition and part.composition[0] == 'mixture': 
    35             flatten.extend(part.composition[1]) 
    36         else: 
    37             flatten.append(part) 
    38     parts = flatten 
    39  
    4032    # Build new parameter list 
    4133    combined_pars = [] 
    42     demo = {} 
    43     for k, part in enumerate(parts): 
     34 
     35    model_num = 0 
     36    all_parts = copy(parts) 
     37    is_flat = False 
     38    while not is_flat: 
     39        is_flat = True 
     40        for part in all_parts: 
     41            if part.composition and part.composition[0] == 'mixture' and \ 
     42                len(part.composition[1]) > 1: 
     43                all_parts += part.composition[1] 
     44                all_parts.remove(part) 
     45                is_flat = False 
     46 
     47    # When creating a mixture model that is a sum of product models (ie (1*2)+(3*4)) 
     48    # the parameters for models 1 & 2 will be prefixed with A & B respectively, 
     49    # but so will the parameters for models 3 & 4. We need to rename models 3 & 4 
     50    # so that they are prefixed with C & D to avoid overlap of parameter names. 
     51    used_prefixes = [] 
     52    for part in parts: 
     53        i = 0 
     54        if part.composition and part.composition[0] == 'mixture': 
     55            npars_list = [info.parameters.npars for info in part.composition[1]] 
     56            for npars in npars_list: 
     57                # List of params of one of the constituent models of part 
     58                submodel_pars = part.parameters.kernel_parameters[i:i+npars] 
     59                # Prefix of the constituent model 
     60                prefix = submodel_pars[0].name[0] 
     61                if prefix not in used_prefixes: # Haven't seen this prefix so far 
     62                    used_prefixes.append(prefix) 
     63                    i += npars 
     64                    continue 
     65                while prefix in used_prefixes: 
     66                    # This prefix has been already used, so change it to the 
     67                    # next letter that hasn't been used 
     68                    prefix = chr(ord(prefix) + 1) 
     69                used_prefixes.append(prefix) 
     70                prefix += "_" 
     71                # Update the parameters of this constituent model to use the 
     72                # new prefix 
     73                for par in submodel_pars: 
     74                    par.id = prefix + par.id[2:] 
     75                    par.name = prefix + par.name[2:] 
     76                    if par.length_control is not None: 
     77                        par.length_control = prefix + par.length_control[2:] 
     78                i += npars 
     79 
     80    for part in parts: 
    4481        # Parameter prefix per model, A_, B_, ... 
    4582        # Note that prefix must also be applied to id and length_control 
    4683        # to support vector parameters 
    47         prefix = chr(ord('A')+k) + '_' 
    48         scale =  Parameter(prefix+'scale', default=1.0, 
    49                            description="model intensity for " + part.name) 
    50         combined_pars.append(scale) 
     84        prefix = '' 
     85        if not part.composition: 
     86            # Model isn't a composition model, so it's parameters don't have a 
     87            # a prefix. Add the next available prefix 
     88            prefix = chr(ord('A')+len(used_prefixes)) 
     89            used_prefixes.append(prefix) 
     90            prefix += '_' 
     91             
     92        if operation == '+': 
     93            # If model is a sum model, each constituent model gets its own scale parameter 
     94            scale_prefix = prefix 
     95            if prefix == '' and part.operation == '*': 
     96                # `part` is a composition product model. Find the prefixes of 
     97                # it's parameters to form a new prefix for the scale, eg: 
     98                # a model with A*B*C will have ABC_scale 
     99                sub_prefixes = [] 
     100                for param in part.parameters.kernel_parameters: 
     101                    # Prefix of constituent model 
     102                    sub_prefix = param.id.split('_')[0] 
     103                    if sub_prefix not in sub_prefixes: 
     104                        sub_prefixes.append(sub_prefix) 
     105                # Concatenate sub_prefixes to form prefix for the scale 
     106                scale_prefix = ''.join(sub_prefixes) + '_' 
     107            scale =  Parameter(scale_prefix + 'scale', default=1.0, 
     108                            description="model intensity for " + part.name) 
     109            combined_pars.append(scale) 
    51110        for p in part.parameters.kernel_parameters: 
    52111            p = copy(p) 
     
    56115                p.length_control = prefix + p.length_control 
    57116            combined_pars.append(p) 
    58         demo.update((prefix+k, v) for k, v in part.demo.items() 
    59                     if k != "background") 
    60     #print("pars",combined_pars) 
    61117    parameters = ParameterTable(combined_pars) 
    62118    parameters.max_pd = sum(part.parameters.max_pd for part in parts) 
    63119 
     120    def random(): 
     121        combined_pars = {} 
     122        for k, part in enumerate(parts): 
     123            prefix = chr(ord('A')+k) + '_' 
     124            pars = part.random() 
     125            combined_pars.update((prefix+k, v) for k, v in pars.items()) 
     126        return combined_pars 
     127 
    64128    model_info = ModelInfo() 
    65     model_info.id = '+'.join(part.id for part in parts) 
    66     model_info.name = ' + '.join(part.name for part in parts) 
     129    model_info.id = operation.join(part.id for part in parts) 
     130    model_info.operation = operation 
     131    model_info.name = '(' + operation.join(part.name for part in parts) + ')' 
    67132    model_info.filename = None 
    68133    model_info.title = 'Mixture model with ' + model_info.name 
     
    71136    model_info.category = "custom" 
    72137    model_info.parameters = parameters 
     138    model_info.random = random 
    73139    #model_info.single = any(part['single'] for part in parts) 
    74140    model_info.structure_factor = False 
     
    79145    # Remember the component info blocks so we can build the model 
    80146    model_info.composition = ('mixture', parts) 
    81     model_info.demo = demo 
    82147    return model_info 
    83148 
     
    88153        self.info = model_info 
    89154        self.parts = parts 
     155        self.dtype = parts[0].dtype 
    90156 
    91157    def make_kernel(self, q_vectors): 
     
    116182        self.kernels = kernels 
    117183        self.dtype = self.kernels[0].dtype 
     184        self.operation = model_info.operation 
    118185        self.results = []  # type: List[np.ndarray] 
    119186 
     
    124191        # remember the parts for plotting later 
    125192        self.results = []  # type: List[np.ndarray] 
    126         offset = 2 # skip scale & background 
    127193        parts = MixtureParts(self.info, self.kernels, call_details, values) 
    128194        for kernel, kernel_details, kernel_values in parts: 
    129195            #print("calling kernel", kernel.info.name) 
    130196            result = kernel(kernel_details, kernel_values, cutoff, magnetic) 
    131             #print(kernel.info.name, result) 
    132             total += result 
     197            result = np.array(result).astype(kernel.dtype) 
     198            # print(kernel.info.name, result) 
     199            if self.operation == '+': 
     200                total += result 
     201            elif self.operation == '*': 
     202                if np.all(total) == 0.0: 
     203                    total = result 
     204                else: 
     205                    total *= result 
    133206            self.results.append(result) 
    134207 
     
    171244 
    172245        self.part_num += 1 
    173         self.par_index += info.parameters.npars + 1 
     246        self.par_index += info.parameters.npars 
     247        if self.model_info.operation == '+': 
     248            self.par_index += 1 # Account for each constituent model's scale param 
    174249        self.mag_index += 3 * len(info.parameters.magnetism_index) 
    175250 
     
    182257        # which includes the initial scale and background parameters. 
    183258        # We want the index into the weight length/offset for each parameter. 
    184         # Exclude the initial scale and background, so subtract two, but each 
    185         # component has its own scale factor which we need to skip when 
    186         # constructing the details for the kernel, so add one, giving a 
    187         # net subtract one. 
    188         index = slice(par_index - 1, par_index - 1 + info.parameters.npars) 
     259        # Exclude the initial scale and background, so subtract two. If we're 
     260        # building an addition model, each component has its own scale factor 
     261        # which we need to skip when constructing the details for the kernel, so 
     262        # add one, giving a net subtract one. 
     263        diff = 1 if self.model_info.operation == '+' else 2 
     264        index = slice(par_index - diff, par_index - diff + info.parameters.npars) 
    189265        length = full.length[index] 
    190266        offset = full.offset[index] 
     
    196272    def _part_values(self, info, par_index, mag_index): 
    197273        # type: (ModelInfo, int, int) -> np.ndarray 
    198         #print(info.name, par_index, self.values[par_index:par_index + info.parameters.npars + 1]) 
    199         scale = self.values[par_index] 
    200         pars = self.values[par_index + 1:par_index + info.parameters.npars + 1] 
     274        # Set each constituent model's scale to 1 if this is a multiplication model 
     275        scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 
     276        diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 
     277        pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 
    201278        nmagnetic = len(info.parameters.magnetism_index) 
    202279        if nmagnetic: 
  • sasmodels/model_test.py

    rbedb9b0 r65314f7  
    201201                ({}, 'VR', None), 
    202202                ] 
    203  
    204             tests = smoke_tests + self.info.tests 
     203            tests = smoke_tests 
     204            if self.info.tests is not None: 
     205                tests += self.info.tests 
    205206            try: 
    206207                model = build_model(self.info, dtype=self.dtype, 
     
    371372        stream.writeln(traceback.format_exc()) 
    372373        return 
    373  
    374374    # Run the test suite 
    375375    suite.run(result) 
  • sasmodels/modelinfo.py

    r0bdddc2 ra85a569  
    727727    models when the model is first called, not when the model is loaded. 
    728728    """ 
     729    if hasattr(kernel_module, "model_info"): 
     730        # Custom sum/multi models 
     731        return kernel_module.model_info 
    729732    info = ModelInfo() 
    730733    #print("make parameter table", kernel_module.parameters) 
  • sasmodels/models/spherical_sld.py

    r63a7fe8 r2ad5d30  
    1818Interface shapes are as follows:: 
    1919 
    20     0: erf(|nu|*z) 
    21     1: Rpow(z^|nu|) 
    22     2: Lpow(z^|nu|) 
    23     3: Rexp(-|nu|z) 
    24     4: Lexp(-|nu|z) 
     20    0: erf($\nu z$) 
     21    1: Rpow($z^\nu$) 
     22    2: Lpow($z^\nu$) 
     23    3: Rexp($-\nu z$) 
     24    4: Lexp($-\nu z$) 
    2525 
    2626Definition 
  • sasmodels/product.py

    r8f04da4 r0edb6c1  
    6868    translate_name = dict((old.id, new.id) for old, new 
    6969                          in zip(s_pars.kernel_parameters[1:], s_list)) 
    70     demo = {} 
    71     demo.update(p_info.demo.items()) 
    72     demo.update((translate_name[k], v) for k, v in s_info.demo.items() 
    73                 if k not in ("background", "scale") and not k.startswith(ER_ID)) 
    7470    combined_pars = p_pars.kernel_parameters + s_list 
    7571    parameters = ParameterTable(combined_pars) 
    7672    parameters.max_pd = p_pars.max_pd + s_pars.max_pd 
     73    def random(): 
     74        combined_pars = p_info.random() 
     75        s_names = set(par.id for par in s_pars.kernel_parameters[1:]) 
     76        s = s_info.random() 
     77        combined_pars.update((translate_name[k], v) 
     78                    for k, v in s_info.random().items() 
     79                    if k in s_names) 
     80        return combined_pars 
    7781 
    7882    model_info = ModelInfo() 
    79     model_info.id = '*'.join((p_id, s_id)) 
    80     model_info.name = '*'.join((p_name, s_name)) 
     83    model_info.id = '@'.join((p_id, s_id)) 
     84    model_info.name = '@'.join((p_name, s_name)) 
    8185    model_info.filename = None 
    8286    model_info.title = 'Product of %s and %s'%(p_name, s_name) 
     
    8589    model_info.category = "custom" 
    8690    model_info.parameters = parameters 
     91    model_info.random = random 
    8792    #model_info.single = p_info.single and s_info.single 
    8893    model_info.structure_factor = False 
     
    95100    # TODO: delegate random to p_info, s_info 
    96101    #model_info.random = lambda: {} 
    97     model_info.demo = demo 
    98  
    99     ## Show the parameter table with the demo values 
     102 
     103    ## Show the parameter table 
    100104    #from .compare import get_pars, parlist 
    101105    #print("==== %s ====="%model_info.name) 
    102     #values = get_pars(model_info, use_demo=True) 
     106    #values = get_pars(model_info) 
    103107    #print(parlist(model_info, values, is2d=True)) 
    104108    return model_info 
     
    126130        self.P = P 
    127131        self.S = S 
     132        self.dtype = P.dtype 
    128133 
    129134    def make_kernel(self, q_vectors): 
  • sasmodels/sasview_model.py

    r44ca3e1 rad79f49  
    120120    else: 
    121121        model_info = modelinfo.make_model_info(kernel_module) 
    122         model = _make_model_from_info(model_info) 
     122        model = make_model_from_info(model_info) 
    123123    model.timestamp = getmtime(path) 
    124124 
     
    142142 
    143143 
     144def make_model_from_info(model_info): 
     145    # type: (ModelInfo) -> SasviewModelType 
     146    """ 
     147    Convert *model_info* into a SasView model wrapper. 
     148    """ 
     149    def __init__(self, multiplicity=None): 
     150        SasviewModel.__init__(self, multiplicity=multiplicity) 
     151    attrs = _generate_model_attributes(model_info) 
     152    attrs['__init__'] = __init__ 
     153    attrs['filename'] = model_info.filename 
     154    ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 
     155    return ConstructedModel 
     156 
     157 
    144158def _make_standard_model(name): 
    145159    # type: (str) -> SasviewModelType 
     
    153167    kernel_module = generate.load_kernel_module(name) 
    154168    model_info = modelinfo.make_model_info(kernel_module) 
    155     return _make_model_from_info(model_info) 
     169    return make_model_from_info(model_info) 
    156170 
    157171 
     
    187201    model_info = product.make_product_info(form_factor._model_info, 
    188202                                           structure_factor._model_info) 
    189     ConstructedModel = _make_model_from_info(model_info) 
     203    ConstructedModel = make_model_from_info(model_info) 
    190204    return ConstructedModel() 
    191205 
    192 def _make_model_from_info(model_info): 
    193     # type: (ModelInfo) -> SasviewModelType 
    194     """ 
    195     Convert *model_info* into a SasView model wrapper. 
    196     """ 
    197     def __init__(self, multiplicity=None): 
    198         SasviewModel.__init__(self, multiplicity=multiplicity) 
    199     attrs = _generate_model_attributes(model_info) 
    200     attrs['__init__'] = __init__ 
    201     attrs['filename'] = model_info.filename 
    202     ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 
    203     return ConstructedModel 
    204206 
    205207def _generate_model_attributes(model_info): 
Note: See TracChangeset for help on using the changeset viewer.