Index: sasmodels/conversion_table.py
===================================================================
--- sasmodels/conversion_table.py	(revision fa6d6fca7fec18564f3e3658015c21afe211d753)
+++ sasmodels/conversion_table.py	(revision bb584b3946fe8b5a6ab2d1a751f4b80ad63cf1b8)
@@ -5,9 +5,28 @@
 names for each parameter in sasmodels.  This is used by :mod:`convert` to
 determine the equivalent parameter set when comparing a sasmodels model to
-the models defined in SasView 3.1.
+the models defined in previous versions of SasView and sasmodels. This is now
+versioned based on the version number of SasView.
+
+When any sasmodels parameter or model name is changed, this must be modified to
+account for that.
+
+Usage:
+<old_Sasview_version> : {
+    <new_model_name> : [
+        <old_model_name> ,
+        {
+            <new_param_name_1> : <old_param_name_1>,
+            ...
+            <new_param_name_n> : <old_param_name_n>
+        }
+    ]
+}
+
+Any future parameter and model name changes can and should be given in this
+table for future compatibility.
 """
 
-
 CONVERSION_TABLE = {
+    (3,1,2) : {
     "adsorbed_layer": [
         "Core2ndMomentModel",
@@ -211,5 +230,5 @@
     ],
     "correlation_length": [
-        "CorrLengthModel",
+        "CorrLength",
         {
             "porod_scale": "scale_p",
@@ -218,5 +237,6 @@
             "lorentz_exp": "exponent_l",
             "cor_length": "length_l"
-        }
+        },
+        "CorrLengthModel"
     ],
     "cylinder": [
@@ -299,5 +319,5 @@
     ],
     "fractal_core_shell": [
-        "FractalCoreShellModel",
+        "FractalCoreShell",
         {
             "sld_core": "core_sld",
@@ -309,5 +329,6 @@
             "cor_length": "cor_length",
             "volfraction": "volfraction",
-        }
+        },
+        "FractalCoreShellModel"
     ],
     "fuzzy_sphere": [
@@ -321,5 +342,5 @@
     ],
     "gauss_lorentz_gel": [
-        "GaussLorentzGelModel",
+        "GaussLorentzGel",
         {
             "gauss_scale": "scale_g",
@@ -328,12 +349,14 @@
             "background": "background",
             "lorentz_scale": "scale_l"
-        }
+        },
+        "GaussLorentzGelModel"
     ],
     "gaussian_peak": [
-        "PeakGaussModel",
+        "Peak Gauss Model",
         {
             "peak_pos": "q0",
             "sigma": "B",
-        }
+        },
+        "PeakGaussModel",
     ],
     "gel_fit": [
@@ -343,16 +366,17 @@
             "lorentz_scale": "lScale",
             "guinier_scale": "gScale",
-            "fractal_dim": "scale",
+            "fractal_dim": "FractalExp",
             "cor_length": "zeta",
         }
     ],
     "guinier": [
+        "Guinier",
+        {
+            "rg": "rg"
+        },
         "GuinierModel",
-        {
-            "rg": "rg"
-        }
     ],
     "guinier_porod": [
-        "GuinierPorodModel",
+        "GuinierPorod",
         {
             "s": "dim",
@@ -361,5 +385,6 @@
             "scale": "scale",
             "background": "background"
-        }
+        },
+        "GuinierPorodModel",
     ],
     "hardsphere": [
@@ -454,5 +479,5 @@
             "d_spacing": "spacing",
             "Caille_parameter": "caille",
-            "Nlayers": "N_plates",
+            "Nlayers": "n_plates",
         }
     ],
@@ -486,8 +511,9 @@
     ],
     "lorentz": [
+        "Lorentz",
+        {
+            "cor_length": "length"
+        },
         "LorentzModel",
-        {
-            "cor_length": "length"
-        }
     ],
     "mass_fractal": [
@@ -510,10 +536,11 @@
     ],
     "mono_gauss_coil": [
-        "DebyeModel",
+        "Debye",
         {
             "rg": "rg",
             "i_zero": "scale",
             "background": "background",
-        }
+        },
+        "DebyeModel",
     ],
     "multilayer_vesicle": [
@@ -564,9 +591,10 @@
     ],
     "peak_lorentz": [
-        "PeakLorentzModel",
+        "Peak Lorentz Model",
         {
             "peak_pos": "q0",
             "peak_hwhm": "B"
-        }
+        },
+        "PeakLorentzModel",
     ],
     "pearl_necklace": [
@@ -802,5 +830,5 @@
     ],
     "two_lorentzian": [
-        "TwoLorentzianModel",
+        "TwoLorentzian",
         {
             "lorentz_scale_1": "scale_1",
@@ -811,8 +839,9 @@
             "lorentz_length_1": "length_1",
             "background": "background"
-        }
+        },
+        "TwoLorentzianModel",
     ],
     "two_power_law": [
-        "TwoPowerLawModel",
+        "TwoPowerLaw",
         {
             "coefficent_1": "coef_A",
@@ -821,10 +850,20 @@
             "background": "background",
             "crossover": "qc"
-        }
+        },
+        "TwoPowerLawModel",
     ],
     "unified_power_Rg": [
+        "UnifiedPowerRg",
+        dict(((field_new+str(index), field_old+str(index))
+              for field_new, field_old in [("rg", "Rg"),
+                                           ("power", "power"),
+                                           ("G", "G"),
+                                           ("B", "B"),]
+              for index in range(11)),
+             **{
+                   "background": "background",
+                   "scale": "scale",
+               }),
         "UnifiedPowerRgModel",
-        {
-        }
     ],
     "vesicle": [
@@ -835,3 +874,4 @@
         }
     ]
+    }
 }
Index: sasmodels/convert.py
===================================================================
--- sasmodels/convert.py	(revision f80f334d8db7925a28a9d4e57dc2f7b7a04bfb1b)
+++ sasmodels/convert.py	(revision 07c8d460b7b27e8016df36dcd00643a649cd4e71)
@@ -53,4 +53,10 @@
     ("_pd_nsigma", ".nsigmas"),
     ("_pd_type", ".type"),
+    (".lower", ".lower"),
+    (".upper", ".upper"),
+    (".fittable", ".fittable"),
+    (".std", ".std"),
+    (".units", ".units"),
+    ("", "")
     ]
 
@@ -64,6 +70,4 @@
     if id.startswith('M0:'):
         return True
-    if id.startswith('volfraction') or id.startswith('radius_effective'):
-        return False
     if '_pd' in id or '.' in id:
         return False
@@ -75,5 +79,5 @@
         if p.id == id:
             return p.type == 'sld'
-    raise ValueError("unknown parameter %r in conversion"%id)
+    return False
 
 def _rescale_sld(model_info, pars, scale):
@@ -88,7 +92,7 @@
 
 
-def _get_translation_table(model_info):
-    _, translation = CONVERSION_TABLE.get(model_info.id, [None, {}])
-    translation = translation.copy()
+def _get_translation_table(model_info, version=(3,1,2)):
+    conv_param = CONVERSION_TABLE.get(version, {}).get(model_info.id, [None, {}])
+    translation = conv_param[1].copy()
     for p in model_info.parameters.kernel_parameters:
         if p.length > 1:
@@ -119,30 +123,4 @@
 def _pd_to_underscores(pars):
     return dict((_dot_pd_to_underscore_pd(k), v) for k, v in pars.items())
-
-def _convert_name(conv_dict, pars):
-    """
-    Renames parameter values (upper, lower, etc) to v4.0 names
-    :param conv_dict: conversion dictionary mapping new name : old name
-    :param pars: parameters to convert
-    :return:
-    """
-    new_pars = {}
-    i = 0
-    j = 0
-    for key_par, value_par in pars.iteritems():
-        j += 1
-        for key_conv, value_conv in conv_dict.iteritems():
-            if re.search(value_conv, key_par):
-                new_pars[key_par.replace(value_conv, key_conv)] = value_par
-                i += 1
-                break
-            elif re.search("background", key_par) or re.search("scale", key_par):
-                new_pars[key_par] = value_par
-                i += 1
-                break
-        if i != j:
-            new_pars[key_par] = value_par
-            i += 1
-    return new_pars
 
 def _convert_pars(pars, mapping):
@@ -167,6 +145,5 @@
     return newpars
 
-
-def _conversion_target(model_name):
+def _conversion_target(model_name, version=(3,1,2)):
     """
     Find the sasmodel name which translates into the sasview name.
