source: sasmodels/sasmodels/convert.py @ 51ec7e8

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

working rpa implementation

  • Property mode set to 100644
File size: 10.5 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 math
8import warnings
9import json
10
11# List of models which SasView versions don't contain the explicit 'scale' argument.
12# When converting such a model, please update this list.
13MODELS_WITHOUT_SCALE = [
14    'teubner_strey',
15    'broad_peak',
16    'two_lorentzian',
17    "two_power_law",
18    'gel_fit',
19    'gauss_lorentz_gel',
20    'be_polyelectrolyte',
21    'correlation_length',
22    'fractal_core_shell',
23    'binary_hard_sphere',
24    'raspberry'
25]
26
27# List of models which SasView versions don't contain the explicit 'background' argument.
28# When converting such a model, please update this list.
29MODELS_WITHOUT_BACKGROUND = [
30    'guinier',
31]
32
33MODELS_WITHOUT_VOLFRACTION = [
34    'fractal',
35    'vesicle',
36    'multilayer_vesicle',
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            translation.pop(newid, None)
179            for k in range(1, p.length+1):
180                if newid+str(k) not in translation:
181                    translation[newid+str(k)] = oldid+str(k)
182    # Remove control parameter from the result
183    if model_info.control:
184        translation[model_info.control] = None
185    return translation
186
187def _trim_vectors(model_info, pars, oldpars):
188    _read_conversion_table()
189    _, translation = CONVERSION_TABLE.get(model_info.id, [None, {}])
190    for p in model_info.parameters.kernel_parameters:
191        if p.length_control is not None:
192            n = int(pars[p.length_control])
193            oldname = translation.get(p.id, p.id)
194            for k in range(n+1, p.length+1):
195                for _, old in PD_DOT:
196                    oldpars.pop(oldname+str(k)+old, None)
197    return oldpars
198
199def revert_pars(model_info, pars):
200    """
201    Convert model from new style parameter names to old style.
202    """
203    if model_info.composition is not None:
204        composition_type, parts = model_info.composition
205        if composition_type == 'product':
206            P, S = parts
207            oldpars = {'scale':'scale_factor'}
208            oldpars.update(_get_old_pars(P))
209            oldpars.update(_get_old_pars(S))
210        else:
211            raise NotImplementedError("cannot convert to sasview sum")
212    else:
213        translation = _get_translation_table(model_info)
214        oldpars = _revert_pars(_unscale_sld(pars), translation)
215        oldpars = _trim_vectors(model_info, pars, oldpars)
216
217
218    # Note: update compare.constrain_pars to match
219    name = model_info.id
220    if name in MODELS_WITHOUT_SCALE or model_info.structure_factor:
221        if oldpars.pop('scale', 1.0) != 1.0:
222            warnings.warn("parameter scale not used in sasview %s"%name)
223    if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor:
224        if oldpars.pop('background', 0.0) != 0.0:
225            warnings.warn("parameter background not used in sasview %s"%name)
226
227
228    # If it is a product model P*S, then check the individual forms for special
229    # cases.  Note: despite the structure factor alone not having scale or
230    # background, the product model does, so this is below the test for
231    # models without scale or background.
232    namelist = name.split('*') if '*' in name else [name]
233    for name in namelist:
234        if name in MODELS_WITHOUT_VOLFRACTION:
235            del oldpars['volfraction']
236        if name == 'stacked_disks':
237            _remove_pd(oldpars, 'n_stacking', name)
238        elif name == 'pearl_necklace':
239            _remove_pd(oldpars, 'num_pearls', name)
240            _remove_pd(oldpars, 'thick_string', name)
241        elif name == 'core_shell_parallelepiped':
242            _remove_pd(oldpars, 'rimA', name)
243            _remove_pd(oldpars, 'rimB', name)
244            _remove_pd(oldpars, 'rimC', name)
245        elif name == 'rpa':
246            # convert scattering lengths from femtometers to centimeters
247            for p in "L1", "L2", "L3", "L4":
248                if p in oldpars: oldpars[p] *= 1e-13
249            if pars['case_num'] < 2:
250                for k in ("a", "b"):
251                    for p in ("L", "N", "Phi", "b", "v"):
252                        oldpars.pop(p+k, None)
253                for k in "Kab,Kac,Kad,Kbc,Kbd".split(','):
254                    oldpars.pop(k, None)
255            elif pars['case_num'] < 5:
256                for k in ("a",):
257                    for p in ("L", "N", "Phi", "b", "v"):
258                        oldpars.pop(p+k, None)
259                for k in "Kab,Kac,Kad".split(','):
260                    oldpars.pop(k, None)
261        elif name == 'core_shell_parallelepiped':
262            _remove_pd(oldpars, 'rimA', name)
263        elif name in ['mono_gauss_coil','poly_gauss_coil']:
264            del oldpars['i_zero']
265
266    return oldpars
267
268def constrain_new_to_old(model_info, pars):
269    """
270    Restrict parameter values to those that will match sasview.
271    """
272    name = model_info.id
273    # Note: update convert.revert_model to match
274    if name in MODELS_WITHOUT_SCALE or model_info.structure_factor:
275        pars['scale'] = 1
276    if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor:
277        pars['background'] = 0
278    # sasview multiplies background by structure factor
279    if '*' in name:
280        pars['background'] = 0
281
282    # If it is a product model P*S, then check the individual forms for special
283    # cases.  Note: despite the structure factor alone not having scale or
284    # background, the product model does, so this is below the test for
285    # models without scale or background.
286    namelist = name.split('*') if '*' in name else [name]
287    for name in namelist:
288        if name in MODELS_WITHOUT_VOLFRACTION:
289            pars['volfraction'] = 1
290        if name == 'pearl_necklace':
291            pars['string_thickness_pd_n'] = 0
292            pars['number_of_pearls_pd_n'] = 0
293        elif name == 'line':
294            pars['scale'] = 1
295            pars['background'] = 0
296        elif name == 'rpa':
297            pars['case_num'] = int(pars['case_num'])
298        elif name == 'mono_gauss_coil':
299            pars['i_zero'] = 1
300        elif name == 'poly_gauss_coil':
301            pars['i_zero'] = 1
302        elif name == 'core_multi_shell':
303            pars['n'] = min(math.ceil(pars['n']), 4)
304
Note: See TracBrowser for help on using the repository browser.