Index: src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/plotting.py	(revision 2d9526dfda3bdfe3c9159ab7d9db8399b4a00848)
@@ -14,4 +14,5 @@
 import wx
 import sys
+from copy import deepcopy
 from sas.sasgui.guiframe.events import EVT_NEW_PLOT
 from sas.sasgui.guiframe.events import EVT_PLOT_QRANGE
@@ -275,6 +276,19 @@
                 action_check = True
             else:
+                if action_string == 'update':
+                    # Update all existing plots of data with this ID
+                    for data in event.plots:
+                        for panel in self.plot_panels.values():
+                            if data.id in panel.plots.keys():
+                                plot_exists = True
+                                # Pass each panel it's own copy of the data
+                                # that's being updated, otherwise things like
+                                # colour and line thickness are unintentionally
+                                # synced across panels
+                                self.update_panel(deepcopy(data), panel)
+                    return
+                    
                 group_id = event.group_id
-                if group_id in self.plot_panels.keys():
+                if group_id in self.plot_panels:
                     #remove data from panel
                     if action_string == 'remove':
Index: src/sas/sasgui/perspectives/fitting/fitting.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitting.py	(revision 489f53a68015fe01293d70169de3d0f8ba84369e)
+++ src/sas/sasgui/perspectives/fitting/fitting.py	(revision 2d9526dfda3bdfe3c9159ab7d9db8399b4a00848)
@@ -1742,5 +1742,4 @@
             @param unsmeared_error: data error, rescaled to unsmeared model
         """
-
         number_finite = np.count_nonzero(np.isfinite(y))
         np.nan_to_num(y)
@@ -1748,22 +1747,34 @@
                                          data_description=model.name,
                                          data_id=str(page_id) + " " + data.name)
+        plots_to_update = [] # List of plottables that have changed since last calculation
+        # Create the new theories
         if unsmeared_model is not None:
-            self.create_theory_1D(x, unsmeared_model, page_id, model, data, state,
+            unsmeared_model_plot = self.create_theory_1D(x, unsmeared_model, 
+                                  page_id, model, data, state,
                                   data_description=model.name + " unsmeared",
                                   data_id=str(page_id) + " " + data.name + " unsmeared")
+            plots_to_update.append(unsmeared_model_plot)
 
             if unsmeared_data is not None and unsmeared_error is not None:
-                self.create_theory_1D(x, unsmeared_data, page_id, model, data, state,
+                unsmeared_data_plot = self.create_theory_1D(x, unsmeared_data, 
+                                      page_id, model, data, state,
                                       data_description="Data unsmeared",
                                       data_id="Data  " + data.name + " unsmeared",
                                       dy=unsmeared_error)
-        # Comment this out until we can get P*S models with correctly populated parameters
-        #if sq_model is not None and pq_model is not None:
-        #    self.create_theory_1D(x, sq_model, page_id, model, data, state,
-        #                          data_description=model.name + " S(q)",
-        #                          data_id=str(page_id) + " " + data.name + " S(q)")
-        #    self.create_theory_1D(x, pq_model, page_id, model, data, state,
-        #                          data_description=model.name + " P(q)",
-        #                          data_id=str(page_id) + " " + data.name + " P(q)")
+                plots_to_update.append(unsmeared_data_plot)
+        if sq_model is not None and pq_model is not None:
+            sq_id = str(page_id) + " " + data.name + " S(q)"
+            sq_plot = self.create_theory_1D(x, sq_model, page_id, model, data, state,
+                                  data_description=model.name + " S(q)",
+                                  data_id=sq_id)
+            plots_to_update.append(sq_plot)
+            pq_id = str(page_id) + " " + data.name + " P(q)"
+            pq_plot = self.create_theory_1D(x, pq_model, page_id, model, data, state,
+                                  data_description=model.name + " P(q)",
+                                  data_id=pq_id)
+            plots_to_update.append(pq_plot)
+        # Update the P(Q), S(Q) and unsmeared theory plots if they exist
+        wx.PostEvent(self.parent, NewPlotEvent(plots=plots_to_update, 
+                                              action='update'))
 
         current_pg = self.fit_panel.get_page_by_id(page_id)
Index: src/sas/sasgui/perspectives/fitting/media/fitting_help.rst
===================================================================
--- src/sas/sasgui/perspectives/fitting/media/fitting_help.rst	(revision 5295cf5164af9572978a04500767b155d0356342)
+++ src/sas/sasgui/perspectives/fitting/media/fitting_help.rst	(revision 05b0bf6345669cffadfd1e2035cd3f66c5161a8c)
@@ -484,4 +484,6 @@
 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
 
+.. _Batch_Fit_Mode:
+
 Batch Fit Mode
 --------------
@@ -636,6 +638,72 @@
 
      Example: radius [2 : 5] , radius [10 : 25]
-
-.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
-
-.. note::  This help document was last changed by Steve King, 10Oct2016
+     
+.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
+
+Combined Batch Fit Mode
+-----------------------
+
+The purpose of the Combined Batch Fit is to allow running two or more batch
+fits in sequence without overwriting the output table of results.  This may be
+of interest for example if one is fitting a series of data sets where there is
+a shape change occurring in the series that requires changing the model part
+way through the series; for example a sphere to rod transition.  Indeed the
+regular batch mode does not allow for multiple models and requires all the
+files in the series to be fit with single model and set of parameters.  While
+it is of course possible to just run part of the series as a batch fit using
+model one followed by running another batch fit on the rest of the series with
+model two (and/or model three etc), doing so will overwrite the table of
+outputs from the previous batch fit(s).  This may not be desirable if one is
+interested in comparing the parameters: for example the sphere radius of set
+one and the cylinder radius of set two.
+
+Method
+^^^^^^
+
+In order to use the *Combined Batch Fit*, first load all the data needed as
+described in :ref:`Loading_data`. Next start up two or more *BatchPage* fits
+following the instructions in :ref:`Batch_Fit_Mode` but **DO NOT PRESS FIT**.
+At this point the *Combine Batch Fit* menu item under the *Fitting menu* should
+be active (if there is one or no *BatchPage* the menu item will be greyed out
+and inactive).  Clicking on *Combine Batch Fit* will bring up a new panel,
+similar to the *Const & Simult Fit* panel. In this case there will be a
+checkbox for each *BatchPage* instead of each *FitPage* that should be included
+in the fit.  Once all are selected, click the Fit button on
+the *BatchPage* to run each batch fit in *sequence*
+
+.. image:: combine_batch_page.png
+
+The batch table will then pop up at the end as for the case of the simple Batch
+Fitting with the following caveats:
+
+.. note::
+   The order matters.  The parameters in the table will be taken from the model
+   used in the first *BatchPage* of the list.  Any parameters from the
+   second and later *BatchPage* s that have the same name as a parameter in the
+   first will show up allowing for plotting of that parameter across the
+   models. The other parameters will not be available in the grid.
+.. note::
+   a corralary of the above is that currently models created as a sum|multiply
+   model will not work as desired because the generated model parameters have a
+   p#_ appended to the beginning and thus radius and p1_radius will not be
+   recognized as the same parameter.
+   
+.. image:: combine_batch_grid.png
+
+In the example shown above the data is a time series with a shifting peak.
+The first part of the series was fitted using the *broad_peak* model, while
+the rest of the data were fit using the *gaussian_peak* model. Unfortunately the
+time is not listed in the file but the file name contains the information. As
+described in :ref:`Grid_Window`, a column can be added manually, in this case
+called time, and the peak position plotted against time. 
+
+.. image:: combine_batch_plot.png
+
+Note the discontinuity in the peak position.  This reflects the fact that the
+Gaussian fit is a rather poor model for the data and is not actually
+finding the peak.
+
+.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
+
+.. note::  This help document was last changed by Paul Butler, 10 September
+   2017
Index: src/sas/sasgui/perspectives/fitting/model_thread.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/model_thread.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
+++ src/sas/sasgui/perspectives/fitting/model_thread.py	(revision 0f9ea1cee80a92f71e4bec65998ca56a3de65fd6)
@@ -71,5 +71,5 @@
                     (self.data.qy_data * self.data.qy_data))
 
-        # For theory, qmax is based on 1d qmax 
+        # For theory, qmax is based on 1d qmax
         # so that must be mulitified by sqrt(2) to get actual max for 2d
         index_model = (self.qmin <= radius) & (radius <= self.qmax)
@@ -91,9 +91,10 @@
                 self.data.qy_data[index_model]
             ])
-        output = np.zeros(len(self.data.qx_data))
+        # Initialize output to NaN so masked elements do not get plotted.
+        output = np.empty_like(self.data.qx_data)
         # output default is None
         # This method is to distinguish between masked
         #point(nan) and data point = 0.
-        output = output / output
+        output[:] = np.NaN
         # set value for self.mask==True, else still None to Plottools
         output[index_model] = value
@@ -198,23 +199,22 @@
             output[index] = self.model.evalDistribution(self.data.x[index])
 
+        x=self.data.x[index]
+        y=output[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, "get_composition_models"):
-            p_model, s_model = self.model.get_composition_models()
-
-        if p_model is not None and s_model is not None:
-            sq_values = np.zeros((len(self.data.x)))
-            pq_values = np.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])
+            sq_values = s_model.evalDistribution(x)
+            pq_values = p_model.evalDistribution(x)
+        elif hasattr(self.model, "calc_composition_models"):
+            results = self.model.calc_composition_models(x)
+            if results is not None:
+                pq_values, sq_values = results
+
 
         elapsed = time.time() - self.starttime
 
-        self.complete(x=self.data.x[index], y=output[index],
+        self.complete(x=x, y=y,
                       page_id=self.page_id,
                       state=self.state,
Index: src/sas/sasgui/perspectives/fitting/simfitpage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/simfitpage.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sasgui/perspectives/fitting/simfitpage.py	(revision a9f9ca43ee83c6bd7d87a13ac54b0f3a3c30565d)
@@ -1,5 +1,19 @@
 """
