source: sasmodels/sasmodels/convert.py @ 54bcd4a

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

spherical sld: simplify code so that it works on AMD GPUs

  • Property mode set to 100644
File size: 12.4 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
9
10from .conversion_table import CONVERSION_TABLE
11
12# List of models which SasView versions don't contain the explicit 'scale' argument.
13# When converting such a model, please update this list.
14MODELS_WITHOUT_SCALE = [
15    'teubner_strey',
16    'broad_peak',
17    'two_lorentzian',
18    "two_power_law",
19    'gel_fit',
20    'gauss_lorentz_gel',
21    'be_polyelectrolyte',
22    'correlation_length',
23    'fractal_core_shell',
24    'binary_hard_sphere',
25    'raspberry'
26]
27
28# List of models which SasView versions don't contain the explicit 'background' argument.
29# When converting such a model, please update this list.
30MODELS_WITHOUT_BACKGROUND = [
31    'guinier',
32]
33
34MODELS_WITHOUT_VOLFRACTION = [
35    'fractal',
36    'vesicle',
37    'multilayer_vesicle',
38]
39
40MAGNETIC_SASVIEW_MODELS = [
41    'core_shell',
42    'core_multi_shell',
43    'cylinder',
44    'parallelepiped',
45    'sphere',
46]
47
48
49# Convert new style names for polydispersity info to old style names
50PD_DOT = [
51    ("", ""),
52    ("_pd", ".width"),
53    ("_pd_n", ".npts"),
54    ("_pd_nsigma", ".nsigmas"),
55    ("_pd_type", ".type"),
56    ]
57
58def _convert_pars(pars, mapping):
59    """
60    Rename the parameters and any associated polydispersity attributes.
61    """
62    newpars = pars.copy()
63    for new, old in mapping.items():
64        if old == new: continue
65        for pd, dot in PD_DOT:
66            if old+dot in newpars:
67                if new is not None:
68                    newpars[new+pd] = pars[old+dot]
69                del newpars[old+dot]
70    return newpars
71
72def convert_model(name, pars):
73    """
74    Convert model from old style parameter names to new style.
75    """
76    _, _ = name, pars # lint
77    raise NotImplementedError
78    # need to load all new models in order to determine old=>new
79    # model name mapping
80
81def _unscale(par, scale):
82    return [pk*scale for pk in par] if isinstance(par, list) else par*scale
83
84def _is_sld(modelinfo, id):
85    if id.startswith('M0:'):
86        return True
87    if (id.endswith('_pd') or id.endswith('_pd_n') or id.endswith('_pd_nsigma')
88            or id.endswith('_pd_width') or id.endswith('_pd_type')):
89        return False
90    for p in modelinfo.parameters.call_parameters:
91        if p.id == id:
92            return p.type == 'sld'
93    # check through kernel parameters in case it is a named as a vector
94    for p in modelinfo.parameters.kernel_parameters:
95        if p.id == id:
96            return p.type == 'sld'
97    raise ValueError("unknown parameter %r in conversion"%id)
98
99def _unscale_sld(modelinfo, pars):
100    """
101    rescale all sld parameters in the new model definition by 1e6 so the
102    numbers are nicer.  Relies on the fact that all sld parameters in the
103    new model definition end with sld.
104    """
105    return dict((id, (_unscale(v,1e-6) if _is_sld(modelinfo, id) else v))
106                for id, v in pars.items())
107
108def _remove_pd(pars, key, name):
109    """
110    Remove polydispersity from the parameter list.
111
112    Note: operates in place
113    """
114    # Bumps style parameter names
115    pd = pars.pop(key+".width", 0.0)
116    pd_n = pars.pop(key+".npts", 0)
117    if pd != 0.0 and pd_n != 0:
118        warnings.warn("parameter %s not polydisperse in sasview %s"%(key, name))
119    pars.pop(key+".nsigmas", None)
120    pars.pop(key+".type", None)
121    return pars
122
123def _revert_pars(pars, mapping):
124    """
125    Rename the parameters and any associated polydispersity attributes.
126    """
127    newpars = pars.copy()
128
129    for new, old in mapping.items():
130        for pd, dot in PD_DOT:
131            if old and old+pd == new+dot:
132                continue
133            if new+pd in newpars:
134                if old is not None:
135                    newpars[old+dot] = pars[new+pd]
136                del newpars[new+pd]
137    for k in list(newpars.keys()):
138        for pd, dot in PD_DOT[1:]:  # skip "" => ""
139            if k.endswith(pd):
140                newpars[k[:-len(pd)]+dot] = newpars[k]
141                del newpars[k]
142    return newpars
143
144def revert_name(model_info):
145    oldname, oldpars = CONVERSION_TABLE.get(model_info.id, [None, {}])
146    return oldname
147
148def _get_translation_table(model_info):
149    _, translation = CONVERSION_TABLE.get(model_info.id, [None, {}])
150    translation = translation.copy()
151    for p in model_info.parameters.kernel_parameters:
152        if p.length > 1:
153            newid = p.id
154            oldid = translation.get(p.id, p.id)
155            translation.pop(newid, None)
156            for k in range(1, p.length+1):
157                if newid+str(k) not in translation:
158                    translation[newid+str(k)] = oldid+str(k)
159    # Remove control parameter from the result
160    if model_info.control:
161        translation[model_info.control] = "CONTROL"
162    return translation
163
164def _trim_vectors(model_info, pars, oldpars):
165    _, translation = CONVERSION_TABLE.get(model_info.id, [None, {}])
166    for p in model_info.parameters.kernel_parameters:
167        if p.length_control is not None:
168            n = int(pars[p.length_control])
169            oldname = translation.get(p.id, p.id)
170            for k in range(n+1, p.length+1):
171                for _, old in PD_DOT:
172                    oldpars.pop(oldname+str(k)+old, None)
173    return oldpars
174
175def revert_pars(model_info, pars):
176    """
177    Convert model from new style parameter names to old style.
178    """
179    if model_info.composition is not None:
180        composition_type, parts = model_info.composition
181        if composition_type == 'product':
182            P, S = parts
183            oldpars = {'scale':'scale_factor'}
184            oldpars.update(_get_old_pars(P))
185            oldpars.update(_get_old_pars(S))
186        else:
187            raise NotImplementedError("cannot convert to sasview sum")
188    else:
189        translation = _get_translation_table(model_info)
190        oldpars = _revert_pars(_unscale_sld(model_info, pars), translation)
191        oldpars = _trim_vectors(model_info, pars, oldpars)
192
193    # Make sure the control parameter is an integer
194    if "CONTROL" in oldpars:
195        oldpars["CONTROL"] = int(oldpars["CONTROL"])
196
197    # Note: update compare.constrain_pars to match
198    name = model_info.id
199    if name in MODELS_WITHOUT_SCALE or model_info.structure_factor:
200        if oldpars.pop('scale', 1.0) != 1.0:
201            warnings.warn("parameter scale not used in sasview %s"%name)
202    if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor:
203        if oldpars.pop('background', 0.0) != 0.0:
204            warnings.warn("parameter background not used in sasview %s"%name)
205
206    # Remove magnetic parameters from non-magnetic sasview models
207    if name not in MAGNETIC_SASVIEW_MODELS:
208        oldpars = dict((k,v) for k,v in oldpars.items() if ':' not in k)
209
210    # If it is a product model P*S, then check the individual forms for special
211    # cases.  Note: despite the structure factor alone not having scale or
212    # background, the product model does, so this is below the test for
213    # models without scale or background.
214    namelist = name.split('*') if '*' in name else [name]
215    for name in namelist:
216        if name in MODELS_WITHOUT_VOLFRACTION:
217            del oldpars['volfraction']
218        if name == 'stacked_disks':
219            _remove_pd(oldpars, 'n_stacking', name)
220        elif name == 'pearl_necklace':
221            _remove_pd(oldpars, 'num_pearls', name)
222            _remove_pd(oldpars, 'thick_string', name)
223        elif name == 'core_shell_parallelepiped':
224            _remove_pd(oldpars, 'rimA', name)
225            _remove_pd(oldpars, 'rimB', name)
226            _remove_pd(oldpars, 'rimC', name)
227        elif name == 'polymer_micelle':
228            if 'ndensity' in oldpars:
229                oldpars['ndensity'] *= 1e15
230        elif name == 'spherical_sld':
231            oldpars["CONTROL"] -= 1
232            # remove polydispersity from shells
233            for k in range(1, 11):
234                _remove_pd(oldpars, 'thick_flat'+str(k), 'thickness')
235                _remove_pd(oldpars, 'thick_inter'+str(k), 'interface')
236            # remove extra shells
237            for k in range(int(pars['n_shells']), 11):
238                oldpars.pop('sld_flat'+str(k), 0)
239                oldpars.pop('thick_flat'+str(k), 0)
240                oldpars.pop('thick_inter'+str(k), 0)
241                oldpars.pop('func_inter'+str(k), 0)
242                oldpars.pop('nu_inter'+str(k), 0)
243        elif name == 'core_multi_shell':
244            # kill extra shells
245            for k in range(5, 11):
246                oldpars.pop('sld_shell'+str(k), 0)
247                oldpars.pop('thick_shell'+str(k), 0)
248                oldpars.pop('mtheta:sld'+str(k), 0)
249                oldpars.pop('mphi:sld'+str(k), 0)
250                oldpars.pop('M0:sld'+str(k), 0)
251                _remove_pd(oldpars, 'sld_shell'+str(k), 'sld')
252                _remove_pd(oldpars, 'thick_shell'+str(k), 'thickness')
253        elif name == 'core_shell_parallelepiped':
254            _remove_pd(oldpars, 'rimA', name)
255        elif name in ['mono_gauss_coil','poly_gauss_coil']:
256            del oldpars['i_zero']
257        elif name == 'onion':
258            oldpars.pop('n_shells', None)
259        elif name == 'rpa':
260            # convert scattering lengths from femtometers to centimeters
261            for p in "L1", "L2", "L3", "L4":
262                if p in oldpars: oldpars[p] *= 1e-13
263            if pars['case_num'] < 2:
264                for k in ("a", "b"):
265                    for p in ("L", "N", "Phi", "b", "v"):
266                        oldpars.pop(p+k, None)
267                for k in "Kab,Kac,Kad,Kbc,Kbd".split(','):
268                    oldpars.pop(k, None)
269            elif pars['case_num'] < 5:
270                for k in ("a",):
271                    for p in ("L", "N", "Phi", "b", "v"):
272                        oldpars.pop(p+k, None)
273                for k in "Kab,Kac,Kad".split(','):
274                    oldpars.pop(k, None)
275
276    #print("convert from",list(sorted(pars)))
277    #print("convert to",list(sorted(oldpars.items())))
278    return oldpars
279
280def constrain_new_to_old(model_info, pars):
281    """
282    Restrict parameter values to those that will match sasview.
283    """
284    name = model_info.id
285    # Note: update convert.revert_model to match
286    if name in MODELS_WITHOUT_SCALE or model_info.structure_factor:
287        pars['scale'] = 1
288    if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor:
289        pars['background'] = 0
290    # sasview multiplies background by structure factor
291    if '*' in name:
292        pars['background'] = 0
293
294    # Shut off magnetism when comparing non-magnetic sasview models
295    if name not in MAGNETIC_SASVIEW_MODELS:
296        suppress_magnetism = False
297        for key in pars.keys():
298            if key.startswith("M0:"):
299                suppress_magnetism = suppress_magnetism or (pars[key] != 0)
300                pars[key] = 0
301        if suppress_magnetism:
302            warnings.warn("suppressing magnetism for comparison with sasview")
303
304    # Shut off theta polydispersity since algorithm has changed
305    if 'theta_pd_n' in pars:
306        if pars['theta_pd_n'] != 0:
307            warnings.warn("suppressing theta polydispersity for comparison with sasview")
308        pars['theta_pd_n'] = 0
309
310    # If it is a product model P*S, then check the individual forms for special
311    # cases.  Note: despite the structure factor alone not having scale or
312    # background, the product model does, so this is below the test for
313    # models without scale or background.
314    namelist = name.split('*') if '*' in name else [name]
315    for name in namelist:
316        if name in MODELS_WITHOUT_VOLFRACTION:
317            pars['volfraction'] = 1
318        if name == 'pearl_necklace':
319            pars['string_thickness_pd_n'] = 0
320            pars['number_of_pearls_pd_n'] = 0
321        elif name == 'line':
322            pars['scale'] = 1
323            pars['background'] = 0
324        elif name == 'rpa':
325            pars['case_num'] = int(pars['case_num'])
326        elif name == 'mono_gauss_coil':
327            pars['i_zero'] = 1
328        elif name == 'poly_gauss_coil':
329            pars['i_zero'] = 1
330        elif name == 'core_multi_shell':
331            pars['n'] = min(math.ceil(pars['n']), 4)
332        elif name == 'onion':
333            pars['n_shells'] = math.ceil(pars['n_shells'])
334        elif name == 'spherical_sld':
335            pars['n_shells'] = math.ceil(pars['n_shells'])
336            pars['n_steps'] = math.ceil(pars['n_steps'])
337            for k in range(1, 12):
338                pars['shape%d'%k] = math.trunc(pars['shape%d'%k]+0.5)
339            for k in range(2, 12):
340                pars['thickness%d_pd_n'%k] = 0
341                pars['interface%d_pd_n'%k] = 0
342
Note: See TracBrowser for help on using the repository browser.