@@ -176,11 +153,16 @@
     two variants in sasview.
     """
-    for sasmodels_name, [sasview_name, _] in CONVERSION_TABLE.items():
-        if sasview_name == model_name:
+    for sasmodels_name, sasview_dict in \
+            CONVERSION_TABLE.get(version, {}).items():
+        if sasview_dict[0] == model_name:
             return sasmodels_name
     return None
 
-
-def _hand_convert(name, oldpars):
+def _hand_convert(name, oldpars, version=(3,1,2)):
+    if version == (3,1,2):
+        oldpars = _hand_convert_3_1_2_to_4_1(name, oldpars)
+    return oldpars
+
+def _hand_convert_3_1_2_to_4_1(name, oldpars):
     if name == 'core_shell_parallelepiped':
         # Make sure pd on rim parameters defaults to zero
@@ -215,4 +197,18 @@
             pd = oldpars['radius.width']*oldpars['radius']/thickness
             oldpars['radius.width'] = pd
+    elif name == 'multilayer_vesicle':
+        if 'scale' in oldpars:
+            oldpars['volfraction'] = oldpars['scale']
+            oldpars['scale'] = 1.0
+        if 'scale.lower' in oldpars:
+            oldpars['volfraction.lower'] = oldpars['scale.lower']
+        if 'scale.upper' in oldpars:
+            oldpars['volfraction.upper'] = oldpars['scale.upper']
+        if 'scale.fittable' in oldpars:
+            oldpars['volfraction.fittable'] = oldpars['scale.fittable']
+        if 'scale.std' in oldpars:
+            oldpars['volfraction.std'] = oldpars['scale.std']
+        if 'scale.units' in oldpars:
+            oldpars['volfraction.units'] = oldpars['scale.units']
     elif name == 'pearl_necklace':
         pass
@@ -236,5 +232,23 @@
                 oldpars[p + ".upper"] /= 1e-13
     elif name == 'spherical_sld':
-        oldpars["CONTROL"] += 1
+        j = 0
+        while "func_inter" + str(j) in oldpars:
+            name = "func_inter" + str(j)
+            new_name = "shape" + str(j + 1)
+            if oldpars[name] == 'Erf(|nu|*z)':
+                oldpars[new_name] = int(0)
+            elif oldpars[name] == 'RPower(z^|nu|)':
+                oldpars[new_name] = int(1)
+            elif oldpars[name] == 'LPower(z^|nu|)':
+                oldpars[new_name] = int(2)
+            elif oldpars[name] == 'RExp(-|nu|*z)':
+                oldpars[new_name] = int(3)
+            elif oldpars[name] == 'LExp(-|nu|*z)':
+                oldpars[new_name] = int(4)
+            else:
+                oldpars[new_name] = int(0)
+            oldpars.pop(name)
+            oldpars['n_shells'] = str(j + 1)
+            j += 1
     elif name == 'teubner_strey':
         # basically undoing the entire Teubner-Strey calculations here.
@@ -281,31 +295,44 @@
     return oldpars
 
-def convert_model(name, pars, use_underscore=False):
+def convert_model(name, pars, use_underscore=False, model_version=(3,1,2)):
     """
     Convert model from old style parameter names to new style.
     """
-    newname = _conversion_target(name)
-    if newname is None:
-        return name, pars
-    if ':' in newname:   # core_shell_ellipsoid:1
-        model_info = load_model_info(newname[:-2])
-        # Know that the table exists and isn't multiplicity so grab it directly
-        # Can't use _get_translation_table since that will return the 'bare'
-        # version.
-        translation = CONVERSION_TABLE[newname][1]
-    else:
-        model_info = load_model_info(newname)
-        translation = _get_translation_table(model_info)
-    newpars = _hand_convert(newname, pars.copy())
-    newpars = _convert_name(translation, newpars)
-    newpars = _convert_pars(newpars, translation)
-    if not model_info.structure_factor:
-        newpars = _rescale_sld(model_info, newpars, 1e6)
-    newpars.setdefault('scale', 1.0)
-    newpars.setdefault('background', 0.0)
-    if use_underscore:
-        newpars = _pd_to_underscores(newpars)
+    newpars = pars
+    keys = sorted(CONVERSION_TABLE.keys())
+    for i, version in enumerate(keys):
+        # Don't allow indices outside list
+        next_i = i + 1
+        if next_i == len(keys):
+            next_i = i
+        # If the save state is from a later version, skip the check
+        if model_version <= keys[next_i]:
+            newname = _conversion_target(name, version)
+        else:
+            newname = None
+        # If no conversion is found, move on
+        if newname is None:
+            newname = name
+            continue
+        if ':' in newname:   # core_shell_ellipsoid:1
+            model_info = load_model_info(newname[:-2])
+            # Know the table exists and isn't multiplicity so grab it directly
+            # Can't use _get_translation_table since that will return the 'bare'
+            # version.
+            translation = CONVERSION_TABLE.get(version, {})[newname][1]
+        else:
+            model_info = load_model_info(newname)
+            translation = _get_translation_table(model_info, version)
+        newpars = _hand_convert(newname, newpars, version)
+        newpars = _convert_pars(newpars, translation)
+        # TODO: Still not convinced this is the best check
+        if not model_info.structure_factor and version == (3,1,2):
+            newpars = _rescale_sld(model_info, newpars, 1e6)
+        newpars.setdefault('scale', 1.0)
+        newpars.setdefault('background', 0.0)
+        if use_underscore:
+            newpars = _pd_to_underscores(newpars)
+        name = newname
     return newname, newpars
-
 
 # ========= BACKWARD CONVERSION sasmodels => sasview 3.x ===========
Index: sasmodels/sasview_model.py
===================================================================
--- sasmodels/sasview_model.py	(revision 64614ad601c59ff9c1ee778c4fe0ac411d0696f3)
+++ sasmodels/sasview_model.py	(revision 64614ad601c59ff9c1ee778c4fe0ac411d0696f3)
@@ -57,8 +57,8 @@
     import sas.models
     from sasmodels.conversion_table import CONVERSION_TABLE
-    for new_name, conversion in CONVERSION_TABLE.items():
+    for new_name, conversion in CONVERSION_TABLE.get((3,1,2), {}).items():
         # CoreShellEllipsoidModel => core_shell_ellipsoid:1
         new_name = new_name.split(':')[0]
-        old_name = conversion[0]
+        old_name = conversion[0] if len(conversion) < 3 else conversion[2]
         module_attrs = {old_name: find_model(new_name)}
         ConstructedModule = type(old_name, (), module_attrs)
