source: sasmodels/sasmodels/convert.py @ 256dfe1

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 256dfe1 was 256dfe1, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

allow comparison of multiplicity models with sasview 3.x

  • Property mode set to 100644
File size: 9.8 KB
Line 
1"""
2Convert models to and from sasview.
3"""
4from __future__ import print_function
5
6from os.path import join as joinpath, abspath, dirname
7import warnings
8import json
9
10# List of models which SasView versions don't contain the explicit 'scale' argument.
11# When converting such a model, please update this list.
12MODELS_WITHOUT_SCALE = [
13    'teubner_strey',
14    'broad_peak',
15    'two_lorentzian',
16    "two_power_law",
17    'gel_fit',
18    'gauss_lorentz_gel',
19    'be_polyelectrolyte',
20    'correlation_length',
21    'fractal_core_shell',
22    'binary_hard_sphere',
23    'raspberry'
24]
25
26# List of models which SasView versions don't contain the explicit 'background' argument.
27# When converting such a model, please update this list.
28MODELS_WITHOUT_BACKGROUND = [
29    'guinier',
30]
31
32MODELS_WITHOUT_VOLFRACTION = [
33    'fractal',
34    'vesicle',
35    'multilayer_vesicle',
36    'core_multi_shell',
37]
38
39
40# Convert new style names for polydispersity info to old style names
41PD_DOT = [
42    ("", ""),
43    ("_pd", ".width"),
44    ("_pd_n", ".npts"),
45    ("_pd_nsigma", ".nsigmas"),
46    ("_pd_type", ".type"),
47    ]
48
49CONVERSION_TABLE = None
50
51def _read_conversion_table():
52    global CONVERSION_TABLE
53    if CONVERSION_TABLE is None:
54        path = joinpath(dirname(abspath(__file__)), "convert.json")
55        with open(path) as fid:
56            CONVERSION_TABLE = json_load_byteified(fid)
57
58def json_load_byteified(file_handle):
59    return _byteify(
60        json.load(file_handle, object_hook=_byteify),
61        ignore_dicts=True
62    )
63
64def _byteify(data, ignore_dicts = False):
65    # if this is a unicode string, return its string representation
66    if isinstance(data, unicode):
67        return data.encode('utf-8')
68    # if this is a list of values, return list of byteified values
69    if isinstance(data, list):
70        return [ _byteify(item, ignore_dicts=True) for item in data ]
71    # if this is a dictionary, return dictionary of byteified keys and values
72    # but only if we haven't already byteified it
73    if isinstance(data, dict) and not ignore_dicts:
74        return dict((_byteify(key, ignore_dicts=True),
75                     _byteify(value, ignore_dicts=True))
76                    for key, value in data.items())
77    # if it's anything else, return it in its original form
78    return data
79
80
81def _convert_pars(pars, mapping):
82    """
83    Rename the parameters and any associated polydispersity attributes.
84    """
85    newpars = pars.copy()
86    for new, old in mapping.items():
87        if old == new: continue
88        for pd, dot in PD_DOT:
89            if old+dot in newpars:
90                if new is not None:
91                    newpars[new+pd] = pars[old+dot]
92                del newpars[old+dot]
93    return newpars
94
95def _rescale_sld(pars):
96    """
97    rescale all sld parameters in the new model definition by 1e6 so the
98    numbers are nicer.  Relies on the fact that all sld parameters in the
99    new model definition end with sld.
100    """
101    return dict((p, (v*1e6 if p.endswith('sld')
102                     else v*1e-15 if 'ndensity' in p
103                     else v))
104                for p, v in pars.items())
105
106def convert_model(name, pars):
107    """
108    Convert model from old style parameter names to new style.
109    """
110    _, _ = name, pars # lint
111    raise NotImplementedError
112    # need to load all new models in order to determine old=>new
113    # model name mapping
114
115def _unscale(par, scale):
116    return [pk*scale for pk in par] if isinstance(par, list) else par*scale
117
118def _unscale_sld(pars):
119    """
120    rescale all sld parameters in the new model definition by 1e6 so the
121    numbers are nicer.  Relies on the fact that all sld parameters in the
122    new model definition end with sld.
123    """
124    return dict((p, (_unscale(v,1e-6) if p.startswith('sld') or p.endswith('sld')
125                     else _unscale(v,1e15) if 'ndensity' in p
126                     else v))
127                for p, v in pars.items())
128
129def _remove_pd(pars, key, name):
130    """
131    Remove polydispersity from the parameter list.
132
133    Note: operates in place
134    """
135    # Bumps style parameter names
136    pd = pars.pop(key+".width", 0.0)
137    pd_n = pars.pop(key+".npts", 0)
138    if pd != 0.0 and pd_n != 0:
139        warnings.warn("parameter %s not polydisperse in sasview %s"%(key, name))
140    pars.pop(key+".nsigmas", None)
141    pars.pop(key+".type", None)
142    return pars
143
144def _revert_pars(pars, mapping):
145    """
146    Rename the parameters and any associated polydispersity attributes.
147    """
148    newpars = pars.copy()
149
150    for new, old in mapping.items():
151        for pd, dot in PD_DOT:
152            if old and old+pd == new+dot:
153                continue
154            if new+pd in newpars:
155                if old is not None:
156                    newpars[old+dot] = pars[new+pd]
157                del newpars[new+pd]
158    for k in list(newpars.keys()):
159        for pd, dot in PD_DOT[1:]:  # skip "" => ""
160            if k.endswith(pd):
161                newpars[k[:-len(pd)]+dot] = newpars[k]
162                del newpars[k]
163    return newpars
164
165def revert_name(model_info):
166    _read_conversion_table()
167    oldname, oldpars = CONVERSION_TABLE.get(model_info.id, [None, {}])
168    return oldname
169
170def _get_translation_table(model_info):
171    _read_conversion_table()
172    _, translation = CONVERSION_TABLE.get(model_info.id, [None, {}])
173    translation = translation.copy()
174    for p in model_info.parameters.kernel_parameters:
175        if p.length > 1:
176            newid = p.id
177            oldid = translation.get(p.id, p.id)
178            del translation[newid]
179            for k in range(1, p.length+1):
180                translation[newid+str(k)] = oldid+str(k)
181    # Remove control parameter from the result
182    if model_info.control:
183        translation[model_info.control] = None
184    return translation
185
186def _trim_vectors(model_info, pars, oldpars):
187    _read_conversion_table()
188    _, translation = CONVERSION_TABLE.get(model_info.id, [None, {}])
189    for p in model_info.parameters.kernel_parameters:
190        if p.length_control is not None:
191            n = int(pars[p.length_control])
192            oldname = translation.get(p.id, p.id)
193            for k in range(n+1, p.length+1):
194                for _, old in PD_DOT:
195                    oldpars.pop(oldname+str(k)+old, None)
196    return oldpars
197
198def revert_pars(model_info, pars):
199    """
200    Convert model from new style parameter names to old style.
201    """
202    if model_info.composition is not None:
203        composition_type, parts = model_info.composition
204        if composition_type == 'product':
205            P, S = parts
206            oldpars = {'scale':'scale_factor'}
207            oldpars.update(_get_old_pars(P))
208            oldpars.update(_get_old_pars(S))
209        else:
210            raise NotImplementedError("cannot convert to sasview sum")
211    else:
212        translation = _get_translation_table(model_info)
213    oldpars = _revert_pars(_unscale_sld(pars), translation)
214    oldpars = _trim_vectors(model_info, pars, oldpars)
215
216
217    # Note: update compare.constrain_pars to match
218    name = model_info.id
219    if name in MODELS_WITHOUT_SCALE or model_info.structure_factor:
220        if oldpars.pop('scale', 1.0) != 1.0:
221            warnings.warn("parameter scale not used in sasview %s"%name)
222    if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor:
223        if oldpars.pop('background', 0.0) != 0.0:
224            warnings.warn("parameter background not used in sasview %s"%name)
225
226
227    # If it is a product model P*S, then check the individual forms for special
228    # cases.  Note: despite the structure factor alone not having scale or
229    # background, the product model does, so this is below the test for
230    # models without scale or background.
231    namelist = name.split('*') if '*' in name else [name]
232    for name in namelist:
233        if name in MODELS_WITHOUT_VOLFRACTION:
234            del oldpars['volfraction']
235        if name == 'stacked_disks':
236            _remove_pd(oldpars, 'n_stacking', name)
237        elif name == 'pearl_necklace':
238            _remove_pd(oldpars, 'num_pearls', name)
239            _remove_pd(oldpars, 'thick_string', name)
240        elif name == 'core_shell_parallelepiped':
241            _remove_pd(oldpars, 'rimA', name)
242            _remove_pd(oldpars, 'rimB', name)
243            _remove_pd(oldpars, 'rimC', name)
244        elif name == 'rpa':
245            # convert scattering lengths from femtometers to centimeters
246            for p in "L1", "L2", "L3", "L4":
247                if p in oldpars: oldpars[p] *= 1e-13
248        elif name == 'core_shell_parallelepiped':
249            _remove_pd(oldpars, 'rimA', name)
250        elif name in ['mono_gauss_coil','poly_gauss_coil']:
251            del oldpars['i_zero']
252
253    return oldpars
254
255def constrain_new_to_old(model_info, pars):
256    """
257    Restrict parameter values to those that will match sasview.
258    """
259    name = model_info.id
260    # Note: update convert.revert_model to match
261    if name in MODELS_WITHOUT_SCALE or model_info.structure_factor:
262        pars['scale'] = 1
263    if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor:
264        pars['background'] = 0
265    # sasview multiplies background by structure factor
266    if '*' in name:
267        pars['background'] = 0
268
269    # If it is a product model P*S, then check the individual forms for special
270    # cases.  Note: despite the structure factor alone not having scale or
271    # background, the product model does, so this is below the test for
272    # models without scale or background.
273    namelist = name.split('*') if '*' in name else [name]
274    for name in namelist:
275        if name in MODELS_WITHOUT_VOLFRACTION:
276            pars['volfraction'] = 1
277        if name == 'pearl_necklace':
278            pars['string_thickness_pd_n'] = 0
279            pars['number_of_pearls_pd_n'] = 0
280        elif name == 'line':
281            pars['scale'] = 1
282            pars['background'] = 0
283        elif name == 'rpa':
284            pars['case_num'] = int(pars['case_num'])
285        elif name == 'mono_gauss_coil':
286            pars['i_zero'] = 1
287        elif name == 'poly_gauss_coil':
288            pars['i_zero'] = 1
289
Note: See TracBrowser for help on using the repository browser.