Changeset 9c44b7b in sasmodels


Ignore:
Timestamp:
Mar 9, 2017 6:42:21 PM (2 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:
3a45c2c
Parents:
68f45cb (diff), 0011ecc (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-843

Location:
sasmodels
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/compare.py

    rd504bcd rf72d70a  
    321321    if '*' in name: 
    322322        name = name.split('*')[0] 
     323 
     324    # Suppress magnetism for python models (not yet implemented) 
     325    if callable(model_info.Iq): 
     326        pars.update(suppress_magnetism(pars)) 
    323327 
    324328    if name == 'barbell': 
  • sasmodels/compare_many.py

    r424fe00 rf72d70a  
    106106    header = ('\n"Model","%s","Count","%d","Dimension","%s"' 
    107107              % (name, N, "2D" if is_2d else "1D")) 
    108     if not mono: header += ',"Cutoff",%g'%(cutoff,) 
     108    if not mono: 
     109        header += ',"Cutoff",%g'%(cutoff,) 
    109110    print(header) 
    110111 
     
    161162    max_diff = [0] 
    162163    for k in range(N): 
    163         print("%s %d"%(name, k), file=sys.stderr) 
     164        print("Model %s %d"%(name, k+1), file=sys.stderr) 
    164165        seed = np.random.randint(1e6) 
    165166        pars_i = randomize_pars(model_info, pars, seed) 
    166167        constrain_pars(model_info, pars_i) 
    167         constrain_new_to_old(model_info, pars_i) 
     168        if 'sasview' in (base, comp): 
     169            constrain_new_to_old(model_info, pars_i) 
    168170        if mono: 
    169171            pars_i = suppress_pd(pars_i) 
     
    187189    Print the command usage string. 
    188190    """ 
    189     print("usage: compare_many.py MODEL COUNT (1dNQ|2dNQ) (CUTOFF|mono) (single|double|quad)") 
     191    print("usage: compare_many.py MODEL COUNT (1dNQ|2dNQ) (CUTOFF|mono) (single|double|quad)", 
     192          file=sys.stderr) 
    190193 
    191194 
     
    204207    print("""\ 
    205208 
    206 MODEL is the model name of the model or "all" for all the models 
    207 in alphabetical order. 
     209MODEL is the model name of the model or one of the model types listed in 
     210sasmodels.core.list_models (all, py, c, double, single, opencl, 1d, 2d, 
     211nonmagnetic, magnetic).  Model types can be combined, such as 2d+single. 
    208212 
    209213COUNT is the number of randomly generated parameter sets to try. A value 
     
    220224below the cutoff will be ignored.  Use "mono" for monodisperse models.  The 
    221225choice of polydisperse parameters, and the number of points in the distribution 
    222 is set in compare.py defaults for each model. 
     226is set in compare.py defaults for each model.  Polydispersity is given in the 
     227"demo" attribute of each model. 
    223228 
    224229PRECISION is the floating point precision to use for comparisons.  If two 
    225 precisions are given, then compare one to the other, ignoring sasview. 
     230precisions are given, then compare one to the other.  Precision is one of 
     231fast, single, double for GPU or single!, double!, quad! for DLL.  If no 
     232precision is given, then use single and double! respectively. 
    226233 
    227234Available models: 
     
    233240    Main program. 
    234241    """ 
    235     if len(argv) not in (5, 6): 
     242    if len(argv) not in (3, 4, 5, 6): 
    236243        print_help() 
    237244        return 
    238245 
    239     model = argv[0] 
    240     if not (model in MODELS) and (model != "all"): 
    241         print('Bad model %s.  Use "all" or one of:'%model) 
     246    target = argv[0] 
     247    try: 
     248        model_list = [target] if target in MODELS else core.list_models(target) 
     249    except ValueError: 
     250        print('Bad model %s.  Use model type or one of:' % target, file=sys.stderr) 
    242251        print_models() 
     252        print('model types: all, py, c, double, single, opencl, 1d, 2d, nonmagnetic, magnetic') 
    243253        return 
    244254    try: 
     
    247257        assert argv[2][1] == 'd' 
    248258        Nq = int(argv[2][2:]) 
    249         mono = argv[3] == 'mono' 
     259        mono = len(argv) <= 3 or argv[3] == 'mono' 
    250260        cutoff = float(argv[3]) if not mono else 0 
    251         base = argv[4] 
    252         comp = argv[5] if len(argv) > 5 else "sasview" 
     261        base = argv[4] if len(argv) > 4 else "single" 
     262        comp = argv[5] if len(argv) > 5 else "double!" 
    253263    except Exception: 
    254264        traceback.print_exc() 
     
    258268    data, index = make_data({'qmax':1.0, 'is2d':is2D, 'nq':Nq, 'res':0., 
    259269                             'accuracy': 'Low', 'view':'log', 'zero': False}) 
    260     model_list = [model] if model != "all" else MODELS 
    261270    for model in model_list: 
    262271        compare_instance(model, data, index, N=count, mono=mono, 
  • sasmodels/core.py

    r52e9a45 r5124c969  
    6969        * magnetic: models with an sld 
    7070        * nommagnetic: models without an sld 
    71     """ 
    72     if kind and kind not in KINDS: 
     71 
     72    For multiple conditions, combine with plus.  For example, *c+single+2d* 
     73    would return all oriented models implemented in C which can be computed 
     74    accurately with single precision arithmetic. 
     75    """ 
     76    if kind and any(k not in KINDS for k in kind.split('+')): 
    7377        raise ValueError("kind not in " + ", ".join(KINDS)) 
    7478    files = sorted(glob(joinpath(generate.MODEL_PATH, "[a-zA-Z]*.py"))) 
    7579    available_models = [basename(f)[:-3] for f in files] 
    76     selected = [name for name in available_models if _matches(name, kind)] 
     80    if kind and '+' in kind: 
     81        all_kinds = kind.split('+') 
     82        condition = lambda name: all(_matches(name, k) for k in all_kinds) 
     83    else: 
     84        condition = lambda name: _matches(name, kind) 
     85    selected = [name for name in available_models if condition(name)] 
    7786 
    7887    return selected 
  • sasmodels/model_test.py

    r479d0f3 r598857b  
    9797        is_py = callable(model_info.Iq) 
    9898 
     99        # Some OpenCL drivers seem to be flaky, and are not producing the 
     100        # expected result.  Since we don't have known test values yet for 
     101        # all of our models, we are instead going to compare the results 
     102        # for the 'smoke test' (that is, evaluation at q=0.1 for the default 
     103        # parameters just to see that the model runs to completion) between 
     104        # the OpenCL and the DLL.  To do this, we define a 'stash' which is 
     105        # shared between OpenCL and DLL tests.  This is just a list.  If the 
     106        # list is empty (which it will be when DLL runs, if the DLL runs 
     107        # first), then the results are appended to the list.  If the list 
     108        # is not empty (which it will be when OpenCL runs second), the results 
     109        # are compared to the results stored in the first element of the list. 
     110        # This is a horrible stateful hack which only makes sense because the 
     111        # test suite is thrown away after being run once. 
     112        stash = [] 
     113 
    99114        if is_py:  # kernel implemented in python 
    100115            test_name = "Model: %s, Kernel: python"%model_name 
     
    103118                                 test_method_name, 
    104119                                 platform="dll",  # so that 
    105                                  dtype="double") 
     120                                 dtype="double", 
     121                                 stash=stash) 
    106122            suite.addTest(test) 
    107123        else:   # kernel implemented in C 
     124 
     125            # test using dll if desired 
     126            if 'dll' in loaders or not core.HAVE_OPENCL: 
     127                test_name = "Model: %s, Kernel: dll"%model_name 
     128                test_method_name = "test_%s_dll" % model_info.id 
     129                test = ModelTestCase(test_name, model_info, 
     130                                     test_method_name, 
     131                                     platform="dll", 
     132                                     dtype="double", 
     133                                     stash=stash) 
     134                suite.addTest(test) 
     135 
    108136            # test using opencl if desired and available 
    109137            if 'opencl' in loaders and core.HAVE_OPENCL: 
     
    116144                test = ModelTestCase(test_name, model_info, 
    117145                                     test_method_name, 
    118                                      platform="ocl", dtype=None) 
     146                                     platform="ocl", dtype=None, 
     147                                     stash=stash) 
    119148                #print("defining", test_name) 
    120                 suite.addTest(test) 
    121  
    122             # test using dll if desired 
    123             if 'dll' in loaders or not core.HAVE_OPENCL: 
    124                 test_name = "Model: %s, Kernel: dll"%model_name 
    125                 test_method_name = "test_%s_dll" % model_info.id 
    126                 test = ModelTestCase(test_name, model_info, 
    127                                      test_method_name, 
    128                                      platform="dll", 
    129                                      dtype="double") 
    130149                suite.addTest(test) 
    131150 
     
    144163        """ 
    145164        def __init__(self, test_name, model_info, test_method_name, 
    146                      platform, dtype): 
    147             # type: (str, ModelInfo, str, str, DType) -> None 
     165                     platform, dtype, stash): 
     166            # type: (str, ModelInfo, str, str, DType, List[Any]) -> None 
    148167            self.test_name = test_name 
    149168            self.info = model_info 
    150169            self.platform = platform 
    151170            self.dtype = dtype 
     171            self.stash = stash  # container for the results of the first run 
    152172 
    153173            setattr(self, test_method_name, self.run_all) 
     
    167187                #({}, (0.0, 0.0), None), 
    168188                # test vector form 
    169                 ({}, [0.1]*2, [None]*2), 
     189                ({}, [0.001, 0.01, 0.1], [None]*3), 
    170190                ({}, [(0.1, 0.1)]*2, [None]*2), 
    171191                # test that ER/VR will run if they exist 
     
    174194                ] 
    175195 
    176             tests = self.info.tests 
     196            tests = smoke_tests + self.info.tests 
    177197            try: 
    178198                model = build_model(self.info, dtype=self.dtype, 
    179199                                    platform=self.platform) 
    180                 for test in smoke_tests + tests: 
    181                     self.run_one(model, test) 
    182  
    183                 if not tests and self.platform == "dll": 
    184                     ## Uncomment the following to make forgetting the test 
    185                     ## values an error.  Only do so for the "dll" tests 
    186                     ## to reduce noise from both opencl and dll, and because 
    187                     ## python kernels use platform="dll". 
    188                     #raise Exception("No test cases provided") 
    189                     pass 
     200                results = [self.run_one(model, test) for test in tests] 
     201                if self.stash: 
     202                    for test, target, actual in zip(tests, self.stash[0], results): 
     203                        assert np.all(abs(target-actual) < 5e-5*abs(actual)),\ 
     204                            "GPU/CPU comparison expected %s but got %s for %s"%(target, actual, test[0]) 
     205                else: 
     206                    self.stash.append(results) 
     207 
     208                # Check for missing tests.  Only do so for the "dll" tests 
     209                # to reduce noise from both opencl and dll, and because 
     210                # python kernels use platform="dll". 
     211                if self.platform == "dll": 
     212                    missing = [] 
     213                    ## Uncomment the following to require test cases 
     214                    #missing = self._find_missing_tests() 
     215                    if missing: 
     216                        raise ValueError("Missing tests for "+", ".join(missing)) 
    190217 
    191218            except: 
    192219                annotate_exception(self.test_name) 
    193220                raise 
     221 
     222        def _find_missing_tests(self): 
     223            # type: () -> None 
     224            """make sure there are 1D, 2D, ER and VR tests as appropriate""" 
     225            model_has_VR = callable(self.info.VR) 
     226            model_has_ER = callable(self.info.ER) 
     227            model_has_1D = True 
     228            model_has_2D = any(p.type == 'orientation' 
     229                               for p in self.info.parameters.kernel_parameters) 
     230 
     231            # Lists of tests that have a result that is not None 
     232            single = [test for test in self.info.tests 
     233                      if not isinstance(test[2], list) and test[2] is not None] 
     234            tests_has_VR = any(test[1] == 'VR' for test in single) 
     235            tests_has_ER = any(test[1] == 'ER' for test in single) 
     236            tests_has_1D_single = any(isinstance(test[1], float) for test in single) 
     237            tests_has_2D_single = any(isinstance(test[1], tuple) for test in single) 
     238 
     239            multiple = [test for test in self.info.tests 
     240                        if isinstance(test[2], list) 
     241                            and not all(result is None for result in test[2])] 
     242            tests_has_1D_multiple = any(isinstance(test[1][0], float) 
     243                                        for test in multiple) 
     244            tests_has_2D_multiple = any(isinstance(test[1][0], tuple) 
     245                                        for test in multiple) 
     246 
     247            missing = [] 
     248            if model_has_VR and not tests_has_VR: 
     249                missing.append("VR") 
     250            if model_has_ER and not tests_has_ER: 
     251                missing.append("ER") 
     252            if model_has_1D and not (tests_has_1D_single or tests_has_1D_multiple): 
     253                missing.append("1D") 
     254            if model_has_2D and not (tests_has_2D_single or tests_has_2D_multiple): 
     255                missing.append("2D") 
     256 
     257            return missing 
    194258 
    195259        def run_one(self, model, test): 
     
    207271 
    208272            if x[0] == 'ER': 
    209                 actual = [call_ER(model.info, pars)] 
     273                actual = np.array([call_ER(model.info, pars)]) 
    210274            elif x[0] == 'VR': 
    211                 actual = [call_VR(model.info, pars)] 
     275                actual = np.array([call_VR(model.info, pars)]) 
    212276            elif isinstance(x[0], tuple): 
    213277                qx, qy = zip(*x) 
     
    238302                                    'f(%s); expected:%s; actual:%s' 
    239303                                    % (xi, yi, actual_yi)) 
     304            return actual 
    240305 
    241306    return ModelTestCase 
  • sasmodels/modelinfo.py

    rf88e248 r9c44b7b  
    734734    info.docs = kernel_module.__doc__ 
    735735    info.category = getattr(kernel_module, 'category', None) 
    736     info.single = getattr(kernel_module, 'single', True) 
    737     info.opencl = getattr(kernel_module, 'opencl', True) 
    738736    info.structure_factor = getattr(kernel_module, 'structure_factor', False) 
    739737    info.profile_axes = getattr(kernel_module, 'profile_axes', ['x', 'y']) 
     
    749747    info.profile = getattr(kernel_module, 'profile', None) # type: ignore 
    750748    info.sesans = getattr(kernel_module, 'sesans', None) # type: ignore 
     749    # Default single and opencl to True for C models.  Python models have callable Iq. 
     750    info.opencl = getattr(kernel_module, 'opencl', not callable(info.Iq)) 
     751    info.single = getattr(kernel_module, 'single', not callable(info.Iq)) 
    751752 
    752753    # multiplicity info 
  • sasmodels/conversion_table.py

    r790b16bb r0c2da4b  
    549549            "radius": "core_radius", 
    550550            "sld_solvent": "core_sld", 
    551             "n_pairs": "n_pairs", 
     551            "n_shells": "n_pairs", 
    552552            "thick_shell": "s_thickness", 
    553553            "sld": "shell_sld", 
  • sasmodels/models/multilayer_vesicle.c

    rc3ccaec rec1d4bc  
    1 static 
    2 double multilayer_vesicle_kernel(double q, 
     1static double 
     2form_volume(double radius, 
     3          double thick_shell, 
     4          double thick_solvent, 
     5          double fp_n_shells) 
     6{ 
     7    int n_shells = (int)(fp_n_shells + 0.5); 
     8    double R_N = radius + n_shells*(thick_shell+thick_solvent) - thick_solvent; 
     9    return M_4PI_3*cube(R_N); 
     10} 
     11 
     12static double 
     13multilayer_vesicle_kernel(double q, 
    314          double volfraction, 
    415          double radius, 
     
    718          double sld_solvent, 
    819          double sld, 
    9           int n_pairs) 
     20          int n_shells) 
    1021{ 
    1122    //calculate with a loop, two shells at a time 
     
    2940 
    3041        //do 2 layers at a time 
    31         ii += 1; 
     42        ii++; 
    3243 
    33     } while(ii <= n_pairs-1);  //change to make 0 < n_pairs < 2 correspond to 
     44    } while(ii <= n_shells-1);  //change to make 0 < n_shells < 2 correspond to 
    3445                               //unilamellar vesicles (C. Glinka, 11/24/03) 
    3546 
    36     fval *= volfraction*1.0e-4*fval/voli; 
    37  
    38     return(fval); 
     47    return 1.0e-4*volfraction*fval*fval;  // Volume normalization happens in caller 
    3948} 
    4049 
    41 static 
    42 double Iq(double q, 
     50static double 
     51Iq(double q, 
    4352          double volfraction, 
    4453          double radius, 
     
    4756          double sld_solvent, 
    4857          double sld, 
    49           double fp_n_pairs) 
     58          double fp_n_shells) 
    5059{ 
    51     int n_pairs = (int)(fp_n_pairs + 0.5); 
     60    int n_shells = (int)(fp_n_shells + 0.5); 
    5261    return multilayer_vesicle_kernel(q, 
    5362           volfraction, 
     
    5766           sld_solvent, 
    5867           sld, 
    59            n_pairs); 
     68           n_shells); 
    6069} 
    6170 
  • sasmodels/models/multilayer_vesicle.py

    rc3ccaec r68f45cb  
    55This model is a trivial extension of the core_shell_sphere function to include 
    66*N* shells where the core is filled with solvent and the shells are interleaved 
    7 with layers of solvent. For *N = 1*, this returns the same as the vesicle model, 
     7with layers of solvent. For $N = 1$, this returns the same as the vesicle model, 
    88except for the normalisation, which here is to outermost volume. 
    99The shell thicknessess and SLD are constant for all shells as expected for 
     
    1919 
    2020.. math:: 
    21     P(q) = \text{scale} \cdot \frac{V_f}{V_t} F^2(q) + \text{background} 
     21    P(q) = \text{scale} \cdot \frac{\phi}{V(R_N)} F^2(q) + \text{background} 
     22 
     23where 
     24 
     25.. math:: 
     26     F(q) = (\rho_\text{shell}-\rho_\text{solv}) \sum_{i=1}^{N} \left[ 
     27     3V(r_i)\frac{\sin(qr_i) - qr_i\cos(qr_i)}{(qr_i)^3} 
     28     - 3V(R_i)\frac{\sin(qR_i) - qR_i\cos(qR_i)}{(qR_i)^3} 
     29     \right] 
    2230 
    2331for 
    2432 
    2533.. math:: 
    26     F(q) = (\rho_\text{shell}-\rho_\text{solv}) \sum_{i=1}^{n_\text{pairs}} 
    27         \left[ 
    28           3V(R_i)\frac{\sin(qR_i)-qR_i\cos(qR_i)}{(qR_i)^3} \\ 
    29           - 3V(R_i+t_s)\frac{\sin(q(R_i+t_s))-q(R_i+t_s)\cos(q(R_i+t_s))}{(q(R_i+t_s))^3} 
    30         \right] 
    3134 
    32 and 
     35     r_i &= r_c + (i-1)(t_s + t_w) && \text{ solvent radius before shell } i \\ 
     36     R_i &= r_i + t_s && \text{ shell radius for shell } i 
    3337 
    34 .. math:: 
    35      R_i = r_c + (i-1)(t_s + t_w) 
     38$\phi$ is the volume fraction of particles, $V(r)$ is the volume of a sphere 
     39of radius $r$, $r_c$ is the radius of the core, $t_s$ is the thickness of 
     40the shell, $t_w$ is the thickness of the solvent layer between the shells, 
     41$\rho_\text{shell}$ is the scattering length density of a shell, and 
     42$\rho_\text{solv}$ is the scattering length density of the solvent. 
    3643 
    37 where $V_f$ is the volume fraction of particles, $V_t$ is the volume of the 
    38 whole particle, $V(r)$ is the volume of a sphere of radius $r$, $r_c$ is the 
    39 radius of the core, $\rho_\text{shell}$ is the scattering length density of a 
    40 shell, $\rho_\text{solv}$ is the scattering length density of the solvent. 
     44The outer-most shell radius $R_N$ is used as the effective radius 
     45for $P(Q)$ when $P(Q) * S(Q)$ is applied. 
    4146 
    42 The outer most radius, $r_o = R_n + t_s$, is used for both the volume fraction 
    43 normalization and for the effective radius for *S(Q)* when $P(Q) * S(Q)$ 
    44 is applied. 
     47For mixed systems in which some vesicles have 1 shell, some have 2, 
     48etc., use polydispersity on $N$ to model the data.  For example, 
     49create a file such as *shell_dist.txt* containing the relative portion 
     50of each vesicle size:: 
     51 
     52    1 20 
     53    2  4 
     54    3  1 
     55 
     56Turn on polydispersity and select an array distribution for the *n_shells* 
     57parameter.  Choose the above *shell_dist.txt* file, and the model will be 
     58computed with 80% 1-shell vesicles, 16% 2-shell vesicles and 4% 
     593-shell vesicles. 
    4560 
    4661The 2D scattering intensity is the same as 1D, regardless of the orientation 
     
    86101    sld_solvent: solvent scattering length density 
    87102    sld: shell scattering length density 
    88     n_pairs:number of "shell plus solvent" layer pairs 
     103    n_shells:number of "shell plus solvent" layer pairs 
    89104    background: incoherent background 
    90105        """ 
     
    95110parameters = [ 
    96111    ["volfraction", "",  0.05, [0.0, 1],  "", "volume fraction of vesicles"], 
    97     ["radius", "Ang", 60.0, [0.0, inf],  "", "radius of solvent filled core"], 
    98     ["thick_shell", "Ang",        10.0, [0.0, inf],  "", "thickness of one shell"], 
    99     ["thick_solvent", "Ang",        10.0, [0.0, inf],  "", "solvent thickness between shells"], 
     112    ["radius", "Ang", 60.0, [0.0, inf],  "volume", "radius of solvent filled core"], 
     113    ["thick_shell", "Ang",        10.0, [0.0, inf],  "volume", "thickness of one shell"], 
     114    ["thick_solvent", "Ang",        10.0, [0.0, inf],  "volume", "solvent thickness between shells"], 
    100115    ["sld_solvent",    "1e-6/Ang^2",  6.4, [-inf, inf], "sld", "solvent scattering length density"], 
    101116    ["sld",   "1e-6/Ang^2",  0.4, [-inf, inf], "sld", "Shell scattering length density"], 
    102     ["n_pairs",     "",            2.0, [1.0, inf],  "", "Number of shell plus solvent layer pairs"], 
     117    ["n_shells",     "",            2.0, [1.0, inf],  "volume", "Number of shell plus solvent layer pairs"], 
    103118    ] 
    104119# pylint: enable=bad-whitespace, line-too-long 
    105120 
     121# TODO: proposed syntax for specifying which parameters can be polydisperse 
     122#polydispersity = ["radius", "thick_shell"] 
     123 
    106124source = ["lib/sas_3j1x_x.c", "multilayer_vesicle.c"] 
    107125 
    108 # TODO: the following line does nothing 
    109 polydispersity = ["radius", "n_pairs"] 
     126def ER(radius, thick_shell, thick_solvent, n_shells): 
     127    n_shells = int(n_shells+0.5) 
     128    return radius + n_shells * (thick_shell + thick_solvent) - thick_solvent 
    110129 
    111130demo = dict(scale=1, background=0, 
     
    116135            sld_solvent=6.4, 
    117136            sld=0.4, 
    118             n_pairs=2.0) 
     137            n_shells=2.0) 
    119138 
    120139tests = [ 
     
    125144      'sld_solvent': 6.4, 
    126145      'sld': 0.4, 
    127       'n_pairs': 2.0, 
     146      'n_shells': 2.0, 
    128147      'scale': 1.0, 
    129148      'background': 0.001, 
     
    136155      'sld_solvent': 6.4, 
    137156      'sld': 0.4, 
    138       'n_pairs': 2.0, 
     157      'n_shells': 2.0, 
    139158      'scale': 1.0, 
    140159      'background': 0.001, 
  • sasmodels/product.py

    r9951a86 rf88e248  
    4545    # structure factor calculator.  Structure factors should not 
    4646    # have any magnetic parameters 
    47     assert(s_info.parameters.kernel_parameters[0].id == ER_ID) 
    48     assert(s_info.parameters.kernel_parameters[1].id == VF_ID) 
    49     assert(s_info.parameters.magnetism_index == []) 
     47    if not s_info.parameters.kernel_parameters[0].id == ER_ID: 
     48        raise TypeError("S needs %s as first parameter"%ER_ID) 
     49    if not s_info.parameters.kernel_parameters[1].id == VF_ID: 
     50        raise TypeError("S needs %s as second parameter"%VF_ID) 
     51    if not s_info.parameters.magnetism_index == []: 
     52        raise TypeError("S should not have SLD parameters") 
    5053    p_id, p_name, p_pars = p_info.id, p_info.name, p_info.parameters 
    5154    s_id, s_name, s_pars = s_info.id, s_info.name, s_info.parameters 
    52     p_set = set(p.id for p in p_pars.call_parameters) 
    53     s_set = set(p.id for p in s_pars.call_parameters) 
    54  
    55     if p_set & s_set: 
    56         # there is some overlap between the parameter names; tag the 
    57         # overlapping S parameters with name_S. 
    58         # Skip the first parameter of s, which is effective radius 
    59         s_list = [(suffix_parameter(par) if par.id in p_set else par) 
    60                   for par in s_pars.kernel_parameters[1:]] 
    61     else: 
    62         # Skip the first parameter of s, which is effective radius 
    63         s_list = s_pars.kernel_parameters[1:] 
     55 
     56    # Create list of parameters for the combined model.  Skip the first 
     57    # parameter of S, which we verified above is effective radius.  If there 
     58    # are any names in P that overlap with those in S, modify the name in S 
     59    # to distinguish it. 
     60    p_set = set(p.id for p in p_pars.kernel_parameters) 
     61    s_list = [(_tag_parameter(par) if par.id in p_set else par) 
     62              for par in s_pars.kernel_parameters[1:]] 
     63    # Check if still a collision after renaming.  This could happen if for 
     64    # example S has volfrac and P has both volfrac and volfrac_S. 
     65    if any(p.id in p_set for p in s_list): 
     66        raise TypeError("name collision: P has P.name and P.name_S while S has S.name") 
     67 
    6468    translate_name = dict((old.id, new.id) for old, new 
    6569                          in zip(s_pars.kernel_parameters[1:], s_list)) 
    6670    demo = {} 
    67     demo.update((k, v) for k, v in p_info.demo.items() 
    68                 if k not in ("background", "scale")) 
     71    demo.update(p_info.demo.items()) 
    6972    demo.update((translate_name[k], v) for k, v in s_info.demo.items() 
    7073                if k not in ("background", "scale") and not k.startswith(ER_ID)) 
     
    9093    # Remember the component info blocks so we can build the model 
    9194    model_info.composition = ('product', [p_info, s_info]) 
    92     model_info.demo = {} 
     95    model_info.demo = demo 
     96 
     97    ## Show the parameter table with the demo values 
     98    #from .compare import get_pars, parlist 
     99    #print("==== %s ====="%model_info.name) 
     100    #values = get_pars(model_info, use_demo=True) 
     101    #print(parlist(model_info, values, is2d=True)) 
    93102    return model_info 
    94103 
    95 def suffix_parameter(par, suffix): 
     104def _tag_parameter(par): 
     105    """ 
     106    Tag the parameter name with _S to indicate that the parameter comes from 
     107    the structure factor parameter set.  This is only necessary if the 
     108    form factor model includes a parameter of the same name as a parameter 
     109    in the structure factor. 
     110    """ 
    96111    par = copy(par) 
    97     par.name = par.name + " S" 
     112    # Protect against a vector parameter in S by appending the vector length 
     113    # to the renamed parameter.  Note: haven't tested this since no existing 
     114    # structure factor models contain vector parameters. 
     115    vector_length = par.name[len(par.id):] 
    98116    par.id = par.id + "_S" 
     117    par.name = par.id + vector_length 
    99118    return par 
    100119 
Note: See TracChangeset for help on using the changeset viewer.