-    Simultaneous fit page
+    Simultaneous or Batch fit page
 """
+# Note that this is used for both Simultaneous/Constrained fit AND for 
+# combined batch fit.  This is done through setting of the batch_on parameter.
+# There are the a half dozen or so places where an if statement is used as in 
+# if not batch_on:
+#     xxxx
+# else:
+#     xxxx
+# This is just wrong but dont have time to fix this go. Proper approach would be
+# to strip all parts of the code that depend on batch_on and create the top
+# level class from which a contrained/simultaneous fit page and a combined 
+# batch page inherit.
+#
+#            04/09/2017   --PDB
+
 import sys
 from collections import namedtuple
@@ -400,5 +414,8 @@
         # General Help button
         self.btHelp = wx.Button(self, wx.ID_HELP, 'HELP')
-        self.btHelp.SetToolTipString("Simultaneous/Constrained Fitting help.")
+        if self.batch_on:
+            self.btHelp.SetToolTipString("Combined Batch Fitting help.")
+        else:
+            self.btHelp.SetToolTipString("Simultaneous/Constrained Fitting help.")
         self.btHelp.Bind(wx.EVT_BUTTON, self._on_help)
 
@@ -527,8 +544,14 @@
     """
         _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
-        _PageAnchor = "#simultaneous-fit-mode"
-        _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation,
+        if not self.batch_on:
+            _PageAnchor = "#simultaneous-fit-mode"
+            _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation,
                                           _PageAnchor,
                                           "Simultaneous/Constrained Fitting Help")
