Repository: Jammy2211/PyAutoGalaxy (language: python)

Issue:
Feature/clump remove
Removes the clump API from the source code, which was an unecessary layer for creating models with extra galaxies that is now replaced with high level documentation to teach a user the modeling API required.


Discussion / hints:

Fix patch:
diff --git a/autogalaxy/__init__.py b/autogalaxy/__init__.py
index 26df5df9..c7c42cca 100644
--- a/autogalaxy/__init__.py
+++ b/autogalaxy/__init__.py
@@ -4,6 +4,7 @@
 from autoarray.dataset.interferometer.dataset import Interferometer  # noqa
 from autoarray.dataset.dataset_model import DatasetModel
 from autoarray.dataset.over_sampling import OverSamplingDataset
+from autoarray.inversion.inversion.mapper_valued import MapperValued
 from autoarray.inversion.pixelization import mesh  # noqa
 from autoarray.inversion import regularization as reg  # noqa
 from autoarray.inversion.pixelization import image_mesh
@@ -108,8 +109,6 @@
 from .gui.clicker import Clicker
 from .gui.scribbler import Scribbler
 
-from .analysis.clump_model import ClumpModel
-
 from autoconf import conf
 
 conf.instance.register(__file__)
diff --git a/autogalaxy/aggregator/ellipse/fit_ellipse.py b/autogalaxy/aggregator/ellipse/fit_ellipse.py
index 031a7c24..0fe10129 100644
--- a/autogalaxy/aggregator/ellipse/fit_ellipse.py
+++ b/autogalaxy/aggregator/ellipse/fit_ellipse.py
@@ -60,7 +60,6 @@ def _fit_ellipse_from(
     for dataset, ellipse_list, multipole_lists in zip(
         dataset_list, ellipse_list_list, multipole_list_list
     ):
-
         for ellipse, multipole_list in zip(ellipse_list, multipole_lists):
             fit_dataset_list.append(
                 FitEllipse(
diff --git a/autogalaxy/aggregator/galaxies.py b/autogalaxy/aggregator/galaxies.py
index a6711ac5..ed1c3224 100644
--- a/autogalaxy/aggregator/galaxies.py
+++ b/autogalaxy/aggregator/galaxies.py
@@ -41,14 +41,14 @@ def _galaxies_from(fit: af.Fit, instance: af.ModelInstance) -> List[Galaxy]:
     if instance is not None:
         galaxies = instance.galaxies
 
-        if hasattr(instance, "clumps"):
-            galaxies = galaxies + fit.instance.clumps
+        if hasattr(instance, "extra_galaxies"):
+            galaxies = galaxies + fit.instance.extra_galaxies
 
     else:
         galaxies = fit.instance.galaxies
 
-        if hasattr(fit.instance, "clumps"):
-            galaxies = galaxies + fit.instance.clumps
+        if hasattr(fit.instance, "extra_galaxies"):
+            galaxies = galaxies + fit.instance.extra_galaxies
 
     if fit.children is not None:
         if len(fit.children) > 0:
diff --git a/autogalaxy/analysis/analysis/analysis.py b/autogalaxy/analysis/analysis/analysis.py
index 15645c66..0c25b68e 100644
--- a/autogalaxy/analysis/analysis/analysis.py
+++ b/autogalaxy/analysis/analysis/analysis.py
@@ -42,8 +42,8 @@ def galaxies_via_instance_from(
         """
         Create a list of galaxies from a model instance, which is used to fit the dataset.
 
-        The instance may only contain galaxies, in which case this function is redundant. However, if the clumns
-        API is being used, the instance will contain both galaxies and clumps, and they should be added to create
+        The instance may only contain galaxies, in which case this function is redundant. However, if extra galaxies
+        are included, the instance will contain both galaxies and extra galaxies, and they should be added to create
         the single list of galaxies used to fit the dataset.
 
         Parameters
@@ -56,11 +56,12 @@ def galaxies_via_instance_from(
         -------
         A list of galaxies that is used to then fit the dataset.
         """
-        if hasattr(instance, "clumps"):
-            return Galaxies(
-                galaxies=instance.galaxies + instance.clumps,
-                run_time_dict=run_time_dict,
-            )
+        if hasattr(instance, "extra_galaxies"):
+            if getattr(instance, "extra_galaxies", None) is not None:
+                return Galaxies(
+                    galaxies=instance.galaxies + instance.extra_galaxies,
+                    run_time_dict=run_time_dict
+                )
 
         return Galaxies(galaxies=instance.galaxies, run_time_dict=run_time_dict)
 
@@ -147,7 +148,7 @@ def profile_log_likelihood_function(
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
             via a non-linear search).
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
 
         Returns
@@ -238,7 +239,7 @@ def output_profiling_info(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         run_time_dict
             A dictionary containing the profiling times of the functions called by the `log_likelihood_function`.
diff --git a/autogalaxy/analysis/analysis/dataset.py b/autogalaxy/analysis/analysis/dataset.py
index ac5e957e..4dd25bca 100644
--- a/autogalaxy/analysis/analysis/dataset.py
+++ b/autogalaxy/analysis/analysis/dataset.py
@@ -90,7 +90,7 @@ def save_attributes(self, paths: af.DirectoryPaths):
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization, and the pickled objects used by the aggregator output by this function.
         """
         paths.save_fits(
@@ -145,7 +145,7 @@ def save_results(self, paths: af.DirectoryPaths, result: ResultDataset):
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         result
             The result of a model fit, including the non-linear search, samples and maximum likelihood tracer.
diff --git a/autogalaxy/analysis/chaining_util.py b/autogalaxy/analysis/chaining_util.py
index 52353670..5affa0bc 100644
--- a/autogalaxy/analysis/chaining_util.py
+++ b/autogalaxy/analysis/chaining_util.py
@@ -166,19 +166,19 @@ def source_from(
     return source_custom_model_from(result=result, source_is_model=True)
 
 
-def clumps_from(
+def extra_galaxies_from(
     result: Result,
     light_as_model: bool = False,
     mass_as_model: bool = False,
     free_centre: bool = False,
 ) -> af.Collection:
     """
-    The clump API models the light and / or mass of additional galaxies surrouding the main galaxy or strong lens
-    system.
+    The extra galaxies API models the light and / or mass of additional galaxies surrouding the main galaxy or
+    strong lens system.
 
-    This function performs model composition of clumps for fits using the search chaining API. It makes it possible to
-    pass the clump parameters from a previous search to a new search, such that the new clumps are either treated
-    as an instance or model component.
+    This function performs model composition of extra galaxies for fits using the search chaining API. It makes it
+    possible to pass the extra galaxy parameters from a previous search to a new search, such that the new extra
+    galaxies are either treated as an instance or model component.
 
     This function currently requires that mass profiles are `IsothermalSph` objects and that light profiles are
     `Sersic` objects. This will be generalised in the future.
@@ -186,57 +186,70 @@ def clumps_from(
     Parameters
     ----------
     result
-        The result, which includes clumps, of the previous search, which via prior passing are used to create the new
-        clump model.
+        The result, which includes extra galaxies, of the previous search, which via prior passing are used to create
+        the new extra galaxy model.
     light_as_model
-        If `True`, the clump light is passed as a model component, else it is a fixed instance.
+        If `True`, the extra_galaxy light is passed as a model component, else it is a fixed instance.
     mass_as_model
-        If `True`, the clump mass is passed as a model component, else it is a fixed instance.
+        If `True`, the extra_galaxy mass is passed as a model component, else it is a fixed instance.
     free_centre
         If `True`, the requested mass and/or light model centres are passed as a model, else they are fixed.
 
     Returns
     -------
     af.Collection
-        A collection of clump `Galaxy` objects, where each clump is either an instance or model component.
+        A collection of extra galaxy `Galaxy` objects, where each is either an instance or model component.
     """
     # ideal API:
 
-    # clumps = result.instance.clumps.as_model((LightProfile, mp.MassProfile,), fixed="centre", prior_pass=True)
+    # extra_galaxies = result.instance.extra_galaxies.as_model((LightProfile, mp.MassProfile,), fixed="centre", prior_pass=True)
 
-    if mass_as_model:
-        clumps = result.instance.clumps.as_model((MassProfile,))
+    if result.instance.extra_galaxies is None:
+        return None
 
-        for clump_index in range(len(result.instance.clumps)):
-            if hasattr(result.instance.clumps[clump_index], "mass"):
-                clumps[clump_index].mass.centre = result.instance.clumps[
-                    clump_index
+    if mass_as_model:
+        extra_galaxies = result.instance.extra_galaxies.as_model((MassProfile,))
+
+        for extra_galaxy_index in range(len(result.instance.extra_galaxies)):
+            if hasattr(result.instance.extra_galaxies[extra_galaxy_index], "mass"):
+                extra_galaxies[
+                    extra_galaxy_index
+                ].mass.centre = result.instance.extra_galaxies[
+                    extra_galaxy_index
                 ].mass.centre
-                clumps[clump_index].mass.einstein_radius = result.model.clumps[
-                    clump_index
+                extra_galaxies[
+                    extra_galaxy_index
+                ].mass.einstein_radius = result.model.extra_galaxies[
+                    extra_galaxy_index
                 ].mass.einstein_radius
                 if free_centre:
-                    clumps[clump_index].mass.centre = result.model.clumps[
-                        clump_index
+                    extra_galaxies[
+                        extra_galaxy_index
+                    ].mass.centre = result.model.extra_galaxies[
+                        extra_galaxy_index
                     ].mass.centre
 
     elif light_as_model:
-        clumps = result.instance.clumps.as_model((LightProfile,))
-
-        for clump_index in range(len(result.instance.clumps)):
-            if clumps[clump_index].light is not None:
-                clumps[clump_index].light.centre = result.instance.clumps[
-                    clump_index
-                ].light.centre
+        extra_galaxies = result.instance.extra_galaxies.as_model((LightProfile,))
+
+        for extra_galaxy_index in range(len(result.instance.extra_galaxies)):
+            if extra_galaxies[extra_galaxy_index].bulge is not None:
+                extra_galaxies[
+                    extra_galaxy_index
+                ].bulge.centre = result.instance.extra_galaxies[
+                    extra_galaxy_index
+                ].bulge.centre
                 if free_centre:
-                    clumps[clump_index].light.centre = result.model.clumps[
-                        clump_index
-                    ].light.centre
+                    extra_galaxies[
+                        extra_galaxy_index
+                    ].bulge.centre = result.model.extra_galaxies[
+                        extra_galaxy_index
+                    ].bulge.centre
 
     else:
-        clumps = result.instance.clumps.as_model(())
+        extra_galaxies = result.instance.extra_galaxies.as_model(())
 
-    return clumps
+    return extra_galaxies
 
 
 def lp_chain_tracer_from(light_result, settings_search):
diff --git a/autogalaxy/analysis/clump_model.py b/autogalaxy/analysis/clump_model.py
deleted file mode 100644
index 509c40f8..00000000
--- a/autogalaxy/analysis/clump_model.py
+++ /dev/null
@@ -1,204 +0,0 @@
-from typing import List, Optional, Type
-
-import autofit as af
-import autoarray as aa
-
-from autogalaxy.galaxy.galaxy import Galaxy
-from autogalaxy.profiles.light.abstract import LightProfile
-from autogalaxy.profiles.mass.abstract.abstract import MassProfile
-
-
-class ClumpModel:
-    def __init__(
-        self,
-        redshift: float,
-        centres: aa.Grid2DIrregular,
-        light_cls: Optional[Type[LightProfile]] = None,
-        mass_cls: Optional[Type[MassProfile]] = None,
-        einstein_radius_upper_limit: Optional[float] = None,
-        unfix_centres: bool = False,
-    ):
-        """
-        The clump API allows creates model components which model the light and mass of galaxies that are nearby the
-        main galaxy(s) of interest.
-
-        The `ClumpModel` object handles the creation of these model components to streamline model composition with
-        multiple clumps.
-
-        Every galaxy which is modeled as a clump has its centre input into this object which is fixed to this value
-        for model-fitting. All clumps are created as model `Galaxy` objects with a shard input redshift.
-
-        The light and mass profiles of the clumps are input via the `light_cls` and `mass_cls` inputs. If either is
-        omitted the clumps are not assigned a light or mass model.
-
-        Parameters
-        ----------
-        redshift
-            The redshift value of all clumps, which is likely the same as the main galaxy redshift.
-        centres
-            The centre of every clump in the model, whose light and mass profile centres are fixed to this value
-            throughout the model-fit.
-        light_cls
-            The light profile given to all clumps; if omitted all clumps have no light profile.
-        mass_cls
-            The mass profile given to all clumps; if omitted all clumps have no mass profile.
-        einstein_radius_upper_limit
-            The upper limit given to any mass model's `einstein_radius` parameter (e.g. if `IsothermalSph` profiles
-            are used to model clumps).
-        unfix_centres
-            If required, change the mass and light centres from fixed values to Uniform Prior Models +/- 0.1 around
-            the input centres.
-        """
-        self.redshift = redshift
-        self.centres = centres
-
-        self.light_cls = light_cls
-        self.mass_cls = mass_cls
-
-        self.einstein_radius_upper_limit = einstein_radius_upper_limit
-        self.unfix_centres = unfix_centres
-
-        self.centre_prior_half_width = 0.1
-
-    @property
-    def total_clumps(self) -> int:
-        return len(self.centres.in_list)
-
-    def unfix_centre(obj, centre):
-        obj.centre.centre_0 = af.UniformPrior(
-            lower_limit=new_centre[0] - centre_prior_half_width,
-            upper_limit=new_centre[0] + centre_prior_half_width,
-        )
-        obj.centre.centre_1 = af.UniformPrior(
-            lower_limit=new_centre[1] - centre_prior_half_width,
-            upper_limit=new_centre[1] + centre_prior_half_width,
-        )
-        return obj
-
-    @property
-    def light_list(self) -> Optional[List[af.Model]]:
-        """
-        Returns a list of every clump's light model, where the centre of that light model is fixed to its corresponding
-        input clump's centre, unless specified to be free.
-        """
-        if self.light_cls is None:
-            return None
-
-        light_list = []
-
-        for centre in self.centres.in_list:
-            if self.unfix_centres:
-                light_list.append(unfix_centre(af.Model(self.light_cls), centre))
-            else:
-                light_list.append(af.Model(self.light_cls, centre=centre))
-
-        return light_list
-
-    @property
-    def mass_list(self) -> Optional[List[af.Model]]:
-        """
-        Returns a list of every clump's mass model, where the centre of that mass model is fixed to its corresponding
-        input clump's centre, unless specified to be free.
-        """
-        if self.mass_cls is None:
-            return None
-
-        mass_list = []
-
-        for centre in self.centres.in_list:
-            if self.unfix_centres:
-                mass = unfix_centres(af.Model(self.mass_cls), centre)
-            else:
-                mass = af.Model(self.mass_cls, centre=centre)
-
-            if (
-                hasattr(mass, "einstein_radius")
-                and self.einstein_radius_upper_limit is not None
-            ):
-                mass.einstein_radius = af.UniformPrior(
-                    lower_limit=0.0, upper_limit=self.einstein_radius_upper_limit
-                )
-            mass_list.append(mass)
-
-        return mass_list
-
-    @property
-    def clumps_light_only(self) -> af.Collection:
-        """
-        Returns all clumps as a `Collection` model object, which can be easily added to an overall model `Collection`
-        of galaxies (see the `clumps` property below for more details).
-
-        All clumps only contain their model mass profiles, which is important for certain pipelines which omit
-        the clump mass profiles.
-        """
-        clumps_light_only = {}
-
-        for i in range(self.total_clumps):
-            light = self.light_list[i] if self.light_cls is not None else None
-
-            clumps_light_only[f"clump_{i}"] = af.Model(
-                Galaxy, redshift=self.redshift, light=light
-            )
-
-        return af.Collection(**clumps_light_only)
-
-    @property
-    def clumps_mass_only(self) -> af.Collection:
-        """
-        Returns all clumps as a `Collection` model object, which can be easily added to an overall model `Collection`
-        of galaxies (see the `clumps` property below for more details).
-
-        All clumps only contain their model mass profiles, which is important for certain pipelines which omit
-        the clump light profiles.
-        """
-        clumps_mass_only = {}
-
-        for i in range(self.total_clumps):
-            mass = self.mass_list[i] if self.mass_cls is not None else None
-
-            clumps_mass_only[f"clump_{i}"] = af.Model(
-                Galaxy, redshift=self.redshift, mass=mass
-            )
-
-        return af.Collection(**clumps_mass_only)
-
-    @property
-    def clumps(self) -> af.Collection:
-        """
-        Returns all clumps as a `Collection` model object, which can be easily added to an overall model `Collection`
-        of galaxies.
-
-        To make this `Collection` every clump centre and the input `light_cls` / `mass_cls` are used to create a
-        dictionary of model `Galaxy` objects with fixed light and mass profile centres. Their redshifts use the
-        input redshift.
-
-        The keys of this dictionary are numerically ordered as `clump_0`, `clump_2` etc.
-        """
-        clumps = {}
-
-        for i in range(self.total_clumps):
-            light = self.light_list[i] if self.light_cls is not None else None
-            mass = self.mass_list[i] if self.mass_cls is not None else None
-
-            clumps[f"clump_{i}"] = af.Model(
-                Galaxy, redshift=self.redshift, light=light, mass=mass
-            )
-
-        return af.Collection(**clumps)
-
-
-class ClumpModelDisabled:
-    def __init__(self):
-        pass
-
-    @property
-    def clumps_light_only(self):
-        return af.Collection()
-
-    @property
-    def clumps_mass_only(self):
-        return af.Collection()
-
-    @property
-    def clumps(self):
-        return af.Collection()
diff --git a/autogalaxy/analysis/plotter_interface.py b/autogalaxy/analysis/plotter_interface.py
index df0da17d..bc564663 100644
--- a/autogalaxy/analysis/plotter_interface.py
+++ b/autogalaxy/analysis/plotter_interface.py
@@ -379,7 +379,7 @@ def adapt_images(
         Visualizes the adapt images used by a model-fit for adaptive pixelization mesh's and regularization.
 
         Images are output to the `image` folder of the `image_path` in a subfolder called `adapt`. When
-        used with a non-linear search the `image_path` points to the search's results folder.
+        used with a non-linear search the `image_path` is the output folder of the non-linear search.
 
         Visualization includes an image of the overall adapt model image and a subplot of all galaxy images on the same
         figure.
diff --git a/autogalaxy/ellipse/model/analysis.py b/autogalaxy/ellipse/model/analysis.py
index 07f92f41..7e228ece 100644
--- a/autogalaxy/ellipse/model/analysis.py
+++ b/autogalaxy/ellipse/model/analysis.py
@@ -181,7 +181,7 @@ def save_attributes(self, paths: af.DirectoryPaths):
          Parameters
          ----------
          paths
-             The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+             The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
              visualization, and the pickled objects used by the aggregator output by this function.
         """
         paths.save_fits(
@@ -226,7 +226,7 @@ def profile_log_likelihood_function(
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
             via a non-linear search).
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
 
         Returns
diff --git a/autogalaxy/ellipse/model/plotter_interface.py b/autogalaxy/ellipse/model/plotter_interface.py
index cbbf52fc..bce0ecdf 100644
--- a/autogalaxy/ellipse/model/plotter_interface.py
+++ b/autogalaxy/ellipse/model/plotter_interface.py
@@ -17,7 +17,7 @@ def imaging(self, dataset: aa.Imaging):
         Visualizes an `Imaging` dataset object.
 
         Images are output to the `image` folder of the `image_path` in a subfolder called `imaging`. When used with
-        a non-linear search the `image_path` points to the search's results folder.
+        a non-linear search the `image_path` is the output folder of the non-linear search.
         `.
         Visualization includes individual images of attributes of the dataset (e.g. the image, noise map, PSF) and a
         subplot of all these attributes on the same figure.
diff --git a/autogalaxy/ellipse/model/visualizer.py b/autogalaxy/ellipse/model/visualizer.py
index 781bf621..42862f10 100644
--- a/autogalaxy/ellipse/model/visualizer.py
+++ b/autogalaxy/ellipse/model/visualizer.py
@@ -20,10 +20,10 @@ def visualize_before_fit(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         model
-            The PyAutoFit model object, which includes model components representing the galaxies that are fitted to
+            The model object, which includes model components representing the galaxies that are fitted to
             the imaging data.
         """
 
@@ -55,7 +55,7 @@ def visualize(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization, and the pickled objects used by the aggregator output by this function.
         instance
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
diff --git a/autogalaxy/gui/scribbler.py b/autogalaxy/gui/scribbler.py
index 505562cc..d8183f50 100644
--- a/autogalaxy/gui/scribbler.py
+++ b/autogalaxy/gui/scribbler.py
@@ -166,13 +166,13 @@ def show_mask(self):
         junk_mask = masks["1"]
         feature_mask = masks["2"]
         plt.ioff()
-        plt.figure()
-        plt.subplot(121)
-        plt.imshow(junk_mask.astype("int"), cmap="gray")
-        plt.title("Junk mask")
-        plt.subplot(122)
-        plt.imshow(feature_mask.astype("int"), cmap="gray")
-        plt.title("Feature mask")
+        # plt.figure()
+        # plt.subplot(121)
+        # plt.imshow(junk_mask.astype("int"), cmap="gray")
+        # plt.title("Junk mask")
+        # plt.subplot(122)
+        # plt.imshow(feature_mask.astype("int"), cmap="gray")
+        # plt.title("Feature mask")
         # plt.show()
         return junk_mask
 
diff --git a/autogalaxy/imaging/model/analysis.py b/autogalaxy/imaging/model/analysis.py
index ad2bc643..5e6a8844 100644
--- a/autogalaxy/imaging/model/analysis.py
+++ b/autogalaxy/imaging/model/analysis.py
@@ -84,10 +84,10 @@ def modify_before_fit(self, paths: af.DirectoryPaths, model: af.Collection):
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         model
-            The PyAutoFit model object, which includes model components representing the galaxies that are fitted to
+            The model object, which includes model components representing the galaxies that are fitted to
             the imaging data.
         """
 
@@ -217,7 +217,7 @@ def save_attributes(self, paths: af.DirectoryPaths):
          Parameters
          ----------
          paths
-             The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+             The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
              visualization, and the pickled objects used by the aggregator output by this function.
         """
         super().save_attributes(paths=paths)
@@ -254,7 +254,7 @@ def profile_log_likelihood_function(
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
             via a non-linear search).
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
 
         Returns
diff --git a/autogalaxy/imaging/model/plotter_interface.py b/autogalaxy/imaging/model/plotter_interface.py
index 564369c8..2352e4e1 100644
--- a/autogalaxy/imaging/model/plotter_interface.py
+++ b/autogalaxy/imaging/model/plotter_interface.py
@@ -1,4 +1,5 @@
 from os import path
+from typing import List
 
 import autoarray as aa
 import autoarray.plot as aplt
@@ -13,21 +14,21 @@
 class PlotterInterfaceImaging(PlotterInterface):
     def imaging(self, dataset: aa.Imaging):
         """
-        Visualizes an `Imaging` dataset object.
+        Output visualization of an `Imaging` dataset, typically before a model-fit is performed.
 
-        Images are output to the `image` folder of the `image_path` in a subfolder called `imaging`. When used with
-        a non-linear search the `image_path` points to the search's results folder.
+        Images are output to the `image` folder of the `image_path` in a subfolder called `dataset`. When used with
+        a non-linear search the `image_path` is the output folder of the non-linear search.
         `.
         Visualization includes individual images of attributes of the dataset (e.g. the image, noise map, PSF) and a
         subplot of all these attributes on the same figure.
 
-        The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under the
-        [dataset] header.
+        The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under
+        the `dataset` header.
 
         Parameters
         ----------
         dataset
-            The imaging dataset whose attributes are visualized.
+            The imaging dataset which is visualized.
         """
 
         def should_plot(name):
@@ -157,3 +158,137 @@ def should_plot(name):
                 subtracted_image=True,
                 model_image=True,
             )
+
+    def imaging_combined(self, dataset_list: List[aa.Imaging]):
+        """
+        Output visualization of all `Imaging` datasets in a summed combined analysis, typically before a model-fit
+        is performed.
+
+        Images are output to the `image` folder of the `image_path` in a subfolder called `dataset_combined`. When
+        used with a non-linear search the `image_path` is the output folder of the non-linear search.
+        `.
+        Visualization includes individual images of attributes of each dataset (e.g. the image, noise map, PSF) on
+        a single subplot, such that the full suite of multiple datasets can be viewed on the same figure.
+
+        The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under
+        the `dataset` header.
+
+        Parameters
+        ----------
+        dataset
+            The list of imaging datasets which are visualized.
+        """
+
+        def should_plot(name):
+            return plot_setting(section=["dataset", "imaging"], name=name)
+
+        mat_plot_2d = self.mat_plot_2d_from(subfolders="combined")
+
+        dataset_plotter_list = [
+            aplt.ImagingPlotter(
+                dataset=dataset, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d
+            )
+            for dataset in dataset_list
+        ]
+
+        subplot_shape = (len(dataset_list), 4)
+
+        multi_plotter = aplt.MultiFigurePlotter(
+            plotter_list=dataset_plotter_list, subplot_shape=subplot_shape
+        )
+
+        if should_plot("subplot_dataset"):
+            multi_plotter.subplot_of_figures_multi(
+                func_name_list=["figures_2d"] * 4,
+                figure_name_list=["data", "noise_map", "signal_to_noise_map", "psf"],
+                filename_suffix="dataset",
+            )
+
+            for plotter in multi_plotter.plotter_list:
+                plotter.mat_plot_2d.use_log10 = True
+
+            multi_plotter.subplot_of_figures_multi(
+                func_name_list=["figures_2d"] * 4,
+                figure_name_list=["data", "noise_map", "signal_to_noise_map", "psf"],
+                filename_suffix="dataset_log10",
+            )
+
+    def fit_imaging_combined(self, fit_list: List[FitImaging]):
+        """
+        Output visualization of all `FitImaging` objects in a summed combined analysis, typically during or after a
+        model-fit is performed.
+
+        Images are output to the `image` folder of the `image_path` in a subfolder called `combined`. When used
+        with a non-linear search the `image_path` is the output folder of the non-linear search.
+        `.
+        Visualization includes individual images of attributes of each fit (e.g. data, normalized residual-map) on
+        a single subplot, such that the full suite of multiple datasets can be viewed on the same figure.
+
+        The images output by the `PlotterInterface` are customized using the file `config/visualize/plots.yaml` under
+        the `fit` header.
+
+        Parameters
+        ----------
+        fit
+            The list of imaging fits which are visualized.
+        """
+
+        def should_plot(name):
+            return plot_setting(section=["fit", "fit_imaging"], name=name)
+
+        mat_plot_2d = self.mat_plot_2d_from(subfolders="combined")
+
+        fit_plotter_list = [
+            FitImagingPlotter(
+                fit=fit, mat_plot_2d=mat_plot_2d, include_2d=self.include_2d
+            )
+            for fit in fit_list
+        ]
+
+        subplot_shape = (len(fit_list), 5)
+
+        multi_plotter = aplt.MultiFigurePlotter(
+            plotter_list=fit_plotter_list, subplot_shape=subplot_shape
+        )
+
+        if should_plot("subplot_fit"):
+
+            def make_subplot_fit(filename_suffix):
+                multi_plotter.subplot_of_figures_multi(
+                    func_name_list=["figures_2d"] * 4,
+                    figure_name_list=[
+                        "data",
+                        "signal_to_noise_map",
+                        "model_image",
+                        "normalized_residual_map",
+                    ],
+                    filename_suffix=filename_suffix,
+                    number_subplots=len(fit_list) * 5,
+                    close_subplot=False,
+                )
+
+                for plotter in multi_plotter.plotter_list:
+                    plotter.mat_plot_2d.cmap.kwargs["vmin"] = -1.0
+                    plotter.mat_plot_2d.cmap.kwargs["vmax"] = 1.0
+
+                multi_plotter.subplot_of_figures_multi(
+                    func_name_list=["figures_2d"],
+                    figure_name_list=[
+                        "normalized_residual_map",
+                    ],
+                    filename_suffix=filename_suffix,
+                    number_subplots=len(fit_list) * 5,
+                    subplot_index_offset=4,
+                    open_subplot=False,
+                )
+
+                for plotter in multi_plotter.plotter_list:
+                    plotter.mat_plot_2d.cmap.kwargs["vmin"] = None
+                    plotter.mat_plot_2d.cmap.kwargs["vmax"] = None
+
+            make_subplot_fit(filename_suffix="fit")
+
+            for plotter in multi_plotter.plotter_list:
+                plotter.mat_plot_2d.use_log10 = True
+
+            make_subplot_fit(filename_suffix="fit_log10")
diff --git a/autogalaxy/imaging/model/visualizer.py b/autogalaxy/imaging/model/visualizer.py
index fa169c0a..201c72d4 100644
--- a/autogalaxy/imaging/model/visualizer.py
+++ b/autogalaxy/imaging/model/visualizer.py
@@ -20,10 +20,10 @@ def visualize_before_fit(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         model
-            The PyAutoFit model object, which includes model components representing the galaxies that are fitted to
+            The model object, which includes model components representing the galaxies that are fitted to
             the imaging data.
         """
 
@@ -63,7 +63,7 @@ def visualize(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization, and the pickled objects used by the aggregator output by this function.
         instance
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
@@ -94,3 +94,82 @@ def visualize(
         )
         if fit.inversion is not None:
             plotter.inversion(inversion=fit.inversion, during_analysis=during_analysis)
+
+    @staticmethod
+    def visualize_before_fit_combined(
+        analyses,
+        paths: af.AbstractPaths,
+        model: af.AbstractPriorModel,
+    ):
+        """
+        Performs visualization before the non-linear search begins of information which shared across all analyses
+        on a single matplotlib figure.
+
+        This function outputs visuals of all information which does not vary during the fit, for example the dataset
+        being fitted.
+
+        Parameters
+        ----------
+        analyses
+            The list of all analysis objects used for fitting via yhe non-linear search.
+        paths
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            visualization and the pickled objects used by the aggregator output by this function.
+        model
+            The model object, which includes model components representing the galaxies that are fitted to
+            the imaging data.
+        """
+
+        if analyses is None:
+            return
+
+        plotter = PlotterInterfaceImaging(
+            image_path=paths.image_path, title_prefix=analyses[0].title_prefix
+        )
+
+        dataset_list = [analysis.dataset for analysis in analyses]
+
+        plotter.imaging_combined(
+            dataset_list=dataset_list,
+        )
+
+    @staticmethod
+    def visualize_combined(
+        analyses,
+        paths: af.AbstractPaths,
+        instance: af.ModelInstance,
+        during_analysis: bool,
+    ):
+        """
+        Performs visualization during the non-linear search of information which is shared across all analyses on a
+        single matplotlib figure.
+
+        This function outputs visuals of all information which varies during the fit, for example the model-fit to
+        the dataset being fitted.
+
+        Parameters
+        ----------
+        analyses
+            The list of all analysis objects used for fitting via yhe non-linear search.
+        paths
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            visualization and the pickled objects used by the aggregator output by this function.
+        model
+            The model object, which includes model components representing the galaxies that are fitted to
+            the imaging data.
+        """
+        if analyses is None:
+            return
+
+        plotter = PlotterInterfaceImaging(
+            image_path=paths.image_path, title_prefix=analyses[0].title_prefix
+        )
+
+        fit_list = [
+            analysis.fit_from(instance=single_instance)
+            for analysis, single_instance in zip(analyses, instance)
+        ]
+
+        plotter.fit_imaging_combined(
+            fit_list=fit_list,
+        )
diff --git a/autogalaxy/interferometer/model/analysis.py b/autogalaxy/interferometer/model/analysis.py
index 7ee73d1f..126561f7 100644
--- a/autogalaxy/interferometer/model/analysis.py
+++ b/autogalaxy/interferometer/model/analysis.py
@@ -89,10 +89,10 @@ def modify_before_fit(self, paths: af.DirectoryPaths, model: af.Collection):
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         model
-            The PyAutoFit model object, which includes model components representing the galaxies that are fitted to
+            The model object, which includes model components representing the galaxies that are fitted to
             the imaging data.
         """
 
@@ -217,7 +217,7 @@ def save_attributes(self, paths: af.DirectoryPaths):
          Parameters
          ----------
          paths
-             The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored, visualization,
+             The paths object which manages all paths, e.g. where the non-linear search outputs are stored, visualization,
              and the pickled objects used by the aggregator output by this function.
         """
         super().save_attributes(paths=paths)
@@ -256,7 +256,7 @@ def profile_log_likelihood_function(
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
             via a non-linear search).
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
 
         Returns
diff --git a/autogalaxy/interferometer/model/plotter_interface.py b/autogalaxy/interferometer/model/plotter_interface.py
index 5686415a..335dbe25 100644
--- a/autogalaxy/interferometer/model/plotter_interface.py
+++ b/autogalaxy/interferometer/model/plotter_interface.py
@@ -18,7 +18,7 @@ def interferometer(self, dataset: aa.Interferometer):
         Visualizes an `Interferometer` dataset object.
 
         Images are output to the `image` folder of the `image_path` in a subfolder called `interferometer`. When
-        used with a non-linear search the `image_path` points to the search's results folder.
+        used with a non-linear search the `image_path` is the output folder of the non-linear search.
 
         Visualization includes individual images of attributes of the dataset (e.g. the visibilities, noise map,
         uv-wavelengths) and a subplot of all these attributes on the same figure.
diff --git a/autogalaxy/interferometer/model/visualizer.py b/autogalaxy/interferometer/model/visualizer.py
index ca3b5bc1..03e5d465 100644
--- a/autogalaxy/interferometer/model/visualizer.py
+++ b/autogalaxy/interferometer/model/visualizer.py
@@ -21,10 +21,10 @@ def visualize_before_fit(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization and the pickled objects used by the aggregator output by this function.
         model
-            The PyAutoFit model object, which includes model components representing the galaxies that are fitted to
+            The model object, which includes model components representing the galaxies that are fitted to
             the imaging data.
         """
 
@@ -67,7 +67,7 @@ def visualize(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization, and the pickled objects used by the aggregator output by this function.
         instance
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
diff --git a/autogalaxy/quantity/model/analysis.py b/autogalaxy/quantity/model/analysis.py
index aaf9e5f2..78ebb372 100644
--- a/autogalaxy/quantity/model/analysis.py
+++ b/autogalaxy/quantity/model/analysis.py
@@ -147,7 +147,7 @@ def save_attributes(self, paths: af.DirectoryPaths):
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization, and the pickled objects used by the aggregator output by this function.
         """
         paths.save_fits(
diff --git a/autogalaxy/quantity/model/visualizer.py b/autogalaxy/quantity/model/visualizer.py
index 48b30e16..2b2af686 100644
--- a/autogalaxy/quantity/model/visualizer.py
+++ b/autogalaxy/quantity/model/visualizer.py
@@ -30,7 +30,7 @@ def visualize(
         Parameters
         ----------
         paths
-            The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
+            The paths object which manages all paths, e.g. where the non-linear search outputs are stored,
             visualization, and the pickled objects used by the aggregator output by this function.
         instance
             An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
diff --git a/docs/howtogalaxy/chapter_1_introduction.rst b/docs/howtogalaxy/chapter_1_introduction.rst
index 5d8cb076..538cd4da 100644
--- a/docs/howtogalaxy/chapter_1_introduction.rst
+++ b/docs/howtogalaxy/chapter_1_introduction.rst
@@ -10,14 +10,17 @@ The chapter contains the following tutorials:
 `Tutorial 0: Visualization <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_0_visualization.ipynb>`_
 - Setting up **PyAutoGalaxy**'s visualization library.
 
-`Tutorial 1: Grids <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_1_grids.ipynb>`_
-- Uniform grids of (y,x) coordinates used for ray-tracing.
+`Tutorial 1: Grids And Galaxies <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_1_grids_and_galaxies.ipynb>`_
+- Using grids of (y,x) coordinates with galaxies made up of light profiles.
 
-`Tutorial 2: Light Profiles <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_2_light_profiles.ipynb>`_
-- Analytic profiles representing light distributions.
+`Tutorial 2: Data <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_2_data.ipynb>`_
+- Simulating and inspecting telescope imaging data of a galaxy.
 
-`Tutorial 3: Galaxies <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_3_galaxies.ipynb>`_
-- Making galaxy objects out of light and mass profile.
+`Tutorial 3: Fitting <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_3_fitting.ipynb>`_
+- Fitting data with a galaxy model.
 
-`Tutorial 6: Summary <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_6_summary.ipynb>`_
+`Tutorial 4: Methods <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_4_methods.ipynb>`_
+- An overview of the different methods used to fit galaxies with.
+
+`Tutorial 5: Summary <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_1_introduction/tutorial_5_summary.ipynb>`_
 - A summary of the chapter.
\ No newline at end of file
diff --git a/docs/howtogalaxy/chapter_2_modeling.rst b/docs/howtogalaxy/chapter_2_modeling.rst
index d912944d..66c2432b 100644
--- a/docs/howtogalaxy/chapter_2_modeling.rst
+++ b/docs/howtogalaxy/chapter_2_modeling.rst
@@ -5,33 +5,27 @@ In chapter 2, we'll take you through how to model galaxies using a non-linear se
 
 The chapter contains the following tutorials:
 
-`Tutorial 1: Data <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_1_data.ipynb>`_
-- Loading and inspecting telescope imaging data of a galaxy.
+`Tutorial 1: Non-linear Search <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_1_non_linear_search.ipynb>`_
+- How a non-linear search is used to fit a lens model.
 
-`Tutorial 2: Fitting <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_2_fitting.ipynb>`_
-- Fitting data with a galaxy model.
-
-`Tutorial 3: Non-linear Search <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_3_non_linear_search.ipynb>`_
-- How a non-linear search is used to fit a model.
-
-`Tutorial 4: Parameter Space And Priors <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_4_parameter_space_and_priors.ipynb>`_
+`Tutorial 2: Parameter Space And Priors <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_2_parameter_space_and_priors.ipynb>`_
 - The Concepts of a parameter space and priors.
 
-`Tutorial 5: Realism and Complexity <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_5_realism_and_complexity.ipynb>`_
-- Finding a balance between realism and complexity when composing and fitting a model.
+`Tutorial 3: Realism and Complexity <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_3_realism_and_complexity.ipynb>`_
+- Finding a balance between realism and complexity when composing and fitting a lens model.
 
-`Tutorial 6: Dealing with Failure <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_6_dealing_with_failure.ipynb>`_
-- What to do when PyAutoGalaxy finds an inaccurate model.
+`Tutorial 4: Dealing with Failure <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_4_dealing_with_failure.ipynb>`_
+- What to do when PyAutoLens finds an inaccurate lens model.
 
-`Tutorial 7: Linear Profiles <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_7_linear_profiles.ipynb>`_
+`Tutorial 5: Linear Profiles <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_5_linear_profiles.ipynb>`_
 - Light profiles which capture complex morphologies in a reduced number of non-linear parameters.
 
-`Tutorial 8: Masking <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_8_masking.ipynb>`_
-- How to mask your data to improve the model.
+`Tutorial 6: Masking and Positions <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_6_masking_and_positions.ipynb>`_
+- How to mask and mark positions on your data to improve the lens model.
 
-`Tutorial 9: Results <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_9_results.ipynb>`_
-- Overview of the results available after successfully fitting a model.
+`Tutorial 7: Results <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_7_results.ipynb>`_
+- Overview of the results available after successfully fitting a lens model.
 
-`Tutorial 10: Need for Speed <https://mybinder.org/v2/gh/Jammy2211/autogalaxy_workspace/release?filepath=notebooks/howtogalaxy/chapter_2_modeling/tutorial_10_need_for_speed.ipynb>`_
+`Tutorial 8: Need for Speed <https://mybinder.org/v2/gh/Jammy2211/autolens_workspace/release?filepath=notebooks/howtolens/chapter_2_lens_modeling/tutorial_8_need_for_speed.ipynb>`_
 - How to fit complex models whilst balancing efficiency and run-time.
 
diff --git a/docs/overview/overview_1_start_here.rst b/docs/overview/overview_1_start_here.rst
index 5d7a399d..ce3046d8 100644
--- a/docs/overview/overview_1_start_here.rst
+++ b/docs/overview/overview_1_start_here.rst
@@ -219,10 +219,10 @@ To further illustrate this, we create a merging galaxy system with 4 star formin
             intensity=0.1,
             effective_radius=1.6,
         ),
-        clump_0=ag.lp.SersicSph(centre=(1.0, 1.0), intensity=0.5, effective_radius=0.2),
-        clump_1=ag.lp.SersicSph(centre=(0.5, 0.8), intensity=0.5, effective_radius=0.2),
-        clump_2=ag.lp.SersicSph(centre=(-1.0, -0.7), intensity=0.5, effective_radius=0.2),
-        clump_3=ag.lp.SersicSph(centre=(-1.0, 0.4), intensity=0.5, effective_radius=0.2),
+        extra_galaxy_0=ag.lp.SersicSph(centre=(1.0, 1.0), intensity=0.5, effective_radius=0.2),
+        extra_galaxy_1=ag.lp.SersicSph(centre=(0.5, 0.8), intensity=0.5, effective_radius=0.2),
+        extra_galaxy_2=ag.lp.SersicSph(centre=(-1.0, -0.7), intensity=0.5, effective_radius=0.2),
+        extra_galaxy_3=ag.lp.SersicSph(centre=(-1.0, 0.4), intensity=0.5, effective_radius=0.2),
     )
 
     galaxy_1 = ag.Galaxy(


Test patch:
diff --git a/test_autogalaxy/analysis/analysis/test_analysis.py b/test_autogalaxy/analysis/analysis/test_analysis.py
index 2e7be98d..387e50c8 100644
--- a/test_autogalaxy/analysis/analysis/test_analysis.py
+++ b/test_autogalaxy/analysis/analysis/test_analysis.py
@@ -11,10 +11,11 @@
 
 def test__galaxies_via_instance(masked_imaging_7x7):
     galaxy = ag.Galaxy(redshift=0.5, light=ag.lp.Sersic(intensity=0.1))
-    clump = ag.Galaxy(redshift=0.5, light=ag.lp.Sersic(intensity=0.2))
+    extra_galaxy = ag.Galaxy(redshift=0.5, light=ag.lp.Sersic(intensity=0.2))
 
     model = af.Collection(
-        galaxies=af.Collection(galaxy=galaxy), clumps=af.Collection(clump_0=clump)
+        galaxies=af.Collection(galaxy=galaxy),
+        extra_galaxies=af.Collection(extra_galaxy_0=extra_galaxy),
     )
 
     analysis = ag.AnalysisImaging(dataset=masked_imaging_7x7)
diff --git a/test_autogalaxy/analysis/test_clump_model.py b/test_autogalaxy/analysis/test_clump_model.py
deleted file mode 100644
index 12d4230a..00000000
--- a/test_autogalaxy/analysis/test_clump_model.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import autofit as af
-import autogalaxy as ag
-
-
-def test__clumps():
-    centres = ag.Grid2DIrregular(values=[(1.0, 1.0)])
-
-    clump_model = ag.ClumpModel(
-        redshift=0.5, centres=centres, light_cls=ag.lp.SersicSph
-    )
-
-    clumps = clump_model.clumps
-
-    assert clumps["clump_0"].redshift == 0.5
-    assert clumps["clump_0"].light.centre == (1.0, 1.0)
-    assert isinstance(clumps["clump_0"].light.intensity, af.LogUniformPrior)
-    assert clumps["clump_0"].mass == None
-
-    clump_model = ag.ClumpModel(
-        redshift=0.5, centres=centres, mass_cls=ag.mp.IsothermalSph
-    )
-
-    clumps = clump_model.clumps
-
-    assert clumps["clump_0"].redshift == 0.5
-    assert clumps["clump_0"].mass.centre == (1.0, 1.0)
-    assert isinstance(clumps["clump_0"].mass.einstein_radius, af.UniformPrior)
-    assert clumps["clump_0"].light == None
-
-    clump_model = ag.ClumpModel(
-        redshift=0.5,
-        centres=centres,
-        light_cls=ag.lp.SersicSph,
-        mass_cls=ag.mp.IsothermalSph,
-    )
-
-    clumps = clump_model.clumps
-
-    assert clumps["clump_0"].redshift == 0.5
-    assert clumps["clump_0"].light.centre == (1.0, 1.0)
-    assert clumps["clump_0"].mass.centre == (1.0, 1.0)
-    assert isinstance(clumps["clump_0"].light.intensity, af.LogUniformPrior)
-    assert isinstance(clumps["clump_0"].mass.einstein_radius, af.UniformPrior)
-
-
-def test__clumps_light_only():
-    centres = ag.Grid2DIrregular(values=[(1.0, 1.0)])
-
-    clump_model = ag.ClumpModel(
-        redshift=0.5,
-        centres=centres,
-        light_cls=ag.lp.SersicSph,
-        mass_cls=ag.mp.IsothermalSph,
-    )
-
-    clumps_light_only = clump_model.clumps_light_only
-
-    assert clumps_light_only["clump_0"].redshift == 0.5
-    assert clumps_light_only["clump_0"].light.centre == (1.0, 1.0)
-    assert isinstance(clumps_light_only["clump_0"].light.intensity, af.LogUniformPrior)
-    assert not hasattr(clumps_light_only["clump_0"], "mass")
-
-
-def test__clumps_mass_only():
-    centres = ag.Grid2DIrregular(values=[(1.0, 1.0)])
-
-    clump_model = ag.ClumpModel(
-        redshift=0.5,
-        centres=centres,
-        light_cls=ag.lp.SersicSph,
-        mass_cls=ag.mp.IsothermalSph,
-    )
-
-    clumps_mass_only = clump_model.clumps_mass_only
-
-    assert clumps_mass_only["clump_0"].redshift == 0.5
-    assert clumps_mass_only["clump_0"].mass.centre == (1.0, 1.0)
-    assert isinstance(clumps_mass_only["clump_0"].mass.einstein_radius, af.UniformPrior)
-    assert not hasattr(clumps_mass_only["clump_0"], "light")
-
-
-def test__einstein_radius_max():
-    centres = ag.Grid2DIrregular(values=[(1.0, 1.0)])
-
-    clump_model = ag.ClumpModel(
-        redshift=0.5,
-        centres=centres,
-        mass_cls=ag.mp.IsothermalSph,
-        einstein_radius_upper_limit=1.0,
-    )
-
-    clumps = clump_model.clumps
-
-    assert isinstance(clumps["clump_0"].mass.einstein_radius, af.UniformPrior)
-    assert clumps["clump_0"].mass.einstein_radius.upper_limit == 1.0
-
-    assert clumps["clump_0"].redshift == 0.5
-    assert clumps["clump_0"].mass.centre == (1.0, 1.0)
-    assert clumps["clump_0"].light == None
diff --git a/test_autogalaxy/imaging/model/test_plotter_interface_imaging.py b/test_autogalaxy/imaging/model/test_plotter_interface_imaging.py
index a2fd3942..0c43849e 100644
--- a/test_autogalaxy/imaging/model/test_plotter_interface_imaging.py
+++ b/test_autogalaxy/imaging/model/test_plotter_interface_imaging.py
@@ -31,6 +31,19 @@ def test__imaging(imaging_7x7, include_2d_all, plot_path, plot_patch):
     assert path.join(plot_path, "signal_to_noise_map.png") not in plot_patch.paths
 
 
+def test__imaging_combined(imaging_7x7, plot_path, plot_patch):
+    if path.exists(plot_path):
+        shutil.rmtree(plot_path)
+
+    visualizer = PlotterInterfaceImaging(image_path=plot_path)
+
+    visualizer.imaging_combined(dataset_list=[imaging_7x7, imaging_7x7])
+
+    plot_path = path.join(plot_path, "combined")
+
+    assert path.join(plot_path, "subplot_dataset.png") in plot_patch.paths
+
+
 def test__fit_imaging(
     masked_imaging_7x7,
     fit_imaging_x2_galaxy_inversion_7x7,
@@ -61,3 +74,18 @@ def test__fit_imaging(
     )
 
     assert image.shape == (7, 7)
+
+
+def test__fit_imaging_combined(
+    fit_imaging_x2_galaxy_inversion_7x7, plot_path, plot_patch
+):
+    if path.exists(plot_path):
+        shutil.rmtree(plot_path)
+
+    visualizer = PlotterInterfaceImaging(image_path=plot_path)
+
+    visualizer.fit_imaging_combined(fit_list=2 * [fit_imaging_x2_galaxy_inversion_7x7])
+
+    plot_path = path.join(plot_path, "combined")
+
+    assert path.join(plot_path, "subplot_fit.png") in plot_patch.paths