Index: src/sas/qtgui/Perspectives/Fitting/FittingLogic.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingLogic.py	(revision dcabba7be660ff2e140942eb331ca846d1f2a399)
+++ src/sas/qtgui/Perspectives/Fitting/FittingLogic.py	(revision dcabba7be660ff2e140942eb331ca846d1f2a399)
@@ -161,5 +161,4 @@
         Create a new 1D data instance based on fitting results
         """
-
         return self._create1DPlot(tab_id, return_data['x'], return_data['y'],
                                   return_data['model'], return_data['data'])
@@ -212,18 +211,10 @@
         (pq_plot, sq_plot). If either are unavailable, the corresponding plot is None.
         """
-
-        pq_plot = None
-        sq_plot = None
-
-        if return_data.get('pq_values', None) is not None:
-            pq_plot = self._create1DPlot(tab_id, return_data['x'],
-                    return_data['pq_values'], return_data['model'],
-                    return_data['data'], component="P(Q)")
-        if return_data.get('sq_values', None) is not None:
-            sq_plot = self._create1DPlot(tab_id, return_data['x'],
-                    return_data['sq_values'], return_data['model'],
-                    return_data['data'], component="S(Q)")
-
-        return pq_plot, sq_plot
+        plots = []
+        for name, result in return_data['intermediate_results'].items():
+            plots.append(self._create1DPlot(tab_id, return_data['x'], result,
+                         return_data['model'], return_data['data'],
+                         component=name))
+        return plots
 
     def computeDataRange(self):
Index: src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py	(revision 70f445859abd127c3fa7cd9f6b8e2700e7bcb1a2)
+++ src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py	(revision 70f445859abd127c3fa7cd9f6b8e2700e7bcb1a2)
@@ -167,5 +167,5 @@
     return rows
 