+        else:
+            _PageAnchor = "#combined-batch-fit-mode"
+            _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation,
+                                          _PageAnchor,
+                                          "Combined Batch Fit Help")
 
     def set_manager(self, manager):
Index: src/sas/sasgui/plottools/plottables.py
===================================================================
--- src/sas/sasgui/plottools/plottables.py	(revision 45dffa69cb5935e2da7c77a2f040debb07ceb1c6)
+++ src/sas/sasgui/plottools/plottables.py	(revision 2d9526dfda3bdfe3c9159ab7d9db8399b4a00848)
@@ -239,13 +239,16 @@
     def replace(self, plottable):
         """Replace an existing plottable from the graph"""
-        selected_color = None
+        # If the user has set a custom color, ensure the new plot is the same color
+        selected_color = plottable.custom_color
         selected_plottable = None
         for p in self.plottables.keys():
             if plottable.id == p.id:
                 selected_plottable = p
-                selected_color = self.plottables[p]
+                if selected_color is None:
+                    selected_color = self.plottables[p]
                 break
-        if  selected_plottable is not None and selected_color is not None:
+        if selected_plottable is not None and selected_color is not None:
             del self.plottables[selected_plottable]
+            plottable.custom_color = selected_color
             self.plottables[plottable] = selected_color
 