-def addSimpleParametersToModel(parameters, is2D, parameters_original=None, model=None, view=None):
+def addSimpleParametersToModel(parameters, is2D, parameters_original=None, model=None, view=None, row_num=None):
     """
     Update local ModelModel with sasmodel parameters (non-dispersed, non-magnetic)
@@ -216,7 +216,13 @@
         # Append to the model and use the combobox, if required
         if None not in (model, view):
-            model.appendRow(row)
+            if row_num is None:
+                model.appendRow(row)
+            else:
+                model.insertRow(row_num, row)
+                row_num += 1
+
             if cbox:
                 view.setIndexWidget(item2.index(), cbox)
+
         rows.append(row)
 
Index: src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision d4ba5659c28772e9c307ab44a02664ec480d11a5)
+++ src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision d4ba5659c28772e9c307ab44a02664ec480d11a5)
@@ -2102,4 +2102,6 @@
             return
 
+        product_params = None
+
         if self.kernel_module is None:
             # Structure factor is the only selected model; build it and show all its params
@@ -2107,5 +2109,4 @@
             s_params = self.kernel_module._model_info.parameters
             s_params_orig = s_params
-
         else:
             s_kernel = self.models[structure_factor]()
@@ -2124,13 +2125,22 @@
             if "radius_effective_mode" in all_param_names:
                 # Show all parameters
+                # In this case, radius_effective is NOT pruned by sasmodels.product
                 s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len])
                 s_params_orig = modelinfo.ParameterTable(s_kernel._model_info.parameters.kernel_parameters)
+                product_params = modelinfo.ParameterTable(
+                        self.kernel_module._model_info.parameters.kernel_parameters[p_pars_len+s_pars_len:])
             else:
                 # Ensure radius_effective is not displayed
                 s_params_orig = modelinfo.ParameterTable(s_kernel._model_info.parameters.kernel_parameters[1:])
                 if "radius_effective" in all_param_names:
+                    # In this case, radius_effective is NOT pruned by sasmodels.product
                     s_params = modelinfo.ParameterTable(all_params[p_pars_len+1:p_pars_len+s_pars_len])
+                    product_params = modelinfo.ParameterTable(
+                            self.kernel_module._model_info.parameters.kernel_parameters[p_pars_len+s_pars_len:])
                 else:
+                    # In this case, radius_effective is pruned by sasmodels.product
                     s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len-1])
+                    product_params = modelinfo.ParameterTable(
+                            self.kernel_module._model_info.parameters.kernel_parameters[p_pars_len+s_pars_len-1:])
 
         # Add heading row
@@ -2140,9 +2150,22 @@
         # Any renamed parameters are stored as data in the relevant item, for later handling
         FittingUtilities.addSimpleParametersToModel(
-                s_params,
-                self.is2D,
-                s_params_orig,
-                self._model_model,
-                self.lstParams)
+                parameters=s_params,
+                is2D=self.is2D,
+                parameters_original=s_params_orig,
+                model=self._model_model,
+                view=self.lstParams)
+
+        # Insert product-only params into QModel
+        if product_params:
+            prod_rows = FittingUtilities.addSimpleParametersToModel(
+                    parameters=product_params,
+                    is2D=self.is2D,
+                    parameters_original=None,
+                    model=self._model_model,
+                    view=self.lstParams,
+                    row_num=2)
+
+            # Since this all happens after shells are dealt with and we've inserted rows, fix this counter
+            self._n_shells_row += len(prod_rows)
 
     def haveParamsToFit(self):
@@ -2405,15 +2428,9 @@
 
         # Create plots for intermediate product data
-        pq_data, sq_data = self.logic.new1DProductPlots(return_data, self.tab_id)
-        if pq_data is not None:
-            pq_data.symbol = "Line"
-            self.createNewIndex(pq_data)
-            # self.communicate.plotUpdateSignal.emit([pq_data])
-            new_plots.append(pq_data)
-        if sq_data is not None:
-            sq_data.symbol = "Line"
-            self.createNewIndex(sq_data)
-            # self.communicate.plotUpdateSignal.emit([sq_data])
-            new_plots.append(sq_data)
+        plots = self.logic.new1DProductPlots(return_data, self.tab_id)
+        for plot in plots:
+            plot.symbol = "Line"
+            self.createNewIndex(plot)
+            new_plots.append(plot)
 
         for plot in new_plots:
@@ -2816,4 +2833,7 @@
         self._num_shell_params = len(new_rows)
         self.current_shell_displayed = index
+
+        # Change 'n' in the parameter model, thereby updating the underlying model
+        self._model_model.item(self._n_shells_row, 1).setText(str(index))
 
         # Update relevant models
Index: src/sas/qtgui/Perspectives/Fitting/ModelThread.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ModelThread.py	(revision dcabba7be660ff2e140942eb331ca846d1f2a399)
+++ src/sas/qtgui/Perspectives/Fitting/ModelThread.py	(revision 5181e9b007b6389be27774b541e2fba4a11bec8d)
@@ -164,4 +164,6 @@
         index = (self.qmin <= self.data.x) & (self.data.x <= self.qmax)
 
+        intermediate_results = None
+
         # If we use a smearer, also return the unsmeared model
         unsmeared_output = None
@@ -174,5 +176,11 @@
             mask = self.data.x[first_bin:last_bin+1]
             unsmeared_output = numpy.zeros((len(self.data.x)))
-            unsmeared_output[first_bin:last_bin+1] = self.model.evalDistribution(mask)
+
+            return_data = self.model.calculate_Iq(mask)
+            if isinstance(return_data, tuple):
+                # see sasmodels beta_approx: SasviewModel.calculate_Iq
+                # TODO: implement intermediate results in smearers
+                return_data, _ = return_data
+            unsmeared_output[first_bin:last_bin+1] = return_data
             output = self.smearer(unsmeared_output, first_bin, last_bin)
 
@@ -193,24 +201,43 @@
                 unsmeared_error=unsmeared_error
         else:
-            output[index] = self.model.evalDistribution(self.data.x[index])
-
-        sq_values = None
-        pq_values = None
-        s_model = None
-        p_model = None
-        if isinstance(self.model, MultiplicationModel):
-            s_model = self.model.s_model
-            p_model = self.model.p_model
-        elif hasattr(self.model, "calc_composition_models"):
-            results = self.model.calc_composition_models(self.data.x[index])
-            if results is not None:
-                pq_values, sq_values = results
-
-        if pq_values is None or sq_values is None:
-            if p_model is not None and s_model is not None:
-                sq_values = numpy.zeros((len(self.data.x)))
-                pq_values = numpy.zeros((len(self.data.x)))
-                sq_values[index] = s_model.evalDistribution(self.data.x[index])
-                pq_values[index] = p_model.evalDistribution(self.data.x[index])
+            return_data = self.model.calculate_Iq(self.data.x[index])
+            if isinstance(return_data, tuple):
+                # see sasmodels beta_approx: SasviewModel.calculate_Iq
+                return_data, intermediate_results = return_data
+            output[index] = return_data
+
+        if intermediate_results:
+            # the model returns a callable which is then used to retrieve the data
+            intermediate_results = intermediate_results()
+        else:
+            # TODO: this conditional branch needs refactoring
+            sq_values = None
+            pq_values = None
+            s_model = None
+            p_model = None
+
+            if isinstance(self.model, MultiplicationModel):
+                s_model = self.model.s_model
+                p_model = self.model.p_model
+
+            elif hasattr(self.model, "calc_composition_models"):
+                results = self.model.calc_composition_models(self.data.x[index])
+                if results is not None:
+                    pq_values, sq_values = results
+
+            if pq_values is None or sq_values is None:
+                if p_model is not None and s_model is not None:
+                    sq_values = numpy.zeros((len(self.data.x)))
+                    pq_values = numpy.zeros((len(self.data.x)))
+                    sq_values[index] = s_model.evalDistribution(self.data.x[index])
+                    pq_values[index] = p_model.evalDistribution(self.data.x[index])
+
+            if pq_values is not None and sq_values is not None:
+                intermediate_results  = {
+                    "P(Q)": pq_values,
+                    "S(Q)": sq_values
+                }
+            else:
+                intermediate_results = {}
 
         elapsed = time.time() - self.starttime
@@ -223,5 +250,5 @@
             source = self.source, unsmeared_output = unsmeared_output,
             unsmeared_data = unsmeared_data, unsmeared_error = unsmeared_error,
-            pq_values = pq_values, sq_values = sq_values)
+            intermediate_results = intermediate_results)
 
         if LocalConfig.USING_TWISTED:
