Repository: lenstronomy/lenstronomy (language: python)

Issue:
`COOLEST` support in `lenstronomy`
### Discussed in https://github.com/lenstronomy/lenstronomy/discussions/451

<div type='discussions-op-text'>

<sup>Originally posted by **ajshajib** April 21, 2023</sup>
It would be convenient to have a feature that packs (and loads) the common dictionaries, lists, `kwargs_data`, `kwargs_psf`, etc. created/used during modeling into a file in the [`COOLEST`](https://github.com/aymgal/COOLEST) format. @LyneVdV informed me that she already has some code that performs this task. 🙏 We can discuss here to figure out where to add such code within `lenstronomy` (e.g., `Util`?), and what would be the requirements/requests for this feature.</div>


Discussion / hints:

Fix patch:
diff --git a/lenstronomy/Util/coolest_interface.py b/lenstronomy/Util/coolest_interface.py
new file mode 100644
index 000000000..bb5ee007b
--- /dev/null
+++ b/lenstronomy/Util/coolest_interface.py
@@ -0,0 +1,567 @@
+from coolest.template.json import JSONSerializer  # install from https://github.com/aymgal/COOLEST
+from astropy.cosmology import FlatLambdaCDM
+from astropy.io import fits
+import lenstronomy.Util.coolest_read_util as read
+import lenstronomy.Util.coolest_update_util as update
+import numpy as np
+from lenstronomy.Sampling.parameters import Param
+import lenstronomy.Util.class_creator as class_util
+
+
+def create_lenstronomy_from_coolest(file_name):
+    """
+    Creates lenstronomy typical kwargs from a COOLEST (JSON) file
+
+    :param file_name: str, name (with path) of the .json file containing the COOLEST information
+
+    :return: return_dict, dictionary with typical lenstronomy kwarg (as kwargs_data, kwargs_psf, kwargs_params, kwargs_results, kwargs_model etc)
+
+    """
+    creation_lens_source_light = False
+    creation_cosmo = False
+    creation_data = False
+    creation_instrument = False
+    creation_redshift_list = False
+    creation_kwargs_likelihood = False
+
+    decoder = JSONSerializer(file_name, indent=2)
+    lens_coolest = decoder.load()
+
+    print(f'LENS COOLEST : {lens_coolest.mode}')
+    exclude_keys = lens_coolest.exclude_keys
+
+    # IMAGE
+
+    kwargs_data = {}
+    if "observation" not in exclude_keys:
+        lens_observation = lens_coolest.observation
+        if lens_observation.pixels is not None:
+            creation_data = True
+            image_path = lens_observation.pixels.fits_file.path
+            pixel_size = lens_observation.pixels.pixel_size
+            nx = lens_observation.pixels.num_pix_x
+            ny = lens_observation.pixels.num_pix_y
+            try:
+                image = fits.open(image_path)[0].data
+                if (np.shape(image)[0] != nx) or (np.shape(image)[1] != ny):
+                    print(f'image shape {np.shape(image)} is different from the coolest file  {nx}, {ny}')
+            except:
+                image = image_path
+                print(f'could not find image file {image_path}. Saving file name instead.')
+            ra_at_xy_0 = - ( list(lens_observation.pixels.field_of_view_x)[0] + pixel_size / 2. )
+            dec_at_xy_0 = list(lens_observation.pixels.field_of_view_y)[0] + pixel_size / 2.
+            transform_pix2angle = np.array([[-1, 0], [0, 1]]) * pixel_size
+
+            kwargs_data = {'ra_at_xy_0': ra_at_xy_0,
+                           'dec_at_xy_0': dec_at_xy_0,
+                           'transform_pix2angle': transform_pix2angle,
+                           'image_data': image}
+            print('Data creation')
+
+    # NOISE
+    if "noise" not in exclude_keys:
+        if lens_observation.noise is not None:
+            if lens_observation.noise.type == "NoiseMap":
+                creation_data = True
+                noise_path = lens_observation.noise.noise_map.fits_file.path
+                try:
+                    noise = fits.open(noise_path)[0].data
+                except:
+                    noise = noise_path
+                    print(f'could not find noise file {noise_path}. Saving file name instead.')
+                noise_pixel_size = lens_observation.noise.noise_map.pixel_size
+                noise_nx = lens_observation.noise.noise_map.num_pix_x
+                noise_ny = lens_observation.noise.noise_map.num_pix_y
+                if pixel_size != noise_pixel_size:
+                    print(f"noise pixel size {noise_pixel_size} is different from image pixel size {pixel_size}")
+                if nx != noise_nx:
+                    print(f"noise nx {noise_nx} is different from image nx {nx}")
+                if ny != noise_ny:
+                    print(f"noise ny {noise_ny} is different from image ny {ny}")
+                kwargs_data['noise_map'] = noise
+                print('Noise (in Data) creation')
+            else:
+                print(f"noise type {lens_observation.noise.type} is unknown")
+
+    # PSF
+    if "instrument" not in exclude_keys:
+        lens_instrument = lens_coolest.instrument
+        if lens_instrument is not None:
+            if lens_instrument.psf is not None:
+                if lens_instrument.psf.type == "PixelatedPSF":
+                    creation_instrument = True
+                    psf_path = lens_instrument.psf.pixels.fits_file.path
+                    try:
+                        psf = fits.open(psf_path)[0].data
+                    except:
+                        psf = psf_path
+                        print(f'could not find PSF file {psf_path}. Saving file name instead.')
+                    psf_pixel_size = lens_instrument.psf.pixels.pixel_size
+                    psf_nx = lens_instrument.psf.pixels.num_pix_x
+                    psf_ny = lens_instrument.psf.pixels.num_pix_y
+                    super_sampling_factor = 1
+                    if pixel_size != psf_pixel_size:
+                        super_sampling_factor = int(pixel_size / psf_pixel_size)
+                        print(f"PSF pixel size {psf_pixel_size} is different from image pixel size {pixel_size}. "
+                              f"Assuming super sampling factor of {super_sampling_factor}.")
+
+                    kwargs_psf = {'psf_type': 'PIXEL', 'kernel_point_source': psf,
+                                  'point_source_supersampling_factor': super_sampling_factor}
+                    print('PSF creation')
+                else:
+                    print(f"PSF type {lens_instrument.psf.type} is unknown")
+
+    # COSMO
+    if "cosmology" not in exclude_keys:
+        lens_cosmo = lens_coolest.cosmology
+        if lens_cosmo is not None:
+            if lens_cosmo.astropy_name == "FlatLambdaCDM":
+                cosmo = FlatLambdaCDM(lens_cosmo.H0, lens_cosmo.Om0)
+                creation_cosmo = True
+                print("Cosmo class creation")
+            else:
+                print(f"Cosmology name {lens_cosmo.astropy_name} is unknown")
+
+#LIKELIHOODS not yet well supported by COOLEST
+    # # LIKELIHOODS
+    # if "likelihoods" not in exclude_keys:
+    #     likelihoods = lens_coolest.likelihoods
+    #     if likelihoods is not None:
+    #         kwargs_likelihood = {}
+    #         creation_kwargs_likelihood = True
+    #         for like in likelihoods:
+    #             if like == 'imaging_data':
+    #                 kwargs_likelihood['image_likelihood'] = True
+    #             else:
+    #                 print(f"Likelihood {like} not yet implemented")
+    #         print("kwargs_likelihood creation")
+
+    # LENSING ENTITIES
+    if "lensing_entities" not in exclude_keys:
+        lensing_entities_list = lens_coolest.lensing_entities
+        if lensing_entities_list is not None:
+
+            lens_model_list = []
+            kwargs_lens = []
+            kwargs_lens_up = []
+            kwargs_lens_down = []
+            kwargs_lens_init = []
+            kwargs_lens_fixed = []
+            kwargs_lens_sigma = []
+            lens_light_model_list = []
+            kwargs_lens_light = []
+            kwargs_lens_light_up = []
+            kwargs_lens_light_down = []
+            kwargs_lens_light_init = []
+            kwargs_lens_light_fixed = []
+            kwargs_lens_light_sigma = []
+            source_model_list = []
+            kwargs_source = []
+            kwargs_source_up = []
+            kwargs_source_down = []
+            kwargs_source_init = []
+            kwargs_source_fixed = []
+            kwargs_source_sigma = []
+            ps_model_list = []
+            kwargs_ps = []
+            kwargs_ps_up = []
+            kwargs_ps_down = []
+            kwargs_ps_init = []
+            kwargs_ps_fixed = []
+            kwargs_ps_sigma = []
+
+            creation_lens_source_light = True
+            multi_plane = False
+            creation_redshift_list = True
+
+            min_redshift, max_redshift, redshift_list = create_redshift_info(lensing_entities_list)
+
+            for lensing_entity in lensing_entities_list:
+                if lensing_entity.type == "galaxy":
+                    galaxy = lensing_entity
+                    if galaxy.redshift > min_redshift:
+                        # SOURCE OF LIGHT
+                        light_list = galaxy.light_model
+                        for light in light_list:
+                            print('Source Light : ')
+                            if light.type == 'Sersic':
+                                read.update_kwargs_sersic(light, source_model_list, kwargs_source,
+                                       kwargs_source_init, kwargs_source_up,
+                                       kwargs_source_down, kwargs_source_fixed,
+                                       kwargs_source_sigma, cleaning=True)
+                            elif light.type == 'Shapelets':
+                                read.update_kwargs_shapelets(light, source_model_list, kwargs_source,
+                                          kwargs_source_init, kwargs_source_up,
+                                          kwargs_source_down, kwargs_source_fixed,
+                                          kwargs_source_sigma, cleaning=True)
+                            elif light.type == 'LensedPS':
+                                read.update_kwargs_lensed_ps(light, ps_model_list, kwargs_ps,
+                                                    kwargs_ps_init, kwargs_ps_up,
+                                                    kwargs_ps_down, kwargs_ps_fixed,
+                                                    kwargs_ps_sigma, cleaning=True)
+                            else:
+                                print(f'Light Type {light.type} not yet implemented.')
+
+                    if galaxy.redshift < max_redshift:
+                        # LENSING GALAXY
+                        if galaxy.redshift > min_redshift:
+                            multi_plane = True
+                            print('Multiplane lensing to consider.')
+                        mass_list = galaxy.mass_model
+                        for mass in mass_list:
+                            print('Lens Mass : ')
+                            if mass.type == 'PEMD':
+                                read.update_kwargs_pemd(mass, lens_model_list, kwargs_lens, kwargs_lens_init, kwargs_lens_up,
+                                     kwargs_lens_down, kwargs_lens_fixed, kwargs_lens_sigma, cleaning=True)
+                            elif mass.type == 'SIE':
+                                read.update_kwargs_sie(mass, lens_model_list, kwargs_lens, kwargs_lens_init, kwargs_lens_up,
+                                    kwargs_lens_down, kwargs_lens_fixed, kwargs_lens_sigma, cleaning=True)
+                            else:
+                                print(f'Mass Type {mass.type} not yet implemented.')
+
+                    if galaxy.redshift == min_redshift:
+                        # LENSING LIGHT GALAXY
+                        light_list = galaxy.light_model
+                        for light in light_list:
+                            print('Lens Light : ')
+                            if light.type == 'Sersic':
+                                read.update_kwargs_sersic(light, lens_light_model_list, kwargs_lens_light, kwargs_lens_light_init,
+                                       kwargs_lens_light_up, kwargs_lens_light_down, kwargs_lens_light_fixed,
+                                       kwargs_lens_light_sigma, cleaning=True)
+                            # elif light.type == 'LensedPS':
+                            #     read.update_kwargs_lensed_ps(light, ps_model_list, kwargs_ps, kwargs_ps_init, kwargs_ps_up,
+                            #                         kwargs_ps_down, kwargs_ps_fixed, kwargs_ps_sigma, cleaning=True)
+                            else:
+                                print(f'Light Type {light.type} not yet implemented.')
+
+                    # if (galaxy.redshift <= min_redshift) or (galaxy.redshift >= max_redshift):
+                    #     print(f'REDSHIFT {galaxy.redshift} is not in the range ] {min_red} , {max_red} [')
+
+
+                elif lensing_entity.type == "external_shear":
+                    shear_list = lensing_entity.mass_model
+                    for shear_idx in shear_list:
+                        print('Shear : ')
+                        if shear_idx.type == 'ExternalShear':
+                            read.update_kwargs_shear(shear_idx, lens_model_list, kwargs_lens, kwargs_lens_init, kwargs_lens_up,
+                                  kwargs_lens_down, kwargs_lens_fixed, kwargs_lens_sigma, cleaning=True)
+
+                        else:
+                            print(f"type of Shear {shear_idx.type} not implemented")
+
+
+
+
+                else:
+                    print(f"lensing entity of type {lensing_entity.type} is unknown.")
+
+    return_dict = {}
+    if creation_lens_source_light is True:
+        return_dict['kwargs_model'] = {'lens_model_list': lens_model_list,
+                                       'source_light_model_list': source_model_list,
+                                       'lens_light_model_list': lens_light_model_list,
+                                       'point_source_model_list': ps_model_list}
+
+        lens_params = [kwargs_lens_init, kwargs_lens_sigma,
+                       kwargs_lens_fixed, kwargs_lens_down, kwargs_lens_up]
+        source_params = [kwargs_source_init, kwargs_source_sigma,
+                         kwargs_source_fixed, kwargs_source_down, kwargs_source_up]
+        lens_light_params = [kwargs_lens_light_init, kwargs_lens_light_sigma,
+                             kwargs_lens_light_fixed, kwargs_lens_light_down, kwargs_lens_light_up]
+        ps_params = [kwargs_ps_init, kwargs_ps_sigma, kwargs_ps_fixed, kwargs_ps_down, kwargs_ps_up]
+
+        kwargs_params = {'lens_model': lens_params,
+                         'source_model': source_params,
+                         'lens_light_model': lens_light_params,
+                         'point_source_model': ps_params}
+        return_dict['kwargs_params'] = kwargs_params
+
+        kwargs_result = {'kwargs_lens': kwargs_lens,
+                         'kwargs_source': kwargs_source,
+                         'kwargs_lens_light': kwargs_lens_light,
+                         'kwargs_ps': kwargs_ps}
+        return_dict['kwargs_result'] = kwargs_result
+    if creation_redshift_list is True:
+        return_dict['redshift_list'] = redshift_list
+    # if creation_kwargs_likelihood is True:
+    #     return_dict['kwargs_likelihood'] = kwargs_likelihood
+    if creation_cosmo is True:
+        return_dict['Cosmo'] = cosmo
+    if creation_data is True:
+        return_dict['kwargs_data'] = kwargs_data
+    if creation_instrument is True:
+        return_dict['kwargs_psf'] = kwargs_psf
+
+    # time delay not implemented
+
+    # I have never dealt with multiplane lensing :/ hope the redshift list is sufficient.
+    # Need to implement more lens models/ source models.
+
+    # GOOD TO KNOW : the COOLEST conventions have default bounds for some parameters
+    # -> not updated with my default bounds !
+
+    return return_dict
+
+
+def update_coolest_from_lenstronomy(file_name, kwargs_result, kwargs_mcmc=None,
+                            ending='_update'):
+    """
+    Function to update a json file already containing a model with the results of this model fitting
+
+    :param file_name: str, name (with path) of the json file to update
+    :param kwargs_results: dict, lenstronomy kwargs_results {'kwargs_lens': [{..},{..}], 'kwargs_source': [{..}],...}
+    :param kwargs_mcmc: dict, {'args_lens':args_lens,'args_source':args_source,'args_lens_light':args_lens_light,
+                        'args_ps': args_ps}
+                        with args_lens being a list, each element of the list being lens results of type
+                        kwargs_results['kwargs_lens'] for a given MCMC point.
+                        ex: {'args_lens': [[{'theta_E':0.71,...},{'gamma1':...}],
+                                           [{'theta_E':0.709,...},{'gamma1':...}],
+                                           [{'theta_E':0.711,...},{'gamma1':...}], ...],
+                             'args_source': [[{'R_sersic':0.11,...}], [{'R_sersic':0.115,...}, ...]], ...}
+                 if None, will not perform the Posterior update
+    :param ending: str, ending of the name for saving the updated json file
+
+     :return:   the new json file is saved with the updated kwargs.
+    """
+
+    decoder = JSONSerializer(file_name, indent=2)
+    lens_coolest = decoder.load()
+    available_profiles = ['LensedPS', 'Sersic', 'Shapelets', 'PEMD', 'SIE', 'SIS', 'ExternalShear']
+    if lens_coolest.mode == 'MAP':
+        print(f'LENS COOLEST : {lens_coolest.mode}')
+    else:
+        print(f'LENS COOLEST IS NOT MAP, BUT IS {lens_coolest.mode}')
+
+    lensing_entities_list = lens_coolest.lensing_entities
+
+    if lensing_entities_list is not None:
+
+        lens_model_list = []
+        kwargs_lens = []
+        kwargs_lens_up = []
+        kwargs_lens_down = []
+        kwargs_lens_init = []
+        kwargs_lens_fixed = []
+        kwargs_lens_sigma = []
+        lens_light_model_list = []
+        kwargs_lens_light = []
+        kwargs_lens_light_up = []
+        kwargs_lens_light_down = []
+        kwargs_lens_light_init = []
+        kwargs_lens_light_fixed = []
+        kwargs_lens_light_sigma = []
+        source_model_list = []
+        kwargs_source = []
+        kwargs_source_up = []
+        kwargs_source_down = []
+        kwargs_source_init = []
+        kwargs_source_fixed = []
+        kwargs_source_sigma = []
+        kwargs_ps = []
+        kwargs_ps_up = []
+        kwargs_ps_down = []
+        kwargs_ps_init = []
+        kwargs_ps_fixed = []
+        kwargs_ps_sigma = []
+        creation_lens_source_light = True
+
+        idx_lens = 0
+        idx_lens_light = 0
+        idx_source = 0
+        idx_ps = 0
+
+        multi_plane = False
+        creation_redshift_list = True
+
+        min_redshift, max_redshift, redshift_list = create_redshift_info(lensing_entities_list)
+
+        for lensing_entity in lensing_entities_list:
+            if lensing_entity.type == "galaxy":
+                galaxy = lensing_entity
+
+                if galaxy.redshift > min_redshift:
+                    # SOURCE OF LIGHT
+                    light_list = galaxy.light_model
+                    for light in light_list:
+
+                        # ASSUME same list of models as in the json !!
+                        if light.type == 'LensedPS':
+                            kwargs_ps = kwargs_result['kwargs_ps'][idx_ps]
+                            kwargs_ps_mcmc = None
+                        elif light.type in ['Sersic', 'Shapelets']:
+                            kwargs_source = kwargs_result['kwargs_source'][idx_source]
+                            kwargs_source_mcmc = None
+                        else:
+                            print(f'Light Type {light.type} not yet implemented.')
+
+                        if (kwargs_mcmc is not None) & (light.type in available_profiles):
+                            if light.type == 'LensedPS':
+                                kwargs_ps_mcmc = [arg[idx_ps] for arg in kwargs_mcmc['args_ps']]
+                            elif light.type in ['Sersic', 'Shapelets']:
+                                kwargs_source_mcmc = [arg[idx_source] for arg in kwargs_mcmc['args_source']]
+
+                        if light.type == 'Sersic':
+                            update.sersic_update(light, kwargs_source, kwargs_source_mcmc)
+                            idx_source += 1
+                        elif light.type == 'Shapelets':
+                            update.shapelets_update(light, kwargs_source, kwargs_source_mcmc)
+                            idx_source += 1
+                        elif light.type == 'LensedPS':
+                            update.lensed_point_source_update(light, kwargs_ps, kwargs_ps_mcmc)
+                            idx_ps += 1
+                        else:
+                            pass
+
+                if galaxy.redshift < max_redshift:
+                    # LENSING GALAXY
+                    if galaxy.redshift > min_redshift:
+                        multi_plane = True
+                        print('Multiplane lensing to consider.')
+                    mass_list = galaxy.mass_model
+                    for mass in mass_list:
+
+                        kwargs_lens_mcmc = None
+                        if (kwargs_mcmc is not None) & (mass.type in available_profiles):
+                            kwargs_lens_mcmc = [arg[idx_lens] for arg in kwargs_mcmc['args_lens']]
+
+                        if mass.type == 'PEMD':
+                            kwargs_lens = kwargs_result['kwargs_lens'][idx_lens]
+                            update.pemd_update(mass, kwargs_lens, kwargs_lens_mcmc)
+                            idx_lens += 1
+                        elif mass.type == 'SIE':
+                            kwargs_lens = kwargs_result['kwargs_lens'][idx_lens]
+                            update.sie_update(mass, kwargs_lens, kwargs_lens_mcmc)
+                            idx_lens += 1
+                        else:
+                            print(f'Mass Type {mass.type} not yet implemented.')
+
+
+
+                if galaxy.redshift == min_redshift:
+                    # LENSING LIGHT GALAXY
+                    light_list = galaxy.light_model
+                    for light in light_list:
+                        if light.type in ['Sersic']:
+                            kwargs_lens_light = kwargs_result['kwargs_lens_light'][idx_lens_light]
+                            kwargs_lens_light_mcmc = None
+                        else:
+                            print(f'Light Type {light.type} not yet implemented.')
+
+                        if (kwargs_mcmc is not None) & (light.type in available_profiles):
+                            if light.type in ['Sersic']:
+                                kwargs_lens_light_mcmc = [arg[idx_lens_light] for arg in
+                                                          kwargs_mcmc['args_lens_light']]
+
+                        if light.type == 'Sersic':
+                            update.sersic_update(light, kwargs_lens_light, kwargs_lens_light_mcmc)
+                            idx_lens_light += 1
+                        else:
+                            pass
+                #
+                # if (galaxy.redshift <= min_redshift) and (galaxy.redshift >= max_redshift):
+                #     print(f'REDSHIFT {galaxy.redshift} is not in the range ] {min_red} , {max_red} [')
+
+            elif lensing_entity.type == "external_shear":
+                shear_list = lensing_entity.mass_model
+                for shear_idx in shear_list:
+
+                    kwargs_lens = kwargs_result['kwargs_lens'][idx_lens]
+                    kwargs_lens_mcmc = None
+                    if (kwargs_mcmc is not None) & (shear_idx.type in available_profiles):
+                        kwargs_lens_mcmc = [arg[idx_lens] for arg in kwargs_mcmc['args_lens']]
+
+                    if shear_idx.type == 'ExternalShear':
+                        update.shear_update(shear_idx, kwargs_lens, kwargs_lens_mcmc)
+                        idx_lens += 1
+                    else:
+                        print(f"type of Shear {shear_idx.type} not implemented")
+
+            else:
+                print(f"Lensing entity of type {lensing_entity.type} is unknown.")
+
+    encoder = JSONSerializer(file_name + ending,
+                             obj=lens_coolest, indent=2)
+    lens_coolest_encoded = encoder.dump_jsonpickle()
+
+    return
+
+def create_kwargs_mcmc_from_chain_list(chain_list, kwargs_model, kwargs_params, kwargs_data, kwargs_psf,
+                                       kwargs_numerics, kwargs_constraints, image_likelihood_mask=None, idx_chain=-1,
+                                       likelihood_threshold=None):
+    """
+    function to construct kwargs_mcmc in the right format for the "update_coolest_from_lenstronomy" function
+
+    :param chain_list: list, output of FittingSequence.fitting_sequence()
+    :param kwargs_model: the usual lenstronomy kwargs
+    :param kwargs_params: the usual lenstronomy kwargs
+    :param kwargs_data: the usual lenstronomy kwargs
+    :param kwargs_psf: the usual lenstronomy kwargs
+    :param kwargs_numerics: the usual lenstronomy kwargs
+    :param kwargs_constraints: the usual lenstronomy kwargs
+    :param image_likelihood_mask: the usual lenstronomy kwargs
+    :param idx_chain: int, index of the MCMC chain in the chain_list, default is the last one.
+                    Can be useful if several PSO and MCMC are perfomed in the fitting sequence.
+    :param likelihood_threshold: float, likelihood limit (negative) underwhich the MCMC point is not considered.
+                                 Can be useful if a few chains are stucked in another (less good) minimum
+
+    :return: kwargs_mcmc, list containing all the relevant MCMC points in a userfriendly format
+                       (with linear parameters etc)
+
+    """
+    par_buf = chain_list[idx_chain][1]
+    dist_buf = chain_list[idx_chain][3]
+
+    kwargs_lens_init, kwargs_lens_sigma, kwargs_fixed_lens, kwargs_lower_lens, kwargs_upper_lens = kwargs_params[
+        'lens_model']
+    kwargs_source_init, kwargs_source_sigma, kwargs_fixed_source, kwargs_lower_source, kwargs_upper_source = \
+    kwargs_params['source_model']
+    kwargs_lens_light_init, kwargs_lens_light_sigma, kwargs_fixed_lens_light, kwargs_lower_lens_light, kwargs_upper_lens_light = \
+    kwargs_params['lens_light_model']
+    kwargs_ps_init, kwargs_ps_sigma, kwargs_fixed_ps, kwargs_lower_ps, kwargs_upper_ps = kwargs_params[
+        'point_source_model']
+
+    param_class = Param(kwargs_model, kwargs_fixed_lens=kwargs_fixed_lens,
+                        kwargs_fixed_source=kwargs_fixed_source, kwargs_fixed_lens_light=kwargs_fixed_lens_light,
+                        kwargs_fixed_ps=kwargs_fixed_ps,
+                        kwargs_lower_lens=kwargs_lower_lens, kwargs_lower_source=kwargs_lower_source,
+                        kwargs_lower_lens_light=kwargs_lower_lens_light, kwargs_lower_ps=kwargs_lower_ps,
+                        kwargs_upper_lens=kwargs_upper_lens, kwargs_upper_source=kwargs_upper_source,
+                        kwargs_upper_lens_light=kwargs_upper_lens_light, kwargs_upper_ps=kwargs_upper_ps,
+                        kwargs_lens_init=kwargs_lens_init, **kwargs_constraints)
+
+    image_linear = class_util.create_image_model(kwargs_data, kwargs_psf, kwargs_numerics, kwargs_model,
+                                          image_likelihood_mask=image_likelihood_mask)
+    args_lens = []
+    args_source = []
+    args_lens_light = []
+    args_ps = []
+    for w in range(len(dist_buf)):
+        if likelihood_threshold is not None:
+            if dist_buf[w] < likelihood_threshold :
+                pass
+        kwargs_return = param_class.args2kwargs(par_buf[w])
+        image_linear.image_linear_solve(**kwargs_return)
+        args_lens.append(kwargs_return['kwargs_lens'])
+        args_source.append(kwargs_return['kwargs_source'])
+        args_lens_light.append(kwargs_return['kwargs_lens_light'])
+        args_ps.append(kwargs_return['kwargs_ps'])
+    kwargs_mcmc_results = {'args_lens': args_lens, 'args_source': args_source, 'args_lens_light': args_lens_light,
+                           'args_ps': args_ps}
+    return kwargs_mcmc_results
+
+def create_redshift_info(lensing_entities_list):
+    """
+    Side fuction to create the minimum, maximum and whole redshift list of galaxies in the COOLEST template
+    Note that the redshifts helps knowing which galaxy is a lens, or a source, and if multiplane has to be considered
+
+    :param lensing_entities_list: coolest.template.classes.lensing_entity_list.LensingEntityList object
+    :return: min_redshift, max_redshift, redshift_list ; minimum, maximum and full list of lensing entities redshifts
+    """
+    redshift_list = []
+    for lensing_entity in lensing_entities_list:
+        redshift_list.append(lensing_entity.redshift)
+    min_redshift = np.min(redshift_list)
+    max_redshift = np.max(redshift_list)
+    return min_redshift, max_redshift, redshift_list
+
+
diff --git a/lenstronomy/Util/coolest_read_util.py b/lenstronomy/Util/coolest_read_util.py
new file mode 100644
index 000000000..daf7bc050
--- /dev/null
+++ b/lenstronomy/Util/coolest_read_util.py
@@ -0,0 +1,683 @@
+import numpy as np
+import coolest
+from lenstronomy.Util.param_util import phi_q2_ellipticity, shear_polar2cartesian
+
+
+def shapelet_amp_coolest_to_lenstronomy(value):
+    """
+    Transforms shapelets coefficients from COOLEST conventions (x to the right)
+    to lenstronomy conventions (x following ra, to the left)
+
+    :param value: amplitude of the shapelet (float or np.array) in COOLEST conventions
+    :return: amplitude of the shapelet (float or np.array) in lenstronomy conventions
+    """
+    if value is None:
+        return None
+    else:
+        n = 0  # this is the shapelet order
+        k = 0  # this is the index of a coefficient for a given order
+        new_value = []
+        for coeff in value:
+            if n % 2 == 0:
+                if k % 2 == 1:
+                    coeff = -coeff
+            elif n % 2 == 1:
+                if k % 2 == 0:
+                    coeff = -coeff
+            new_value.append(coeff)
+            k += 1
+            if k == n + 1:
+                n += 1
+                k = 0
+        return new_value
+
+
+def degree_coolest_to_radian_lenstronomy(value):
+    """
+    Transform an angle in degree in COOLEST conventions (from y to negative x - aka East of North)
+    into an angle in radian in lenstronomy conventions (from x to y with x pointing to the left - aka North of East)
+
+    :param value: float, angle in COOLEST conventions
+    :return: float, angle in lenstronomy conventions
+    """
+    if value is None:
+        return None
+    else:
+        lenstro_oriented_degree = - value + 90.
+        if lenstro_oriented_degree >= 180.:
+            lenstro_oriented_degree -= 180.
+        elif lenstro_oriented_degree < 0.:
+            lenstro_oriented_degree += 180
+        return lenstro_oriented_degree * np.pi / 180.
+
+
+def qphi_coolest_to_e1e2_lenstronomy(q, phi):
+    """
+    Transform q and phi (axis ratio, position angle East-of-North) to e1,e2 in lenstronomy
+
+    :param q: float, axis ratio
+    :param phi: float, position angle in COOLEST conventions
+    :return: e1, e2, lenstronomy usual ellipticity parameters
+    """
+    if None in [q, phi]:
+        return None, None
+    else:
+        angle = degree_coolest_to_radian_lenstronomy(phi)
+        e1, e2 = phi_q2_ellipticity(angle, q)
+        return e1, e2
+
+
+def gamma_phi_coolest_to_g1_g2_lenstronomy(gamma_ext, phi_ext):
+    """
+    Transform gamma_ext and phi_ext (shear strength, position angle East-of-North)
+    to gamma1,gamma2 in lenstronomy
+
+    :param gamma_ext: float, shear strenght
+    :param phi_ext: float, shear angle in COOLEST conventions
+    :return: gamma1, gamma2, lenstronomy usual shear parameters
+    """
+    if None in [gamma_ext, phi_ext]:
+        return None, None
+    else:
+        angle = degree_coolest_to_radian_lenstronomy(phi_ext)
+        gamma1, gamma2 = shear_polar2cartesian(angle,gamma_ext)
+        return gamma1, gamma2
+
+
+def ellibounds_coolest_to_lenstronomy(q_down, q_up, phi_down, phi_up):
+    """
+    Transforms upper and lower bounds on coolest ellipticity parameters (q, phi) towards lenstronomy bound on e1, e2
+    The mapping can not be perfect but it's the best we can do
+
+    :param q_down: float, lower bound of axis ratio
+    :param q_up: float, upper bound of axis ratio
+    :param phi_down: float, lower bound of position angle in COOLEST conventions
+    :param phi_up: float, upper bound of position angle in COOLEST conventions
+    :return: e1_down, e1_up, e2_down, e2_up, bounds for lenstronomy usual ellipticity parameters
+    """
+    if None in [q_down, q_up, phi_down, phi_up]:
+        return None, None, None, None
+    else:
+        e1_down = - (1 - q_down) / (1 + q_down)
+        e2_down = - (1 - q_down) / (1 + q_down)
+
+        e1_up = (1 - q_down) / (1 + q_down)
+        e2_up = (1 - q_down) / (1 + q_down)
+
+        # WE LOOSE THE ANGLE INFORMATION AND THE OTHER INFO IS NOT WELL TRANSLATED (best we can do)
+
+        return e1_down, e1_up, e2_down, e2_up
+
+
+def shearbounds_coolest_to_lenstronomy(gamma_ext_down, gamma_ext_up, phi_ext_down, phi_ext_up):
+    """
+    Transforms upper and lower bounds on coolest shear parameters (gamma_ext, phi_ext) towards lenstronomy bounds
+    on gamma_1, gamma_2
+    The mapping can not be perfect but it's the best we can do
+
+    :param gamma_ext_down: float, lower bound of shear strenght
+    :param gamma_ext_up: float, upper bound of shear strenght
+    :param phi_ext_down: float, lower bound of shear position angle in COOLEST conventions
+    :param phi_ext_up: float, upper bound of shear position angle in COOLEST conventions
+    :return: gamma1_down, gamma1_up, gamma2_down, gamma2_up ; bounds for lenstronomy usual shear parameters
+    """
+    if None in [gamma_ext_down, gamma_ext_up, phi_ext_down, phi_ext_up]:
+        return None, None, None, None
+    else:
+        gamma1_down = - gamma_ext_up
+        gamma2_down = - gamma_ext_up
+
+        gamma1_up = gamma_ext_up
+        gamma2_up = gamma_ext_up
+
+        # WE LOOSE THE ANGLE INFORMATION AND THE OTHER INFO IS NOT WELL TRANSLATED (best we can do)
+
+        return gamma1_down, gamma1_up, gamma2_down, gamma2_up
+
+
+def update_kwargs_shear(shear_idx, lens_model_list, kwargs_lens, kwargs_lens_init, kwargs_lens_up, kwargs_lens_down,
+          kwargs_lens_fixed, kwargs_lens_sigma, cleaning=False):
+    """
+    Update the lens model list and kwargs with SHEAR mass model (gamma_ext - phi_ext)
+
+    :param shear_idx: coolest.template.classes.profiles.mass.ExternalShear object
+    :param lens_model_list: the usual lenstronomy lens_model_list
+    :param kwargs_lens: the usual lenstronomy kwargs
+    :param kwargs_lens_init: the usual lenstronomy kwargs
+    :param kwargs_lens_up: the usual lenstronomy kwargs
+    :param kwargs_lens_down: the usual lenstronomy kwargs
+    :param kwargs_lens_fixed: the usual lenstronomy kwargs
+    :param kwargs_lens_sigma: the usual lenstronomy kwargs
+    :param cleaning: bool, if True, will update the empty fields with default values + cleans the kwargs_fixed
+    :return: updated list and kwargs
+    """
+    lens_model_list.append('SHEAR')
+    for shear_name, shear_param in shear_idx.parameters.items():
+        if shear_name == 'gamma_ext':
+            gammaext = getattr(shear_param.point_estimate, 'value')
+            gammaext_up = getattr(shear_param.definition_range, 'max_value')
+            gammaext_down = getattr(shear_param.definition_range, 'min_value')
+            gammaext_fixed = gammaext if getattr(shear_param, 'fixed') else None
+        elif shear_name == 'phi_ext':
+            psiext = getattr(shear_param.point_estimate, 'value')
+            psiext_up = getattr(shear_param.definition_range, 'max_value')
+            psiext_down = getattr(shear_param.definition_range, 'min_value')
+            psiext_fixed = psiext if getattr(shear_param, 'fixed') else None
+        else:
+            print(f"{shear_name} not known")
+    gamma1, gamma2 = gamma_phi_coolest_to_g1_g2_lenstronomy(gammaext, psiext)
+    gamma1_fixed, gamma2_fixed = gamma_phi_coolest_to_g1_g2_lenstronomy(gammaext_fixed, psiext_fixed)
+    gamma1_down, gamma1_up, gamma2_down, gamma2_up = shearbounds_coolest_to_lenstronomy(gammaext_down, gammaext_up, psiext_down,
+                                                                            psiext_up)
+
+    kw_1 = {'gamma1': gamma1, 'gamma2': gamma2, 'ra_0': 0., 'dec_0': 0.}
+    kw_up_1 = {'gamma1': gamma1_up, 'gamma2': gamma2_up, 'ra_0': 0., 'dec_0': 0.}
+    kw_down_1 = {'gamma1': gamma1_down, 'gamma2': gamma2_down, 'ra_0': 0., 'dec_0': 0.}
+    kw_fixed_1 = {'gamma1': gamma1_fixed, 'gamma2': gamma2_fixed, 'ra_0': 0., 'dec_0': 0.}
+
+    kw_ = kw_1.copy()
+    kw_init = kw_1.copy()
+    kw_up = kw_up_1.copy()
+    kw_down = kw_down_1.copy()
+    kw_fixed = kw_fixed_1.copy()
+
+    if cleaning is True:
+        kw_init_default = {'gamma1': 0.0, 'gamma2': -0.0, 'ra_0': 0., 'dec_0': 0.}
+        kw_up_default = {'gamma1': 0.5, 'gamma2': 0.5, 'ra_0': 100, 'dec_0': 100}
+        kw_down_default = {'gamma1': -0.5, 'gamma2': -0.5, 'ra_0': -100, 'dec_0': -100}
+        for key, val in kw_1.items():
+            if val is None:
+                kw_init[key] = kw_init_default[key]
+        for key, val in kw_up_1.items():
+            if val is None:
+                kw_up[key] = kw_up_default[key]
+        for key, val in kw_down_1.items():
+            if val is None:
+                kw_down[key] = kw_down_default[key]
+        for key, val in kw_fixed_1.items():
+            if val is None:
+                del kw_fixed[key]
+
+    kwargs_lens.append(kw_)
+    kwargs_lens_init.append(kw_init)
+    kwargs_lens_up.append(kw_up)
+    kwargs_lens_down.append(kw_down)
+    kwargs_lens_fixed.append(kw_fixed)
+    kwargs_lens_sigma.append({'gamma1': 0.1, 'gamma2': 0.1, 'ra_0': 0., 'dec_0': 0.})
+    print('\t Shear correctly added')
+
+    return
+
+
+def update_kwargs_pemd(mass, lens_model_list, kwargs_lens, kwargs_lens_init, kwargs_lens_up, kwargs_lens_down, kwargs_lens_fixed,
+         kwargs_lens_sigma, cleaning=False):
+    """
+    Update the lens list and kwargs with PEMD mass model
+
+    :param mass: coolest.template.classes.profiles.mass.PEMD object
+    :param lens_model_list: the usual lenstronomy lens_model_list
+    :param kwargs_lens: the usual lenstronomy kwargs
+    :param kwargs_lens_init: the usual lenstronomy kwargs
+    :param kwargs_lens_up: the usual lenstronomy kwargs
+    :param kwargs_lens_down: the usual lenstronomy kwargs
+    :param kwargs_lens_fixed: the usual lenstronomy kwargs
+    :param kwargs_lens_sigma: the usual lenstronomy kwargs
+    :param cleaning: bool, if True, will update the empty fields with default values + cleans the kwargs_fixed
+
+    :return: updated list and kwargs
+    """
+    lens_model_list.append('PEMD')
+    for mass_name, mass_param in mass.parameters.items():
+        if mass_name == 'theta_E':
+            te = getattr(mass_param.point_estimate, 'value')
+            te_up = getattr(mass_param.definition_range, 'max_value')
+            te_down = getattr(mass_param.definition_range, 'min_value')
+            te_fixed = te if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'gamma':
+            gamma = getattr(mass_param.point_estimate, 'value')
+            gamma_up = getattr(mass_param.definition_range, 'max_value')
+            gamma_down = getattr(mass_param.definition_range, 'min_value')
+            gamma_fixed = gamma if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'q':
+            q = getattr(mass_param.point_estimate, 'value')
+            q_up = getattr(mass_param.definition_range, 'max_value')
+            q_down = getattr(mass_param.definition_range, 'min_value')
+            q_fixed = q if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'phi':
+            phi = getattr(mass_param.point_estimate, 'value')
+            phi_up = getattr(mass_param.definition_range, 'max_value')
+            phi_down = getattr(mass_param.definition_range, 'min_value')
+            phi_fixed = phi if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'center_x':
+            center_x = - getattr(mass_param.point_estimate, 'value') if getattr(mass_param.point_estimate,
+                                                                                'value') is not None else None
+            center_x_up = getattr(mass_param.definition_range, 'max_value')
+            center_x_down = getattr(mass_param.definition_range, 'min_value')
+            center_x_fixed = center_x if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'center_y':
+            center_y = getattr(mass_param.point_estimate, 'value')
+            center_y_up = getattr(mass_param.definition_range, 'max_value')
+            center_y_down = getattr(mass_param.definition_range, 'min_value')
+            center_y_fixed = center_y if getattr(mass_param, 'fixed') else None
+        else:
+            print(f"{mass_name} not known")
+
+    e1, e2 = qphi_coolest_to_e1e2_lenstronomy(q, phi)
+    e1_fixed, e2_fixed = qphi_coolest_to_e1e2_lenstronomy(q_fixed, phi_fixed)
+    e1_down, e1_up, e2_down, e2_up = ellibounds_coolest_to_lenstronomy(q_down, q_up, phi_down, phi_up)
+
+    kw_1 = {'theta_E': te, 'gamma': gamma, 'e1': e1, 'e2': e2, 'center_x': center_x, 'center_y': center_y}
+    kw_up_1 = {'theta_E': te_up, 'gamma': gamma_up, 'e1': e1_up, 'e2': e2_up, 'center_x': center_x_up,
+               'center_y': center_y_up}
+    kw_down_1 = {'theta_E': te_down, 'gamma': gamma_down, 'e1': e1_down, 'e2': e2_down,
+                 'center_x': center_x_down, 'center_y': center_y_down}
+    kw_fixed_1 = {'theta_E': te_fixed, 'gamma': gamma_fixed, 'e1': e1_fixed, 'e2': e2_fixed,
+                  'center_x': center_x_fixed, 'center_y': center_y_fixed}
+
+    kw_ = kw_1.copy()
+    kw_init = kw_1.copy()
+    kw_up = kw_up_1.copy()
+    kw_down = kw_down_1.copy()
+    kw_fixed = kw_fixed_1.copy()
+
+    if cleaning is True:
+        kw_init_default = {'theta_E': 1., 'gamma': 2., 'e1': 0.0, 'e2': -0.0, 'center_x': 0., 'center_y': 0.}
+        kw_up_default = {'theta_E': 100, 'gamma': 2.5, 'e1': 0.5, 'e2': 0.5, 'center_x': 100, 'center_y': 100}
+        kw_down_default = {'theta_E': 0, 'gamma': 1.5, 'e1': -0.5, 'e2': -0.5, 'center_x': -100, 'center_y': -100}
+        for key, val in kw_1.items():
+            if val is None:
+                kw_init[key] = kw_init_default[key]
+        for key, val in kw_up_1.items():
+            if val is None:
+                kw_up[key] = kw_up_default[key]
+        for key, val in kw_down_1.items():
+            if val is None:
+                kw_down[key] = kw_down_default[key]
+        for key, val in kw_fixed_1.items():
+            if val is None:
+                del kw_fixed[key]
+
+    kwargs_lens.append(kw_)
+    kwargs_lens_init.append(kw_init)
+    kwargs_lens_up.append(kw_up)
+    kwargs_lens_down.append(kw_down)
+    kwargs_lens_fixed.append(kw_fixed)
+    kwargs_lens_sigma.append({'theta_E': 1.5, 'gamma': 0.2, 'e1': 0.3, 'e2': 0.3, 'center_x': 0.5, 'center_y': 0.5})
+
+    print('\t PEMD correctly added')
+
+    return
+
+
+def update_kwargs_sie(mass, lens_model_list, kwargs_lens, kwargs_lens_init, kwargs_lens_up, kwargs_lens_down, kwargs_lens_fixed,
+        kwargs_lens_sigma, cleaning=False):
+    """
+    Update the lens list and kwargs with SIE mass model
+
+    :param mass: coolest.template.classes.profiles.mass.SIE object
+    :param lens_model_list: the usual lenstronomy lens_model_list
+    :param kwargs_lens: the usual lenstronomy kwargs
+    :param kwargs_lens_init: the usual lenstronomy kwargs
+    :param kwargs_lens_up: the usual lenstronomy kwargs
+    :param kwargs_lens_down: the usual lenstronomy kwargs
+    :param kwargs_lens_fixed: the usual lenstronomy kwargs
+    :param kwargs_lens_sigma: the usual lenstronomy kwargs
+    :param cleaning: bool, if True, will update the empty fields with default values + cleans the kwargs_fixed
+
+    :return: updated list and kwargs
+    """
+    lens_model_list.append('SIE')
+    for mass_name, mass_param in mass.parameters.items():
+        if mass_name == 'theta_E':
+            te = getattr(mass_param.point_estimate, 'value')
+            te_up = getattr(mass_param.definition_range, 'max_value')
+            te_down = getattr(mass_param.definition_range, 'min_value')
+            te_fixed = te if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'q':
+            q = getattr(mass_param.point_estimate, 'value')
+            q_up = getattr(mass_param.definition_range, 'max_value')
+            q_down = getattr(mass_param.definition_range, 'min_value')
+            q_fixed = q if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'phi':
+            phi = getattr(mass_param.point_estimate, 'value')
+            phi_up = getattr(mass_param.definition_range, 'max_value')
+            phi_down = getattr(mass_param.definition_range, 'min_value')
+            phi_fixed = phi if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'center_x':
+            center_x = - getattr(mass_param.point_estimate, 'value') if getattr(mass_param.point_estimate,
+                                                                                'value') is not None else None
+            center_x_up = getattr(mass_param.definition_range, 'max_value')
+            center_x_down = getattr(mass_param.definition_range, 'min_value')
+            center_x_fixed = center_x if getattr(mass_param, 'fixed') else None
+        elif mass_name == 'center_y':
+            center_y = getattr(mass_param.point_estimate, 'value')
+            center_y_up = getattr(mass_param.definition_range, 'max_value')
+            center_y_down = getattr(mass_param.definition_range, 'min_value')
+            center_y_fixed = center_y if getattr(mass_param, 'fixed') else None
+        else:
+            print(f"{mass_name} not known")
+
+    e1, e2 = qphi_coolest_to_e1e2_lenstronomy(q, phi)
+    e1_fixed, e2_fixed = qphi_coolest_to_e1e2_lenstronomy(q_fixed, phi_fixed)
+    e1_down, e1_up, e2_down, e2_up = ellibounds_coolest_to_lenstronomy(q_down, q_up, phi_down, phi_up)
+
+    kw_1 = {'theta_E': te, 'e1': e1, 'e2': e2, 'center_x': center_x, 'center_y': center_y}
+    kw_up_1 = {'theta_E': te_up, 'e1': e1_up, 'e2': e2_up, 'center_x': center_x_up,
+               'center_y': center_y_up}
+    kw_down_1 = {'theta_E': te_down, 'e1': e1_down, 'e2': e2_down,
+                 'center_x': center_x_down, 'center_y': center_y_down}
+    kw_fixed_1 = {'theta_E': te_fixed, 'e1': e1_fixed, 'e2': e2_fixed,
+                  'center_x': center_x_fixed, 'center_y': center_y_fixed}
+
+    kw_ = kw_1.copy()
+    kw_init = kw_1.copy()
+    kw_up = kw_up_1.copy()
+    kw_down = kw_down_1.copy()
+    kw_fixed = kw_fixed_1.copy()
+
+    if cleaning is True:
+        kw_init_default = {'theta_E': 1., 'e1': 0.0, 'e2': -0.0, 'center_x': 0., 'center_y': 0.}
+        kw_up_default = {'theta_E': 100, 'e1': 0.5, 'e2': 0.5, 'center_x': 100, 'center_y': 100}
+        kw_down_default = {'theta_E': 0, 'e1': -0.5, 'e2': -0.5, 'center_x': -100, 'center_y': -100}
+        for key, val in kw_1.items():
+            if val is None:
+                kw_init[key] = kw_init_default[key]
+        for key, val in kw_up_1.items():
+            if val is None:
+                kw_up[key] = kw_up_default[key]
+        for key, val in kw_down_1.items():
+            if val is None:
+                kw_down[key] = kw_down_default[key]
+        for key, val in kw_fixed_1.items():
+            if val is None:
+                del kw_fixed[key]
+
+    kwargs_lens.append(kw_)
+    kwargs_lens_init.append(kw_init)
+    kwargs_lens_up.append(kw_up)
+    kwargs_lens_down.append(kw_down)
+    kwargs_lens_fixed.append(kw_fixed)
+    kwargs_lens_sigma.append({'theta_E': 1.5, 'e1': 0.3, 'e2': 0.3, 'center_x': 0.5, 'center_y': 0.5})
+
+    print('\t SIE correctly added')
+
+    return
+
+
+def update_kwargs_sersic(light, light_model_list, kwargs_light, kwargs_light_init, kwargs_light_up, kwargs_light_down,
+           kwargs_light_fixed, kwargs_light_sigma, cleaning=False):
+    """
+    Update the source list and kwargs with SERSIC_ELLISPE light model
+
+    :param light: coolest.template.classes.profiles.light.Sersic object
+    :param light_model_list: the usual lenstronomy lens_light_model_list or source_light_model_list
+    :param kwargs_light: the usual lenstronomy kwargs
+    :param kwargs_light_init: the usual lenstronomy kwargs
+    :param kwargs_light_up: the usual lenstronomy kwargs
+    :param kwargs_light_down: the usual lenstronomy kwargs
+    :param kwargs_light_fixed: the usual lenstronomy kwargs
+    :param kwargs_light_sigma: the usual lenstronomy kwargs
+    :param cleaning: bool, if True, will update the empty fields with default values + cleans the kwargs_fixed
+
+    :return: updated list and kwargs
+    """
+    light_model_list.append('SERSIC_ELLIPSE')
+    for light_name, light_param in light.parameters.items():
+        if light_name == 'I_eff':
+            amp = getattr(light_param.point_estimate, 'value')
+            amp_up = getattr(light_param.definition_range, 'max_value')
+            amp_down = getattr(light_param.definition_range, 'min_value')
+            amp_fixed = amp if getattr(light_param, 'fixed') else None
+        elif light_name == 'theta_eff':
+            R = getattr(light_param.point_estimate, 'value')
+            R_up = getattr(light_param.definition_range, 'max_value')
+            R_down = getattr(light_param.definition_range, 'min_value')
+            R_fixed = R if getattr(light_param, 'fixed') else None
+        elif light_name == 'n':
+            n = getattr(light_param.point_estimate, 'value')
+            n_up = getattr(light_param.definition_range, 'max_value')
+            n_down = getattr(light_param.definition_range, 'min_value')
+            n_fixed = n if getattr(light_param, 'fixed') else None
+        elif light_name == 'q':
+            q = getattr(light_param.point_estimate, 'value')
+            q_up = getattr(light_param.definition_range, 'max_value')
+            q_down = getattr(light_param.definition_range, 'min_value')
+            q_fixed = q if getattr(light_param, 'fixed') else None
+        elif light_name == 'phi':
+            phi = getattr(light_param.point_estimate, 'value')
+            phi_up = getattr(light_param.definition_range, 'max_value')
+            phi_down = getattr(light_param.definition_range, 'min_value')
+            phi_fixed = phi if getattr(light_param, 'fixed') else None
+        elif light_name == 'center_x':
+            cx = - getattr(light_param.point_estimate, 'value') if getattr(light_param.point_estimate,
+                                                                           'value') is not None else None
+            cx_up = getattr(light_param.definition_range, 'max_value')
+            cx_down = getattr(light_param.definition_range, 'min_value')
+            cx_fixed = cx if getattr(light_param, 'fixed') else None
+        elif light_name == 'center_y':
+            cy = getattr(light_param.point_estimate, 'value')
+            cy_up = getattr(light_param.definition_range, 'max_value')
+            cy_down = getattr(light_param.definition_range, 'min_value')
+            cy_fixed = cy if getattr(light_param, 'fixed') else None
+        else:
+            print(f'Parameter {light_name} unknown in SersicEllipse profile.')
+
+    e1, e2 = qphi_coolest_to_e1e2_lenstronomy(q, phi)
+    e1_fixed, e2_fixed = qphi_coolest_to_e1e2_lenstronomy(q_fixed, phi_fixed)
+    e1_down, e1_up, e2_down, e2_up = ellibounds_coolest_to_lenstronomy(q_down, q_up, phi_down, phi_up)
+
+    kw_1 = {'amp': amp, 'R_sersic': R, 'n_sersic': n, 'e1': e1, 'e2': e2, 'center_x': cx, 'center_y': cy}
+    kw_up_1 = {'R_sersic': R_up, 'n_sersic': n_up, 'e1': e1_up, 'e2': e2_up,
+               'center_x': cx_up, 'center_y': cy_up}
+    kw_down_1 = {'R_sersic': R_down, 'n_sersic': n_down, 'e1': e1_down, 'e2': e2_down,
+                 'center_x': cx_down, 'center_y': cy_down}
+    kw_fixed_1 = {'R_sersic': R_fixed, 'n_sersic': n_fixed, 'e1': e1_fixed,
+                  'e2': e2_fixed, 'center_x': cx_fixed, 'center_y': cy_fixed}
+
+    kw_ = kw_1.copy()
+    kw_init = kw_1.copy()
+    kw_up = kw_up_1.copy()
+    kw_down = kw_down_1.copy()
+    kw_fixed = kw_fixed_1.copy()
+
+    if cleaning is True:
+        kw_init_default = {'amp': 1., 'R_sersic': 1., 'n_sersic': 3.5, 'e1': 0.0, 'e2': -0.0, 'center_x': 0.,
+                           'center_y': 0.}
+        kw_up_default = {'amp': 100, 'R_sersic': 100, 'n_sersic': 8, 'e1': 0.5, 'e2': 0.5, 'center_x': 100,
+                         'center_y': 100}
+        kw_down_default = {'amp': 0, 'R_sersic': 0, 'n_sersic': 0.5, 'e1': -0.5, 'e2': -0.5, 'center_x': -100,
+                           'center_y': -100}
+        for key, val in kw_1.items():
+            if val is None:
+                kw_init[key] = kw_init_default[key]
+        for key, val in kw_up_1.items():
+            if val is None:
+                kw_up[key] = kw_up_default[key]
+        for key, val in kw_down_1.items():
+            if val is None:
+                kw_down[key] = kw_down_default[key]
+        for key, val in kw_fixed_1.items():
+            if val is None:
+                del kw_fixed[key]
+
+    kwargs_light.append(kw_)
+    kwargs_light_init.append(kw_init)
+    kwargs_light_up.append(kw_up)
+    kwargs_light_down.append(kw_down)
+    kwargs_light_fixed.append(kw_fixed)
+    kwargs_light_sigma.append(
+        {'R_sersic': 1.5, 'n_sersic': 1.5, 'e1': 0.2, 'e2': 0.2, 'center_x': 0.5, 'center_y': 0.5})
+    print('\t Sersic (Ellipse) correctly added')
+    return
+
+
+def update_kwargs_shapelets(light, light_model_list, kwargs_light, kwargs_light_init, kwargs_light_up, kwargs_light_down,
+              kwargs_light_fixed, kwargs_light_sigma, cleaning=False):
+    """
+    Update the source list and kwargs with SHAPELETS light model
+
+    :param light: coolest.template.classes.profiles.light.Shapelets object
+    :param light_model_list: the usual lenstronomy lens_light_model_list or source_light_model_list
+    :param kwargs_light: the usual lenstronomy kwargs
+    :param kwargs_light_init: the usual lenstronomy kwargs
+    :param kwargs_light_up: the usual lenstronomy kwargs
+    :param kwargs_light_down: the usual lenstronomy kwargs
+    :param kwargs_light_fixed: the usual lenstronomy kwargs
+    :param kwargs_light_sigma: the usual lenstronomy kwargs
+    :param cleaning: bool, if True, will update the empty fields with default values + cleans the kwargs_fixed
+
+    :return: updated list and kwargs
+    """
+    light_model_list.append('SHAPELETS')
+    for light_name, light_param in light.parameters.items():
+        if light_name == 'beta':
+            b = getattr(light_param.point_estimate, 'value')
+            b_up = getattr(light_param.definition_range, 'max_value')
+            b_down = getattr(light_param.definition_range, 'min_value')
+            b_fixed = b if getattr(light_param, 'fixed') else None
+        elif light_name == 'n_max':
+            nmax = getattr(light_param.point_estimate, 'value')
+            nmax_fixed = nmax if getattr(light_param, 'fixed') else None
+        elif light_name == 'center_x':
+            cx = - getattr(light_param.point_estimate, 'value') if getattr(light_param.point_estimate,
+                                                                           'value') is not None else None
+            cx_up = getattr(light_param.definition_range, 'max_value')
+            cx_down = getattr(light_param.definition_range, 'min_value')
+            cx_fixed = cx if getattr(light_param, 'fixed') else None
+        elif light_name == 'center_y':
+            cy = getattr(light_param.point_estimate, 'value')
+            cy_up = getattr(light_param.definition_range, 'max_value')
+            cy_down = getattr(light_param.definition_range, 'min_value')
+            cy_fixed = cy if getattr(light_param, 'fixed') else None
+        elif light_name == 'amps':
+            amp = shapelet_amp_coolest_to_lenstronomy(getattr(light_param.point_estimate, 'value'))
+            amp_up = shapelet_amp_coolest_to_lenstronomy(getattr(light_param.definition_range, 'max_value'))
+            amp_down = shapelet_amp_coolest_to_lenstronomy(getattr(light_param.definition_range, 'min_value'))
+            amp_fixed = amp if getattr(light_param, 'fixed') else None
+        else:
+            print(f'Parameter {light_name} unknown in Shapelets profile.')
+
+    kw_1 = {'amp': amp, 'beta': b, 'center_x': cx, 'center_y': cy, 'n_max': nmax}
+    kw_up_1 = {'beta': b_up, 'center_x': cx_up, 'center_y': cy_up}
+    kw_down_1 = {'beta': b_down, 'center_x': cx_down, 'center_y': cy_down}
+    kw_fixed_1 = {'beta': b_fixed, 'center_x': cx_fixed, 'center_y': cy_fixed, 'n_max': nmax}
+
+    kw_ = kw_1.copy()
+    kw_init = kw_1.copy()
+    kw_up = kw_up_1.copy()
+    kw_down = kw_down_1.copy()
+    kw_fixed = kw_fixed_1.copy()
+
+    if cleaning is True:
+        kw_init_default = {'amp': 1., 'beta': 0.1, 'center_x': 0., 'center_y': 0., 'n_max': -1}
+        kw_up_default = {'amp': 100, 'beta': 100, 'center_x': 100, 'center_y': 100, 'n_max': 1000}
+        kw_down_default = {'amp': 0, 'beta': 0, 'center_x': -100, 'center_y': -100, 'n_max': -1}
+        for key, val in kw_1.items():
+            if val is None:
+                kw_init[key] = kw_init_default[key]
+        for key, val in kw_up_1.items():
+            if val is None:
+                kw_up[key] = kw_up_default[key]
+        for key, val in kw_down_1.items():
+            if val is None:
+                kw_down[key] = kw_down_default[key]
+        for key, val in kw_fixed_1.items():
+            if val is None:
+                del kw_fixed[key]
+
+    kwargs_light.append(kw_)
+    kwargs_light_init.append(kw_init)
+    kwargs_light_up.append(kw_up)
+    kwargs_light_down.append(kw_down)
+    kwargs_light_fixed.append(kw_fixed)
+    kwargs_light_sigma.append({'beta': 0.5, 'center_x': 0.5, 'center_y': 0.5})
+    print('\t Shapelets correctly added')
+    return
+
+
+def update_kwargs_lensed_ps(light, ps_model_list, kwargs_ps, kwargs_ps_init, kwargs_ps_up, kwargs_ps_down, kwargs_ps_fixed,
+                        kwargs_ps_sigma, cleaning=False):
+    """
+    Update the source list and kwargs with lensed point source "LENSED_POSITION" light model
+
+    :param light: coolest.template.classes.profiles.lightLensedPS object
+    :param ps_model_list: the usual lenstronomy point_source_model_list
+    :param kwargs_ps: the usual lenstronomy kwargs
+    :param kwargs_ps_init: the usual lenstronomy kwargs
+    :param kwargs_ps_up: the usual lenstronomy kwargs
+    :param kwargs_ps_down: the usual lenstronomy kwargs
+    :param kwargs_ps_fixed: the usual lenstronomy kwargs
+    :param kwargs_ps_sigma: the usual lenstronomy kwargs
+    :param cleaning: bool, if True, will update the empty fields with default values + cleans the kwargs_fixed
+
+    :return: updated list and kwargs
+    """
+    ps_model_list.append('LENSED_POSITION')
+
+    try:
+        num_ps = len(getattr(light.parameters['ra_list'].point_estimate, 'value'))
+    except:
+        num_ps = 4
+
+    for light_name, light_param in light.parameters.items():
+        if light_name == 'ra_list':
+            ra = - np.array(getattr(light_param.point_estimate, 'value')) if getattr(light_param.point_estimate,
+                                                                           'value') is not None else None
+            ra_up = getattr(light_param.definition_range, 'max_value')
+            ra_down = getattr(light_param.definition_range, 'min_value')
+            ra_fixed = ra if getattr(light_param, 'fixed') else None
+        elif light_name == 'dec_list':
+            dec = getattr(light_param.point_estimate, 'value')
+            dec_up = getattr(light_param.definition_range, 'max_value')
+            dec_down = getattr(light_param.definition_range, 'min_value')
+            dec_fixed = dec if getattr(light_param, 'fixed') else None
+        elif light_name == 'amps':
+            amp = getattr(light_param.point_estimate, 'value')
+            amp_up = getattr(light_param.definition_range, 'max_value')
+            amp_down = getattr(light_param.definition_range, 'min_value')
+            amp_fixed = amp if getattr(light_param, 'fixed') else None
+        else:
+            print(f'Parameter {light_name} unknown in LensedPS profile.')
+
+    kw_1 = {'point_amp': np.array(amp) if amp is not None else None,
+            'ra_image': np.array(ra) if ra is not None else None,
+            'dec_image': np.array(dec) if dec is not None else None}
+    kw_up_1 = {'ra_image': np.array(ra_up) if ra_up is not None else None,
+               'dec_image': np.array(dec_up) if dec_up is not None else None}
+    kw_down_1 = {'ra_image': np.array(ra_down) if ra_down is not None else None,
+                 'dec_image': np.array(dec_down) if dec_down is not None else None}
+    kw_fixed_1 = {'ra_image': ra_fixed,
+                  'dec_image': dec_fixed}
+
+    kw_ = kw_1.copy()
+    kw_init = kw_1.copy()
+    kw_up = kw_up_1.copy()
+    kw_down = kw_down_1.copy()
+    kw_fixed = kw_fixed_1.copy()
+
+    if cleaning is True:
+        kw_init_default = {'point_amp': np.ones(num_ps), 'ra_image': np.ones(num_ps), 'dec_image': np.ones(num_ps)}
+        kw_up_default = {'ra_image': np.ones(num_ps) * 10, 'dec_image': np.ones(num_ps) * 10}
+        kw_down_default = {'ra_image': np.ones(num_ps) * (-10), 'dec_image': np.ones(num_ps) * (-10)}
+        for key, val in kw_1.items():
+            if val is None:
+                kw_init[key] = kw_init_default[key]
+        for key, val in kw_up_1.items():
+            if val is None:
+                kw_up[key] = kw_up_default[key]
+        for key, val in kw_down_1.items():
+            if val is None:
+                kw_down[key] = kw_down_default[key]
+        for key, val in kw_fixed_1.items():
+            if val is None:
+                del kw_fixed[key]
+
+    kwargs_ps.append(kw_)
+    kwargs_ps_init.append(kw_init)
+    kwargs_ps_up.append(kw_up)
+    kwargs_ps_down.append(kw_down)
+    kwargs_ps_fixed.append(kw_fixed)
+    kwargs_ps_sigma.append({'ra_image': np.ones(num_ps), 'dec_image': np.ones(num_ps)})
+    print('\t Lensed point sources correctly added')
+    return
+
diff --git a/lenstronomy/Util/coolest_update_util.py b/lenstronomy/Util/coolest_update_util.py
new file mode 100644
index 000000000..71b5161ad
--- /dev/null
+++ b/lenstronomy/Util/coolest_update_util.py
@@ -0,0 +1,444 @@
+import numpy as np
+from coolest.template.classes.parameter import PointEstimate
+from coolest.template.classes.probabilities import PosteriorStatistics
+from lenstronomy.Util.param_util import ellipticity2phi_q, shear_cartesian2polar
+
+
+def shapelet_amp_lenstronomy_to_coolest(value):
+    """
+    Transforms shapelets coefficients from lenstronomy conventions (x following ra, to the left) to
+    COOLEST conventions (x to the right)
+
+    :param value: amplitude of the shapelet (float or np.array) in lenstronomy conventions
+    :return: amplitude of the shapelet (float or np.array) in COOLEST conventions
+    """
+    if value is None:
+        return None
+    else:
+        n = 0  # this is the shapelet order
+        k = 0  # this is the index of a coefficient for a given order
+        new_value = []
+        for coeff in value:
+
+            if n % 2 == 0:
+                if k % 2 == 1:
+                    coeff = -coeff
+            elif n % 2 == 1:
+                if k % 2 == 0:
+                    coeff = -coeff
+            new_value.append(coeff)
+            k += 1
+            if k == n + 1:
+                n += 1
+                k = 0
+
+        return new_value
+
+
+def radian_lenstronomy_to_degree_coolest(value):
+    """
+    Transform an angle in radian from lenstronomy (from x to y with x pointing to the left - aka North of East)
+    into an angle in degree for the COOLEST (from y to negative x - aka East of North) with folding
+
+    :param value: float, angle in lenstronomy conventions
+    :return: float, angle in COOLEST conventions
+    """
+    coolest_oriented_degree = folding_coolest(radian_lenstronomy_to_degree(value))
+    return coolest_oriented_degree
+
+
+def radian_lenstronomy_to_degree(value):
+    """
+    Transform an angle in radian from lenstronomy (from x to y with x pointing to the left - aka North of East)
+    into an angle in degree for the COOLEST (from y to negative x - aka East of North) without folding in ]-90;90],
+    which is COOLEST convention
+
+    :param value: float, angle in lenstronomy conventions
+    :return: float, angle almost in COOLEST conventions (without folding)
+    """
+
+    lenstro_degree = value * 180. / np.pi
+    coolest_oriented_degree = lenstro_degree - 90.
+    coolest_oriented_degree *= -1
+    return coolest_oriented_degree
+
+
+def folding_coolest(value):
+    """
+    Folds the angle (already in degree with COOLEST East of North convention) into COOLEST range, ]-90;90]
+
+    :param value: float, angle almost in COOLEST conventions (without folding in ]-90;90])
+    :return: float, angle in COOLEST conventions (with folding in ]-90;90])
+    """
+    coolest_oriented_degree = value
+    if type(coolest_oriented_degree) == type(np.array([])):
+        for idx, val in enumerate(coolest_oriented_degree):
+            if val <= -90.:
+                coolest_oriented_degree[idx] += 180.
+            elif val > 90.:
+                coolest_oriented_degree[idx] -= 180.
+    else:
+        if coolest_oriented_degree <= -90:
+            coolest_oriented_degree += 180.
+        elif coolest_oriented_degree > 90:
+            coolest_oriented_degree -= 180.
+    return coolest_oriented_degree
+
+
+def e1e2_lenstronomy_to_qphi_coolest(e1, e2):
+    """
+    Transform e1,e2 in lenstronomy to q and phi (axis ratio, position angle East-of-North)
+
+    :param e1: lenstronomy usual ellipticity parameters
+    :param e2: lenstronomy usual ellipticity parameters
+
+    :return: q, phi ; axis ratio and position angle in COOLEST conventions
+    """
+    angle,q = ellipticity2phi_q(e1,e2)
+    phi = radian_lenstronomy_to_degree_coolest(angle)
+    return q, phi
+
+
+def g1g2_lenstronomy_to_gamma_phi_coolest(gamma1, gamma2):
+    """
+    Transform gamma1,gamma2 in lenstronomy to gamma_ext and phi_ext (shear strength, position angle East-of-North)
+    with folding
+
+    :param gamma1: lenstronomy usual shear parameters
+    :param gamma2: lenstronomy usual shear parameters
+
+    :return: gamma_ext, phi_ext ; shear strenght and shear position angle in COOLEST conventions (with folding)
+    """
+    angle, gamma_ext = shear_cartesian2polar(gamma1, gamma2)
+    phi_ext = radian_lenstronomy_to_degree_coolest(angle)
+    return gamma_ext, phi_ext
+
+
+def g1g2_lenstronomy_to_gamma_phi(gamma1, gamma2):
+    """
+    Transform gamma1,gamma2 in lenstronomy to gamma_ext and phi_ext (shear strength, position angle East-of-North)
+    without folding
+
+    :param gamma1: lenstronomy usual shear parameters
+    :param gamma2: lenstronomy usual shear parameters
+
+    :return: gamma_ext, phi_ext ; shear strenght and shear position angle almost in COOLEST conventions (without folding)
+    """
+
+    angle = np.arctan2(gamma2, gamma1) / 2.
+    phi_ext = radian_lenstronomy_to_degree(angle)
+
+    gamma_ext = np.sqrt(gamma1 ** 2 + gamma2 ** 2)
+
+    return gamma_ext, phi_ext
+
+
+def shear_update(shear_idx, kwargs_lens, kwargs_lens_mcmc=None):
+    """
+    Update the COOLEST SHEAR mass model (gamma_ext - phi_ext) with results in kwargs_lens
+
+    :param shear_idx: coolest.template.classes.profiles.mass.ExternalShear object
+    :param kwargs_lens: dictionnary with the point estimate
+
+    :return: updated shear_idx
+    """
+    gamma_ext, phi_ext = g1g2_lenstronomy_to_gamma_phi_coolest(float(kwargs_lens['gamma1']),
+                                                               float(kwargs_lens['gamma2']))
+    shear_idx.parameters['gamma_ext'].set_point_estimate(PointEstimate(float(gamma_ext)))
+    shear_idx.parameters['phi_ext'].set_point_estimate(PointEstimate(float(phi_ext)))
+
+    if kwargs_lens_mcmc is not None:
+        g1 = [arg['gamma1'] for arg in kwargs_lens_mcmc]
+        g2 = [arg['gamma2'] for arg in kwargs_lens_mcmc]
+
+        g_ext, p_ext = g1g2_lenstronomy_to_gamma_phi(np.array(g1), np.array(g2))
+
+        g_ext_mean = np.mean(g_ext)
+        g_ext_16, g_ext_50, g_ext_84 = np.quantile(g_ext, [0.16, 0.5, 0.84])
+        shear_idx.parameters['gamma_ext'].set_posterior(PosteriorStatistics(float(g_ext_mean), float(g_ext_50),
+                                                                            float(g_ext_16), float(g_ext_84)))
+
+        p_ext_mean = folding_coolest(np.mean(p_ext))
+        p_ext_16, p_ext_50, p_ext_84 = folding_coolest(np.quantile(p_ext, [0.16, 0.5, 0.84]))
+        shear_idx.parameters['phi_ext'].set_posterior(PosteriorStatistics(float(p_ext_mean), float(p_ext_50),
+                                                                          float(p_ext_16), float(p_ext_84)))
+    print('shear correctly updated')
+
+    return
+
+
+def pemd_update(mass, kwargs_lens, kwargs_lens_mcmc=None):
+    """
+    Update the COOLEST PEMD mass model with results in kwargs_lens
+
+    :param mass: coolest.template.classes.profiles.mass.PEMD object
+    :param kwargs_lens: dictionnary with the point estimate
+
+    :return: updated mass
+    """
+    q, phi = e1e2_lenstronomy_to_qphi_coolest(float(kwargs_lens['e1']), float(kwargs_lens['e2']))
+    mass.parameters['theta_E'].set_point_estimate(PointEstimate(float(kwargs_lens['theta_E'])))
+    mass.parameters['gamma'].set_point_estimate(PointEstimate(float(kwargs_lens['gamma'])))
+    mass.parameters['q'].set_point_estimate(PointEstimate(float(q)))
+    mass.parameters['phi'].set_point_estimate(PointEstimate(float(phi)))
+    mass.parameters['center_x'].set_point_estimate(PointEstimate(-float(kwargs_lens['center_x'])))
+    mass.parameters['center_y'].set_point_estimate(PointEstimate(float(kwargs_lens['center_y'])))
+
+    if kwargs_lens_mcmc is not None:
+        te = [arg['theta_E'] for arg in kwargs_lens_mcmc]
+        te_mean = np.mean(te)
+        te_16, te_50, te_84 = np.quantile(te, [0.16, 0.5, 0.84])
+        mass.parameters['theta_E'].set_posterior(PosteriorStatistics(float(te_mean), float(te_50),
+                                                                     float(te_16), float(te_84)))
+
+        g = [arg['gamma'] for arg in kwargs_lens_mcmc]
+        g_mean = np.mean(g)
+        g_16, g_50, g_84 = np.quantile(g, [0.16, 0.5, 0.84])
+        mass.parameters['gamma'].set_posterior(PosteriorStatistics(float(g_mean), float(g_50),
+                                                                   float(g_16), float(g_84)))
+
+        e1 = [arg['e1'] for arg in kwargs_lens_mcmc]
+        e2 = [arg['e2'] for arg in kwargs_lens_mcmc]
+        ql, phil = e1e2_lenstronomy_to_qphi_coolest(np.array(e1), np.array(e2))
+
+        ql_mean = np.mean(ql)
+        ql_16, ql_50, ql_84 = np.quantile(ql, [0.16, 0.5, 0.84])
+        mass.parameters['q'].set_posterior(PosteriorStatistics(float(ql_mean), float(ql_50),
+                                                               float(ql_16), float(ql_84)))
+
+        phil_mean = np.mean(phil)
+        phil_16, phil_50, phil_84 = np.quantile(phil, [0.16, 0.5, 0.84])
+        mass.parameters['phi'].set_posterior(PosteriorStatistics(float(phil_mean), float(phil_50),
+                                                                 float(phil_16), float(phil_84)))
+
+        cx = [arg['center_x'] for arg in kwargs_lens_mcmc]
+        cx_mean = np.mean(cx)
+        cx_16, cx_50, cx_84 = np.quantile(cx, [0.16, 0.5, 0.84])
+        mass.parameters['center_x'].set_posterior(PosteriorStatistics(-float(cx_mean), -float(cx_50),
+                                                                      -float(cx_16), -float(cx_84)))
+
+        cy = [arg['center_y'] for arg in kwargs_lens_mcmc]
+        cy_mean = np.mean(cy)
+        cy_16, cy_50, cy_84 = np.quantile(cy, [0.16, 0.5, 0.84])
+        mass.parameters['center_y'].set_posterior(PosteriorStatistics(float(cy_mean), float(cy_50),
+                                                                      float(cy_16), float(cy_84)))
+
+    print('PEMD correctly updated')
+
+    return
+
+
+def sie_update(mass, kwargs_lens, kwargs_lens_mcmc=None):
+    """
+    Update the COOLEST SIE mass model with results in kwargs_lens
+
+    :param mass: coolest.template.classes.profiles.mass.SIE object
+    :param kwargs_lens : dictionnary with the point estimate
+
+    :return: updated mass
+    """
+
+    q, phi = e1e2_lenstronomy_to_qphi_coolest(float(kwargs_lens['e1']), float(kwargs_lens['e2']))
+    mass.parameters['theta_E'].set_point_estimate(PointEstimate(float(kwargs_lens['theta_E'])))
+    mass.parameters['q'].set_point_estimate(PointEstimate(float(q)))
+    mass.parameters['phi'].set_point_estimate(PointEstimate(float(phi)))
+    mass.parameters['center_x'].set_point_estimate(PointEstimate(-float(kwargs_lens['center_x'])))
+    mass.parameters['center_y'].set_point_estimate(PointEstimate(float(kwargs_lens['center_y'])))
+
+    if kwargs_lens_mcmc is not None:
+        te = [arg['theta_E'] for arg in kwargs_lens_mcmc]
+        te_mean = np.mean(te)
+        te_16, te_50, te_84 = np.quantile(te, [0.16, 0.5, 0.84])
+        mass.parameters['theta_E'].set_posterior(PosteriorStatistics(float(te_mean), float(te_50),
+                                                                     float(te_16), float(te_84)))
+
+        e1 = [arg['e1'] for arg in kwargs_lens_mcmc]
+        e2 = [arg['e2'] for arg in kwargs_lens_mcmc]
+        ql, phil = e1e2_lenstronomy_to_qphi_coolest(np.array(e1), np.array(e2))
+
+        ql_mean = np.mean(ql)
+        ql_16, ql_50, ql_84 = np.quantile(ql, [0.16, 0.5, 0.84])
+        mass.parameters['q'].set_posterior(PosteriorStatistics(float(ql_mean), float(ql_50),
+                                                               float(ql_16), float(ql_84)))
+
+        phil_mean = np.mean(phil)
+        phil_16, phil_50, phil_84 = np.quantile(phil, [0.16, 0.5, 0.84])
+        mass.parameters['phi'].set_posterior(PosteriorStatistics(float(phil_mean), float(phil_50),
+                                                                 float(phil_16), float(phil_84)))
+
+        cx = [arg['center_x'] for arg in kwargs_lens_mcmc]
+        cx_mean = np.mean(cx)
+        cx_16, cx_50, cx_84 = np.quantile(cx, [0.16, 0.5, 0.84])
+        mass.parameters['center_x'].set_posterior(PosteriorStatistics(-float(cx_mean), -float(cx_50),
+                                                                      -float(cx_16), -float(cx_84)))
+
+        cy = [arg['center_y'] for arg in kwargs_lens_mcmc]
+        cy_mean = np.mean(cy)
+        cy_16, cy_50, cy_84 = np.quantile(cy, [0.16, 0.5, 0.84])
+        mass.parameters['center_y'].set_posterior(PosteriorStatistics(float(cy_mean), float(cy_50),
+                                                                      float(cy_16), float(cy_84)))
+
+    print('SIE correctly updated')
+
+    return
+
+
+def sersic_update(light, kwargs_light, kwargs_light_mcmc=None):
+    """
+    Update the COOLEST Sersic (ellipse) light model with results in kwargs_light
+
+    :param light: coolest.template.classes.profiles.light.Sersic object
+    :param kwargs_light: dictionnary with the point estimate
+
+    :return: updated light
+    """
+    q, phi = e1e2_lenstronomy_to_qphi_coolest(float(kwargs_light['e1']), float(kwargs_light['e2']))
+    light.parameters['I_eff'].set_point_estimate(PointEstimate(float(kwargs_light['amp'])))
+    light.parameters['theta_eff'].set_point_estimate(PointEstimate(float(kwargs_light['R_sersic'])))
+    light.parameters['n'].set_point_estimate(PointEstimate(float(kwargs_light['n_sersic'])))
+    light.parameters['q'].set_point_estimate(PointEstimate(float(q)))
+    light.parameters['phi'].set_point_estimate(PointEstimate(float(phi)))
+    light.parameters['center_x'].set_point_estimate(PointEstimate(-float(kwargs_light['center_x'])))
+    light.parameters['center_y'].set_point_estimate(PointEstimate(float(kwargs_light['center_y'])))
+
+    if kwargs_light_mcmc is not None:
+        a = [arg['amp'] for arg in kwargs_light_mcmc]
+        a_mean = np.mean(a)
+        a_16, a_50, a_84 = np.quantile(a, [0.16, 0.5, 0.84])
+        light.parameters['I_eff'].set_posterior(PosteriorStatistics(float(a_mean), float(a_50),
+                                                                    float(a_16), float(a_84)))
+
+        rs = [arg['R_sersic'] for arg in kwargs_light_mcmc]
+        rs_mean = np.mean(rs)
+        rs_16, rs_50, rs_84 = np.quantile(rs, [0.16, 0.5, 0.84])
+        light.parameters['theta_eff'].set_posterior(PosteriorStatistics(float(rs_mean), float(rs_50),
+                                                                        float(rs_16), float(rs_84)))
+
+        ns = [arg['n_sersic'] for arg in kwargs_light_mcmc]
+        ns_mean = np.mean(ns)
+        ns_16, ns_50, ns_84 = np.quantile(ns, [0.16, 0.5, 0.84])
+        light.parameters['n'].set_posterior(PosteriorStatistics(float(ns_mean), float(ns_50),
+                                                                float(ns_16), float(ns_84)))
+
+        e1 = [arg['e1'] for arg in kwargs_light_mcmc]
+        e2 = [arg['e2'] for arg in kwargs_light_mcmc]
+        ql, phil = e1e2_lenstronomy_to_qphi_coolest(np.array(e1), np.array(e2))
+
+        ql_mean = np.mean(ql)
+        ql_16, ql_50, ql_84 = np.quantile(ql, [0.16, 0.5, 0.84])
+        light.parameters['q'].set_posterior(PosteriorStatistics(float(ql_mean), float(ql_50),
+                                                                float(ql_16), float(ql_84)))
+
+        phil_mean = np.mean(phil)
+        phil_16, phil_50, phil_84 = np.quantile(phil, [0.16, 0.5, 0.84])
+        light.parameters['phi'].set_posterior(PosteriorStatistics(float(phil_mean), float(phil_50),
+                                                                  float(phil_16), float(phil_84)))
+
+        cx = [arg['center_x'] for arg in kwargs_light_mcmc]
+        cx_mean = np.mean(cx)
+        cx_16, cx_50, cx_84 = np.quantile(cx, [0.16, 0.5, 0.84])
+        light.parameters['center_x'].set_posterior(PosteriorStatistics(-float(cx_mean), -float(cx_50),
+                                                                       -float(cx_16), -float(cx_84)))
+
+        cy = [arg['center_y'] for arg in kwargs_light_mcmc]
+        cy_mean = np.mean(cy)
+        cy_16, cy_50, cy_84 = np.quantile(cy, [0.16, 0.5, 0.84])
+        light.parameters['center_y'].set_posterior(PosteriorStatistics(float(cy_mean), float(cy_50),
+                                                                       float(cy_16), float(cy_84)))
+
+    print('Sersic (Ellipse) correctly updated')
+    return
+
+
+def shapelets_update(light, kwargs_light, kwargs_light_mcmc=None):
+    """
+    Update the COOLEST Shapelets light model with results in kwargs_light
+
+    :param light: coolest.template.classes.profiles.light.Shapelets object
+    :param kwargs_light: dictionnary with the point estimate
+
+    :return: updated light
+    """
+    light.parameters['amps'].set_point_estimate(
+        PointEstimate(shapelet_amp_lenstronomy_to_coolest(np.ndarray.tolist(kwargs_light['amp']))))
+    light.parameters['beta'].set_point_estimate(PointEstimate(float(kwargs_light['beta'])))
+    light.parameters['n_max'].set_point_estimate(PointEstimate(int(kwargs_light['n_max'])))
+    light.parameters['center_x'].set_point_estimate(PointEstimate(-float(kwargs_light['center_x'])))
+    light.parameters['center_y'].set_point_estimate(PointEstimate(float(kwargs_light['center_y'])))
+
+    if kwargs_light_mcmc is not None:
+        a = [arg['amp'] for arg in kwargs_light_mcmc]
+        a_mean = np.mean(a, axis=0)
+        a_16, a_50, a_84 = np.quantile(a, [0.16, 0.5, 0.84], axis=0)
+        light.parameters['amps'].set_posterior(PosteriorStatistics(shapelet_amp_lenstronomy_to_coolest(np.ndarray.tolist(a_mean)),
+                                                                   shapelet_amp_lenstronomy_to_coolest(np.ndarray.tolist(a_50)),
+                                                                   shapelet_amp_lenstronomy_to_coolest(np.ndarray.tolist(a_16)),
+                                                                   shapelet_amp_lenstronomy_to_coolest(np.ndarray.tolist(a_84))))
+
+        b = [arg['beta'] for arg in kwargs_light_mcmc]
+        b_mean = np.mean(b)
+        b_16, b_50, b_84 = np.quantile(b, [0.16, 0.5, 0.84])
+        light.parameters['beta'].set_posterior(PosteriorStatistics(float(b_mean), float(b_50),
+                                                                   float(b_16), float(b_84)))
+
+        nmax = [arg['n_max'] for arg in kwargs_light_mcmc]
+        nmax_mean = np.mean(nmax)
+        nmax_16, nmax_50, nmax_84 = np.quantile(nmax, [0.16, 0.5, 0.84])
+        light.parameters['n_max'].set_posterior(PosteriorStatistics(float(nmax_mean), float(nmax_50),
+                                                                    float(nmax_16), float(nmax_84)))
+
+        cx = [arg['center_x'] for arg in kwargs_light_mcmc]
+        cx_mean = np.mean(cx)
+        cx_16, cx_50, cx_84 = np.quantile(cx, [0.16, 0.5, 0.84])
+        light.parameters['center_x'].set_posterior(PosteriorStatistics(-float(cx_mean), -float(cx_50),
+                                                                       -float(cx_16), -float(cx_84)))
+
+        cy = [arg['center_y'] for arg in kwargs_light_mcmc]
+        cy_mean = np.mean(cy)
+        cy_16, cy_50, cy_84 = np.quantile(cy, [0.16, 0.5, 0.84])
+        light.parameters['center_y'].set_posterior(PosteriorStatistics(float(cy_mean), float(cy_50),
+                                                                       float(cy_16), float(cy_84)))
+
+    print('Shapelets correctly updated')
+    return
+
+
+def lensed_point_source_update(light, kwargs_ps, kwargs_ps_mcmc=None):
+    """
+    Update the COOLEST LensedPS light model with results in kwargs_ps
+
+    :param light: coolest.template.classes.profiles.light.LensedPS object
+    :param kwargs_ps: dictionnary with the point estimate
+
+    :return: updated light
+    """
+    light.parameters['amps'].set_point_estimate(PointEstimate(np.ndarray.tolist(kwargs_ps['point_amp'])))
+    light.parameters['ra_list'].set_point_estimate(PointEstimate(np.ndarray.tolist(-kwargs_ps['ra_image'])))
+    light.parameters['dec_list'].set_point_estimate(PointEstimate(np.ndarray.tolist(kwargs_ps['dec_image'])))
+
+    if kwargs_ps_mcmc is not None:
+        a = [arg['point_amp'] for arg in kwargs_ps_mcmc]
+        a_mean = np.mean(a, axis=0)
+        a_16, a_50, a_84 = np.quantile(a, [0.16, 0.5, 0.84], axis=0)
+        light.parameters['amps'].set_posterior(PosteriorStatistics(np.ndarray.tolist(a_mean),
+                                                                   np.ndarray.tolist(a_50),
+                                                                   np.ndarray.tolist(a_16),
+                                                                   np.ndarray.tolist(a_84)))
+
+        ra = [arg['ra_image'] for arg in kwargs_ps_mcmc]
+        ra_mean = np.mean(ra, axis=0)
+        ra_16, ra_50, ra_84 = np.quantile(ra, [0.16, 0.5, 0.84], axis=0)
+        light.parameters['ra_list'].set_posterior(PosteriorStatistics(np.ndarray.tolist(-ra_mean),
+                                                                      np.ndarray.tolist(-ra_50),
+                                                                      np.ndarray.tolist(-ra_16),
+                                                                      np.ndarray.tolist(-ra_84)))
+        dec = [arg['dec_image'] for arg in kwargs_ps_mcmc]
+        dec_mean = np.mean(dec, axis=0)
+        dec_16, dec_50, dec_84 = np.quantile(dec, [0.16, 0.5, 0.84], axis=0)
+        light.parameters['dec_list'].set_posterior(PosteriorStatistics(np.ndarray.tolist(dec_mean),
+                                                                       np.ndarray.tolist(dec_50),
+                                                                       np.ndarray.tolist(dec_16),
+                                                                       np.ndarray.tolist(dec_84)))
+
+    print('Lensed point source correctly updated')
+    return
diff --git a/setup.py b/setup.py
index 2d3b9277c..91a88c4fd 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ def run_tests(self):
             ]
 tests_require = ['pytest>=2.3', "mock", 'colossus==1.3.0', 'slitronomy==0.3.2',
                  'emcee>=3.0.0', 'dynesty', 'nestcheck', 'pymultinest', 'zeus-mcmc>=2.4.0',
-                 'nautilus-sampler>=0.2.1',
+                 'nautilus-sampler>=0.2.1', 'coolest',
                  ]
 
 PACKAGE_PATH = os.path.abspath(os.path.join(__file__, os.pardir))


Test patch:
diff --git a/test/test_Util/coolest_template_pemd_pyAPI.json b/test/test_Util/coolest_template_pemd_pyAPI.json
new file mode 100644
index 000000000..ec32aead0
--- /dev/null
+++ b/test/test_Util/coolest_template_pemd_pyAPI.json
@@ -0,0 +1,1135 @@
+{
+  "py/object": "coolest.template.standard.COOLEST",
+  "mode": "MAP",
+  "coordinates_origin": {
+    "py/object": "coolest.template.classes.coordinates.CoordinatesOrigin",
+    "ra": "00h11m20.244s",
+    "dec": "-08d45m51.48s",
+    "documentation": "Defines the origin of the coordinate system, given in RA-Dec.\n    That origin is then assumed to lie at the center of a data pixel."
+  },
+  "lensing_entities": {
+    "py/object": "coolest.template.classes.lensing_entity_list.LensingEntityList",
+    "documentation": "The list of components that define the lensing system.\n    In COOLEST, a \"lensing entity\" means either a galaxy, or an external shear component.",
+    "py/seq": [
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shearrrrr",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShear",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shear",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShearrrr",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+
+
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a lens galaxy",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.PEMD",
+              "type": "PEMD",
+              "documentation": "Powerlaw elliptical mass distribution",
+              "parameters": {
+                "theta_E": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-PEMD_0-theta_E"
+                },
+                "gamma": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Mass density slope at Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 1.0,
+                    "max_value": 3.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma$",
+                  "id": "galaxy_1-mass_model-PEMD_0-gamma"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-mass_model-PEMD_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-mass_model-PEMD_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-mass_model-PEMD_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-mass_model-PEMD_0-center_y"
+                }
+              },
+              "id": "galaxy_1-mass_model-PEMD_0"
+            }
+          ]
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersicccc",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            },
+
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a source galaxy",
+        "redshift": 2.0,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.11,
+                    "median": 0.15,
+                    "percentile_16th": 0.03,
+                    "percentile_84th": 0.05
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_2-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_2-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Sersic_0"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "observation": {
+    "py/object": "coolest.template.classes.observation.Observation",
+    "pixels": {
+      "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+      "fits_file": {
+        "py/object": "coolest.template.classes.fits_file.FitsFile",
+        "path": "test_image.fits"
+      },
+      "documentation": "",
+      "field_of_view_x": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "field_of_view_y": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "num_pix_x": 100,
+      "num_pix_y": 100
+    },
+    "exposure_time": null,
+    "mag_zero_point": null,
+    "mag_sky_brightness": null,
+    "noise": {
+      "py/object": "coolest.template.classes.noise.NoiseMap",
+      "type": "NoiseMap",
+      "noise_map": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_image.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "num_pix_x": 102,
+        "num_pix_y": 102
+      },
+      "documentation": "Noise characterized by a noise map, which contains diagonal elements of the data covariance matrix"
+    },
+    "documentation": "Defines the observation itself, that is the image pixels, the exposure time,\n    the noise model and/or properties, the magnitude zero-point and sky brightness."
+  },
+  "instrument": {
+    "py/object": "coolest.template.classes.instrument.Instrument",
+    "name": "some instrument",
+    "band": "F160W",
+    "pixel_size": 0.08,
+    "readout_noise": 4,
+    "psf": {
+      "py/object": "coolest.template.classes.psf.PixelatedPSF",
+      "type": "PixelatedPSF",
+      "description": "a PSF kernel",
+      "pixels": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_psf.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -3.8,
+            3.7
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -3.8,
+            3.7
+          ]
+        },
+        "num_pix_x": 99,
+        "num_pix_y": 99
+      },
+      "documentation": ""
+    },
+    "documentation": "Defines the instrument used for the observation.\n    This includes the name of the telescope and detector, the filter, \n    the pixel size, the readout noise and the point spread function (PSF)."
+  },
+  "cosmology": {
+    "py/object": "coolest.template.classes.cosmology.Cosmology",
+    "H0": 73.0,
+    "Om0": 0.3,
+    "astropy_name": "FlatLambdaCDM",
+    "documentation": "Defines the cosmological model. \n    Currently, only FlatLambdaCDM from astropy is supported, based on H0 and Omega_m."
+  },
+  "standard": "COOLEST",
+  "meta": {},
+  "exclude_keys": [
+    "documentation",
+    "id",
+    "latex_str",
+    "units",
+    "fixed",
+    "definition_range",
+    "likelihoods",
+    "regularizations",
+    "exclude_keys"
+  ]
+}
diff --git a/test/test_Util/coolest_template_pemd_random_pyAPI.json b/test/test_Util/coolest_template_pemd_random_pyAPI.json
new file mode 100644
index 000000000..479398955
--- /dev/null
+++ b/test/test_Util/coolest_template_pemd_random_pyAPI.json
@@ -0,0 +1,1791 @@
+{
+  "py/object": "coolest.template.standard.COOLEST",
+  "mode": "MOCK",
+  "coordinates_origin": {
+    "py/object": "coolest.template.classes.coordinates.CoordinatesOrigin",
+    "ra": "00h11m20.244s",
+    "dec": "-08d45m51.48s",
+    "documentation": "Defines the origin of the coordinate system, given in RA-Dec.\n    That origin is then assumed to lie at the center of a data pixel."
+  },
+  "lensing_entities": {
+    "py/object": "coolest.template.classes.lensing_entity_list.LensingEntityList",
+    "documentation": "The list of components that define the lensing system.\n    In COOLEST, a \"lensing entity\" means either a galaxy, or an external shear component.",
+    "py/seq": [
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shearrrrr",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShear",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+
+
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shear",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShear",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_extttt": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shear",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShearrrrrr",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a lens galaxy",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.PEMD",
+              "type": "PEMD",
+              "documentation": "Powerlaw elliptical mass distribution",
+              "parameters": {
+                "theta_EEEEE": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-PEMD_0-theta_E"
+                },
+                "theta_E": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-PEMD_0-theta_E"
+                },
+                "gamma": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Mass density slope at Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 1.0,
+                    "max_value": 3.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma$",
+                  "id": "galaxy_1-mass_model-PEMD_0-gamma"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-mass_model-PEMD_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-mass_model-PEMD_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-mass_model-PEMD_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-mass_model-PEMD_0-center_y"
+                }
+              },
+              "id": "galaxy_1-mass_model-PEMD_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.mass.SIE",
+              "type": "SIE",
+              "documentation": "Powerlaw elliptical mass distribution",
+              "parameters": {
+                "theta_EEEEE": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-SIE_0-theta_E"
+                },
+                "theta_E": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-SIE_0-theta_E"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-mass_model-SIE_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-mass_model-SIE_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-mass_model-SIE_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-mass_model-SIE_0-center_y"
+                }
+              },
+              "id": "galaxy_1-mass_model-SIE_0"
+            }
+          ]
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersicccc",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            },
+
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_efffffff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a source galaxy",
+        "redshift": 2.0,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Shapelets",
+              "type": "Shapelets",
+              "documentation": "Set of shapelet functions",
+              "parameters": {
+                "n_maxxxxxx": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Maximum order of the Shapelet decomposition",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -1,
+                    "max_value": null
+                  },
+                  "fixed": true,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 3
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\n_{\\rm max}$",
+                  "id": "galaxy_2-light_model-Shapelets_1-n_max"
+                },
+                "n_max": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Maximum order of the Shapelet decomposition",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -1,
+                    "max_value": null
+                  },
+                  "fixed": true,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 3
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\n_{\\rm max}$",
+                  "id": "galaxy_2-light_model-Shapelets_1-n_max"
+                },
+                "beta": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelet characteristic scale",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\beta$",
+                  "id": "galaxy_2-light_model-Shapelets_1-beta"
+                },
+                "amps": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Set of amplitude values for each shapelet function",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$A$",
+                  "id": "galaxy_2-light_model-Shapelets-amps"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelets center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Shapelets_1-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelets center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Shapelets_1-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Shapelets_1"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.11,
+                    "median": 0.15,
+                    "percentile_16th": 0.03,
+                    "percentile_84th": 0.05
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_2-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_2-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Sersic_0"
+            },
+
+
+            {
+              "py/object": "coolest.template.classes.profiles.light.LensedPS",
+              "type": "LensedPS",
+              "documentation": "Set of lensed point sources",
+              "parameters": {
+                "ra_listttttt": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "RA positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$ra$",
+                  "id": "galaxy_2-light_model-LensedPS_1-ra_list"
+                },
+                "ra_list": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "RA positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$ra$",
+                  "id": "galaxy_2-light_model-LensedPS_1-ra_list"
+                },
+                "dec_list": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "DEC positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$dec$",
+                  "id": "galaxy_2-light_model-LensedPS_1-dec_list"
+                },
+                "amps": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameterSet",
+                  "documentation": "Set of amplitude values for the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$A$",
+                  "id": "galaxy_2-light_model-LensedPS_1-amps"
+                }
+              },
+              "id": "galaxy_2-light_model-LensedPS_1"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "observation": {
+    "py/object": "coolest.template.classes.observation.Observation",
+    "pixels": {
+      "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+      "fits_file": {
+        "py/object": "coolest.template.classes.fits_file.FitsFile",
+        "path": "test_image.fits"
+      },
+      "documentation": "",
+      "field_of_view_x": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "field_of_view_y": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "num_pix_x": 100,
+      "num_pix_y": 100
+    },
+    "exposure_time": null,
+    "mag_zero_point": null,
+    "mag_sky_brightness": null,
+    "noise": {
+      "py/object": "coolest.template.classes.noise.NoiseMap",
+      "type": "NoiseMapppp",
+      "noise_map": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_image.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -8.0,
+            8.0
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -8.0,
+            8.0
+          ]
+        },
+        "num_pix_x": 200,
+        "num_pix_y": 200
+      },
+      "documentation": "Noise characterized by a noise map, which contains diagonal elements of the data covariance matrix"
+    },
+    "documentation": "Defines the observation itself, that is the image pixels, the exposure time,\n    the noise model and/or properties, the magnitude zero-point and sky brightness."
+  },
+  "instrument": {
+    "py/object": "coolest.template.classes.instrument.Instrument",
+    "name": "some instrument",
+    "band": "F160W",
+    "pixel_size": 0.08,
+    "readout_noise": 4,
+    "psf": {
+      "py/object": "coolest.template.classes.psf.PixelatedPSF",
+      "type": "PixelatedPSFFFF",
+      "description": "a PSF kernel",
+      "pixels": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_psf.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -3.8,
+            3.7
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -3.8,
+            3.7
+          ]
+        },
+        "num_pix_x": 99,
+        "num_pix_y": 99
+      },
+      "documentation": ""
+    },
+    "documentation": "Defines the instrument used for the observation.\n    This includes the name of the telescope and detector, the filter, \n    the pixel size, the readout noise and the point spread function (PSF)."
+  },
+  "cosmology": {
+    "py/object": "coolest.template.classes.cosmology.Cosmology",
+    "H0": 73.0,
+    "Om0": 0.3,
+    "astropy_name": "FlatLambdaCDMMMMM",
+    "documentation": "Defines the cosmological model. \n    Currently, only FlatLambdaCDM from astropy is supported, based on H0 and Omega_m."
+  },
+  "standard": "COOLEST",
+  "meta": {},
+  "exclude_keys": [
+    "documentation",
+    "id",
+    "latex_str",
+    "units",
+    "fixed",
+    "definition_range",
+    "likelihoods",
+    "regularizations",
+    "exclude_keys"
+  ]
+}
diff --git a/test/test_Util/coolest_template_pemd_update_pyAPI.json b/test/test_Util/coolest_template_pemd_update_pyAPI.json
new file mode 100644
index 000000000..e1a77ddcd
--- /dev/null
+++ b/test/test_Util/coolest_template_pemd_update_pyAPI.json
@@ -0,0 +1,1132 @@
+{
+  "py/object": "coolest.template.standard.COOLEST",
+  "mode": "MAP",
+  "coordinates_origin": {
+    "py/object": "coolest.template.classes.coordinates.CoordinatesOrigin",
+    "ra": "00h11m20.244s",
+    "dec": "-08d45m51.48s",
+    "documentation": "Defines the origin of the coordinate system, given in RA-Dec.\n    That origin is then assumed to lie at the center of a data pixel."
+  },
+  "lensing_entities": {
+    "py/object": "coolest.template.classes.lensing_entity_list.LensingEntityList",
+    "documentation": "The list of components that define the lensing system.\n    In COOLEST, a \"lensing entity\" means either a galaxy, or an external shear component.",
+    "py/seq": [
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shearrrrr",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShear",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shear",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShearrrr",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a lens galaxy",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.PEMD",
+              "type": "PEMD",
+              "documentation": "Powerlaw elliptical mass distribution",
+              "parameters": {
+                "theta_E": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.7
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.6575,
+                    "median": 0.65,
+                    "percentile_16th": 0.65,
+                    "percentile_84th": 0.6656000000000001
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-PEMD_0-theta_E"
+                },
+                "gamma": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Mass density slope at Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 1.0,
+                    "max_value": 3.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 2.1
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 1.9,
+                    "median": 1.9,
+                    "percentile_16th": 1.9,
+                    "percentile_84th": 1.9
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma$",
+                  "id": "galaxy_1-mass_model-PEMD_0-gamma"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.7386270436183159
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.8055376443895385,
+                    "median": 0.8055376443895385,
+                    "percentile_16th": 0.8055376443895385,
+                    "percentile_84th": 0.8055376443895385
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-mass_model-PEMD_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 1.907037417145176
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -10.900704743175908,
+                    "median": -10.900704743175908,
+                    "percentile_16th": -10.900704743175908,
+                    "percentile_84th": -10.900704743175908
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-mass_model-PEMD_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.03
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.0275,
+                    "median": -0.03,
+                    "percentile_16th": -0.0248,
+                    "percentile_84th": -0.03
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-mass_model-PEMD_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.01
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.0325,
+                    "median": 0.01,
+                    "percentile_16th": 0.01,
+                    "percentile_84th": 0.0568
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-mass_model-PEMD_0-center_y"
+                }
+              },
+              "id": "galaxy_1-mass_model-PEMD_0"
+            }
+          ]
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersicccc",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 11.0
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 11.0,
+                    "median": 11.0,
+                    "percentile_16th": 11.0,
+                    "percentile_84th": 11.0
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.2
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.2,
+                    "median": 0.2,
+                    "percentile_16th": 0.2,
+                    "percentile_84th": 0.2
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 3.0
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 3.0,
+                    "median": 3.0,
+                    "percentile_16th": 3.0,
+                    "percentile_84th": 3.0
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.7386270436183159
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.7386270436183159,
+                    "median": 0.7386270436183159,
+                    "percentile_16th": 0.7386270436183159,
+                    "percentile_84th": 0.7386270436183159
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 1.907037417145176
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 1.907037417145176,
+                    "median": 1.907037417145176,
+                    "percentile_16th": 1.907037417145176,
+                    "percentile_84th": 1.907037417145176
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.03
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.03,
+                    "median": -0.03,
+                    "percentile_16th": -0.03,
+                    "percentile_84th": -0.03
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.01
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.01,
+                    "median": 0.01,
+                    "percentile_16th": 0.01,
+                    "percentile_84th": 0.01
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a source galaxy",
+        "redshift": 2.0,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 15.0
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 15.0,
+                    "median": 15.0,
+                    "percentile_16th": 15.0,
+                    "percentile_84th": 15.0
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.11
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.11,
+                    "median": 0.11,
+                    "percentile_16th": 0.11,
+                    "percentile_84th": 0.11
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 3.6
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 3.6,
+                    "median": 3.6,
+                    "percentile_16th": 3.6,
+                    "percentile_84th": 3.6
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.6345120047368864
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.6345120047368864,
+                    "median": 0.6345120047368864,
+                    "percentile_16th": 0.6345120047368864,
+                    "percentile_84th": 0.6345120047368864
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_2-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -58.28252558853899
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -58.28252558853899,
+                    "median": -58.28252558853899,
+                    "percentile_16th": -58.28252558853899,
+                    "percentile_84th": -58.28252558853899
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_2-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.02
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.02,
+                    "median": -0.02,
+                    "percentile_16th": -0.02,
+                    "percentile_84th": -0.02
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.03
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.03,
+                    "median": -0.03,
+                    "percentile_16th": -0.03,
+                    "percentile_84th": -0.03
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Sersic_0"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "observation": {
+    "py/object": "coolest.template.classes.observation.Observation",
+    "pixels": {
+      "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+      "fits_file": {
+        "py/object": "coolest.template.classes.fits_file.FitsFile",
+        "path": "test_image.fits"
+      },
+      "documentation": "",
+      "field_of_view_x": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "field_of_view_y": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "num_pix_x": 100,
+      "num_pix_y": 100
+    },
+    "exposure_time": null,
+    "mag_zero_point": null,
+    "mag_sky_brightness": null,
+    "noise": {
+      "py/object": "coolest.template.classes.noise.NoiseMap",
+      "type": "NoiseMap",
+      "noise_map": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_image.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "num_pix_x": 102,
+        "num_pix_y": 102
+      },
+      "documentation": "Noise characterized by a noise map, which contains diagonal elements of the data covariance matrix"
+    },
+    "documentation": "Defines the observation itself, that is the image pixels, the exposure time,\n    the noise model and/or properties, the magnitude zero-point and sky brightness."
+  },
+  "instrument": {
+    "py/object": "coolest.template.classes.instrument.Instrument",
+    "name": "some instrument",
+    "band": "F160W",
+    "pixel_size": 0.08,
+    "readout_noise": 4,
+    "psf": {
+      "py/object": "coolest.template.classes.psf.PixelatedPSF",
+      "type": "PixelatedPSF",
+      "description": "a PSF kernel",
+      "pixels": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_psf.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -3.8,
+            3.7
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -3.8,
+            3.7
+          ]
+        },
+        "num_pix_x": 99,
+        "num_pix_y": 99
+      },
+      "documentation": ""
+    },
+    "documentation": "Defines the instrument used for the observation.\n    This includes the name of the telescope and detector, the filter, \n    the pixel size, the readout noise and the point spread function (PSF)."
+  },
+  "cosmology": {
+    "py/object": "coolest.template.classes.cosmology.Cosmology",
+    "H0": 73.0,
+    "Om0": 0.3,
+    "astropy_name": "FlatLambdaCDM",
+    "documentation": "Defines the cosmological model. \n    Currently, only FlatLambdaCDM from astropy is supported, based on H0 and Omega_m."
+  },
+  "standard": "COOLEST",
+  "meta": {},
+  "exclude_keys": [
+    "documentation",
+    "id",
+    "latex_str",
+    "units",
+    "fixed",
+    "definition_range",
+    "likelihoods",
+    "regularizations",
+    "exclude_keys"
+  ]
+}
\ No newline at end of file
diff --git a/test/test_Util/coolest_template_pyAPI.json b/test/test_Util/coolest_template_pyAPI.json
new file mode 100644
index 000000000..2a7d59152
--- /dev/null
+++ b/test/test_Util/coolest_template_pyAPI.json
@@ -0,0 +1,1349 @@
+{
+  "py/object": "coolest.template.standard.COOLEST",
+  "mode": "MOCK",
+  "coordinates_origin": {
+    "py/object": "coolest.template.classes.coordinates.CoordinatesOrigin",
+    "ra": "00h11m20.244s",
+    "dec": "-08d45m51.48s",
+    "documentation": "Defines the origin of the coordinate system, given in RA-Dec.\n    That origin is then assumed to lie at the center of a data pixel."
+  },
+  "lensing_entities": {
+    "py/object": "coolest.template.classes.lensing_entity_list.LensingEntityList",
+    "documentation": "The list of components that define the lensing system.\n    In COOLEST, a \"lensing entity\" means either a galaxy, or an external shear component.",
+    "py/seq": [
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shear",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShear",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.07
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a lens galaxy",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.SIE",
+              "type": "SIE",
+              "documentation": "Powerlaw elliptical mass distribution",
+              "parameters": {
+                "theta_E": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-SIE_0-theta_E"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-mass_model-SIE_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-mass_model-SIE_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-mass_model-SIE_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-mass_model-SIE_0-center_y"
+                }
+              },
+              "id": "galaxy_1-mass_model-SIE_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.mass.PixelatedRegularGridPotential",
+              "type": "PixelatedRegularGridPotential",
+              "documentation": "A pixelated profile defined on a regular Cartesian grid",
+              "parameters": {
+                "pixels": {
+                  "py/object": "coolest.template.classes.parameter.PixelatedRegularGridParameter",
+                  "documentation": "Pixel values",
+                  "fits_file": {
+                    "py/object": "coolest.template.classes.fits_file.FitsFile",
+                    "path": "test_image.fits"
+                  },
+                  "field_of_view_x": {
+                    "py/tuple": [
+                      -3.0,
+                      1.0
+                    ]
+                  },
+                  "field_of_view_y": {
+                    "py/tuple": [
+                      -2.0,
+                      2.0
+                    ]
+                  },
+                  "num_pix_x": 100,
+                  "num_pix_y": 100
+                }
+              },
+              "id": "galaxy_1-mass_model-PixelatedRegularGridPotential_1"
+            }
+          ]
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_1-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_1-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_1-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.89
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_1-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_1-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_1-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_1-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_1"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a source galaxy",
+        "redshift": 2.0,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.11,
+                    "median": 0.15,
+                    "percentile_16th": 0.03,
+                    "percentile_84th": 0.05
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_2-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_2-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Sersic_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.Shapelets",
+              "type": "Shapelets",
+              "documentation": "Set of shapelet functions",
+              "parameters": {
+                "n_max": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Maximum order of the Shapelet decomposition",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -1,
+                    "max_value": null
+                  },
+                  "fixed": true,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 3
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\n_{\\rm max}$",
+                  "id": "galaxy_2-light_model-Shapelets_1-n_max"
+                },
+                "beta": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelet characteristic scale",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\beta$",
+                  "id": "galaxy_2-light_model-Shapelets_1-beta"
+                },
+                "amps": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Set of amplitude values for each shapelet function",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$A$",
+                  "id": "galaxy_2-light_model-Shapelets-amps"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelets center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Shapelets_1-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelets center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Shapelets_1-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Shapelets_1"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.LensedPS",
+              "type": "LensedPS",
+              "documentation": "Set of lensed point sources",
+              "parameters": {
+                "ra_list": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "RA positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": [-0.1]
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$ra$",
+                  "id": "galaxy_2-light_model-LensedPS_2-ra_list"
+                },
+                "dec_list": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "DEC positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": [0.0015]
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$dec$",
+                  "id": "galaxy_2-light_model-LensedPS_2-dec_list"
+                },
+                "amps": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameterSet",
+                  "documentation": "Set of amplitude values for the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": null
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": null,
+                    "median": null,
+                    "percentile_16th": null,
+                    "percentile_84th": null
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$A$",
+                  "id": "galaxy_2-light_model-LensedPS_2-amps"
+                }
+              },
+              "id": "galaxy_2-light_model-LensedPS_2"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "another source",
+        "redshift": 1.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.PixelatedRegularGrid",
+              "type": "PixelatedRegularGrid",
+              "documentation": "A pixelated profile defined on a regular Cartesian grid",
+              "parameters": {
+                "pixels": {
+                  "py/object": "coolest.template.classes.parameter.PixelatedRegularGridParameter",
+                  "documentation": "Pixel values",
+                  "fits_file": {
+                    "py/object": "coolest.template.classes.fits_file.FitsFile",
+                    "path": "test_image.fits"
+                  },
+                  "field_of_view_x": {
+                    "py/tuple": [
+                      -3.0,
+                      1.0
+                    ]
+                  },
+                  "field_of_view_y": {
+                    "py/tuple": [
+                      -2.0,
+                      2.0
+                    ]
+                  },
+                  "num_pix_x": 100,
+                  "num_pix_y": 100
+                }
+              },
+              "id": "galaxy_3-light_model-PixelatedRegularGrid_0"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "observation": {
+    "py/object": "coolest.template.classes.observation.Observation",
+    "pixels": {
+      "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+      "fits_file": {
+        "py/object": "coolest.template.classes.fits_file.FitsFile",
+        "path": "test_image.fits"
+      },
+      "documentation": "",
+      "field_of_view_x": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "field_of_view_y": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "num_pix_x": 100,
+      "num_pix_y": 100
+    },
+    "exposure_time": null,
+    "mag_zero_point": null,
+    "mag_sky_brightness": null,
+    "noise": {
+      "py/object": "coolest.template.classes.noise.NoiseMap",
+      "type": "NoiseMap",
+      "noise_map": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_image.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "num_pix_x": 100,
+        "num_pix_y": 100
+      },
+      "documentation": "Noise characterized by a noise map, which contains diagonal elements of the data covariance matrix"
+    },
+    "documentation": "Defines the observation itself, that is the image pixels, the exposure time,\n    the noise model and/or properties, the magnitude zero-point and sky brightness."
+  },
+  "instrument": {
+    "py/object": "coolest.template.classes.instrument.Instrument",
+    "name": "some instrument",
+    "band": "F160W",
+    "pixel_size": 0.08,
+    "readout_noise": 4,
+    "psf": {
+      "py/object": "coolest.template.classes.psf.PixelatedPSF",
+      "type": "PixelatedPSF",
+      "description": "a PSF kernel",
+      "pixels": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_psf.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -3.96,
+            3.96
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -3.96,
+            3.96
+          ]
+        },
+        "num_pix_x": 99,
+        "num_pix_y": 99
+      },
+      "documentation": ""
+    },
+    "documentation": "Defines the instrument used for the observation.\n    This includes the name of the telescope and detector, the filter, \n    the pixel size, the readout noise and the point spread function (PSF)."
+  },
+  "cosmology": {
+    "py/object": "coolest.template.classes.cosmology.Cosmology",
+    "H0": 73.0,
+    "Om0": 0.3,
+    "astropy_name": "FlatLambdaCDM",
+    "documentation": "Defines the cosmological model. \n    Currently, only FlatLambdaCDM from astropy is supported, based on H0 and Omega_m."
+  },
+  "standard": "COOLEST",
+  "meta": {},
+  "exclude_keys": [
+    "documentation",
+    "id",
+    "latex_str",
+    "units",
+    "fixed",
+    "definition_range",
+    "likelihoods",
+    "regularizations",
+    "exclude_keys"
+  ]
+}
diff --git a/test/test_Util/coolest_template_update_pyAPI.json b/test/test_Util/coolest_template_update_pyAPI.json
new file mode 100644
index 000000000..75344c8a7
--- /dev/null
+++ b/test/test_Util/coolest_template_update_pyAPI.json
@@ -0,0 +1,1434 @@
+{
+  "py/object": "coolest.template.standard.COOLEST",
+  "mode": "MOCK",
+  "coordinates_origin": {
+    "py/object": "coolest.template.classes.coordinates.CoordinatesOrigin",
+    "ra": "00h11m20.244s",
+    "dec": "-08d45m51.48s",
+    "documentation": "Defines the origin of the coordinate system, given in RA-Dec.\n    That origin is then assumed to lie at the center of a data pixel."
+  },
+  "lensing_entities": {
+    "py/object": "coolest.template.classes.lensing_entity_list.LensingEntityList",
+    "documentation": "The list of components that define the lensing system.\n    In COOLEST, a \"lensing entity\" means either a galaxy, or an external shear component.",
+    "py/seq": [
+      {
+        "py/object": "coolest.template.classes.external_shear.ExternalShear",
+        "type": "external_shear",
+        "name": "my lovely external shear",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.ExternalShear",
+              "type": "ExternalShear",
+              "documentation": "External shear defined with a strength and orientation",
+              "parameters": {
+                "gamma_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Strength of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.08028784240848165
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.0834314686288201,
+                    "median": 0.08360405849427985,
+                    "percentile_16th": 0.08257035022266043,
+                    "percentile_84th": 0.08447758889470816
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\gamma_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-gamma_ext"
+                },
+                "phi_ext": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Orientation of external shear",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -88.6461686344104
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -87.74138975488273,
+                    "median": -87.70905162431639,
+                    "percentile_16th": -88.11359089640823,
+                    "percentile_84th": -87.34816955268884
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi_{\\rm ext}$",
+                  "id": "extshear_model_0-ExternalShear_0-phi_ext"
+                }
+              },
+              "id": "extshear_model_0-ExternalShear_0"
+            }
+          ]
+        },
+        "documentation": ""
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a lens galaxy",
+        "redshift": 0.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.mass.SIE",
+              "type": "SIE",
+              "documentation": "Powerlaw elliptical mass distribution",
+              "parameters": {
+                "theta_E": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Einstein radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.2698550382002391
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.2848512499860762,
+                    "median": 0.28391919074644295,
+                    "percentile_16th": 0.27723525686769923,
+                    "percentile_84th": 0.2912362857991146
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\theta_{\\rm E}$",
+                  "id": "galaxy_1-mass_model-SIE_0-theta_E"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.8456295909157578
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.867778869655172,
+                    "median": 0.8675683101304902,
+                    "percentile_16th": 0.8634646141855524,
+                    "percentile_84th": 0.8723194469220661
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-mass_model-SIE_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 35.86034360188829
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 34.93042094049908,
+                    "median": 34.81129080819228,
+                    "percentile_16th": 34.12942295826212,
+                    "percentile_84th": 35.829885230551945
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-mass_model-SIE_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.14192002413502444
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.14108142386201725,
+                    "median": -0.1417887723375098,
+                    "percentile_16th": -0.13877318648155423,
+                    "percentile_84th": -0.14473350973338134
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-mass_model-SIE_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.012451021050170687
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.0336070783588442,
+                    "median": 0.034764130022613686,
+                    "percentile_16th": 0.025869266857929615,
+                    "percentile_84th": 0.039640196438159206
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-mass_model-SIE_0-center_y"
+                }
+              },
+              "id": "galaxy_1-mass_model-SIE_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.mass.PixelatedRegularGridPotential",
+              "type": "PixelatedRegularGridPotential",
+              "documentation": "A pixelated profile defined on a regular Cartesian grid",
+              "parameters": {
+                "pixels": {
+                  "py/object": "coolest.template.classes.parameter.PixelatedRegularGridParameter",
+                  "documentation": "Pixel values",
+                  "fits_file": {
+                    "py/object": "coolest.template.classes.fits_file.FitsFile",
+                    "path": "test_image.fits"
+                  },
+                  "field_of_view_x": {
+                    "py/tuple": [
+                      -3.0,
+                      1.0
+                    ]
+                  },
+                  "field_of_view_y": {
+                    "py/tuple": [
+                      -2.0,
+                      2.0
+                    ]
+                  },
+                  "num_pix_x": 100,
+                  "num_pix_y": 100
+                }
+              },
+              "id": "galaxy_1-mass_model-PixelatedRegularGridPotential_1"
+            }
+          ]
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.043032320820713524
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.0608861312764143,
+                    "median": 0.06373564730711578,
+                    "percentile_16th": 0.0525717489105345,
+                    "percentile_84th": 0.07058833814316429
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 1.4056738052248503
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 1.3653901628398548,
+                    "median": 1.3633294155209843,
+                    "percentile_16th": 1.3458475813339812,
+                    "percentile_84th": 1.3829220342417692
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 6.3846194078019005
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 6.331472336677272,
+                    "median": 6.325768904388582,
+                    "percentile_16th": 6.309999901552871,
+                    "percentile_84th": 6.355624456773335
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.805177916032711
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.8149072395475689,
+                    "median": 0.8143875399726024,
+                    "percentile_16th": 0.8121094982157592,
+                    "percentile_84th": 0.8176425870562799
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 75.30463313467948
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 74.62485231606139,
+                    "median": 74.61747234160111,
+                    "percentile_16th": 74.07556764956524,
+                    "percentile_84th": 75.14802889376142
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.2289476873695148
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.20868566308330602,
+                    "median": 0.20778149980106572,
+                    "percentile_16th": 0.21364466351740044,
+                    "percentile_84th": 0.20382009162162373
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -1.3371361055417084
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -1.3715206235004056,
+                    "median": -1.3722856802348302,
+                    "percentile_16th": -1.3780834382218279,
+                    "percentile_84th": -1.3656885504515224
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 4.927822562542158
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 5.101023481557856,
+                    "median": 5.078020130568445,
+                    "percentile_16th": 4.758982014560028,
+                    "percentile_84th": 5.3919649808423324
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_1-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.8300723176394926
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.6722740252439011,
+                    "median": 0.6673429038883207,
+                    "percentile_16th": 0.6521483150364589,
+                    "percentile_84th": 0.694747535748473
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_1-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 4.396481133588683
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 4.529856843342577,
+                    "median": 4.532358513493419,
+                    "percentile_16th": 4.513819555840464,
+                    "percentile_84th": 4.550226926374958
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_1-light_model-Sersic_1-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.8456295909157578
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.867778869655172,
+                    "median": 0.8675683101304902,
+                    "percentile_16th": 0.8634646141855524,
+                    "percentile_84th": 0.8723194469220661
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_1-light_model-Sersic_1-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 35.86034360188829
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 34.93042094049908,
+                    "median": 34.81129080819228,
+                    "percentile_16th": 34.12942295826212,
+                    "percentile_84th": 35.829885230551945
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_1-light_model-Sersic_1-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.14192002413502444
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.14108142386201725,
+                    "median": -0.1417887723375098,
+                    "percentile_16th": -0.13877318648155423,
+                    "percentile_84th": -0.14473350973338134
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_1-light_model-Sersic_1-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.012451021050170687
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.0336070783588442,
+                    "median": 0.034764130022613686,
+                    "percentile_16th": 0.025869266857929615,
+                    "percentile_84th": 0.039640196438159206
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_1-light_model-Sersic_1-center_y"
+                }
+              },
+              "id": "galaxy_1-light_model-Sersic_1"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "a source galaxy",
+        "redshift": 2.0,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.Sersic",
+              "type": "Sersic",
+              "documentation": "Elliptical Sersic",
+              "parameters": {
+                "I_eff": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Amplitude at the Sersic radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 5.416863154114772
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 4.394688074589356,
+                    "median": 4.459869031376075,
+                    "percentile_16th": 4.005331642838988,
+                    "percentile_84th": 4.773793305145151
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$I_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-I_eff"
+                },
+                "theta_eff": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic (half-light) radius",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.3640249282548136
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.4660548399594208,
+                    "median": 0.46578502946298783,
+                    "percentile_16th": 0.4505897410052205,
+                    "percentile_84th": 0.48481836286629654
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$R_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-theta_eff"
+                },
+                "n": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Sersic index",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.5,
+                    "max_value": 10.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 4.396774282267133
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 4.386012064519258,
+                    "median": 4.386145401420407,
+                    "percentile_16th": 4.371488891989945,
+                    "percentile_84th": 4.401903273354358
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$n_{\\rm Sersic}$",
+                  "id": "galaxy_2-light_model-Sersic_0-n"
+                },
+                "q": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Axis ratio, semi-minor axis / semi-major axis",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": 1.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.8031289693097592
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.8047540447511803,
+                    "median": 0.803908138631314,
+                    "percentile_16th": 0.8008270849522741,
+                    "percentile_84th": 0.8089010199822535
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$q$",
+                  "id": "galaxy_2-light_model-Sersic_0-q"
+                },
+                "phi": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Position angle",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -90.0,
+                    "max_value": 90.0
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 57.66472491375059
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 57.49967705679063,
+                    "median": 57.42618922675578,
+                    "percentile_16th": 57.032675826908395,
+                    "percentile_84th": 57.94911782908431
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\phi$",
+                  "id": "galaxy_2-light_model-Sersic_0-phi"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.1768323470448125
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.16122753959112418,
+                    "median": -0.16021984738423553,
+                    "percentile_16th": -0.157291581491518,
+                    "percentile_84th": -0.1661538519644343
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Profile center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.38821361884269345
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.3728880221693646,
+                    "median": 0.3736937544404638,
+                    "percentile_16th": 0.3690298145480581,
+                    "percentile_84th": 0.37771047240572
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Sersic_0-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Sersic_0"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.Shapelets",
+              "type": "Shapelets",
+              "documentation": "Set of shapelet functions",
+              "parameters": {
+                "n_max": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Maximum order of the Shapelet decomposition",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": -1,
+                    "max_value": null
+                  },
+                  "fixed": true,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 3
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 3.0,
+                    "median": 3.0,
+                    "percentile_16th": 3.0,
+                    "percentile_84th": 3.0
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\n_{\\rm max}$",
+                  "id": "galaxy_2-light_model-Shapelets_1-n_max"
+                },
+                "beta": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelet characteristic scale",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.2343713567761273
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.2326051826420254,
+                    "median": 0.23251038415982542,
+                    "percentile_16th": 0.22901648563771657,
+                    "percentile_84th": 0.23675609569514788
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$\\beta$",
+                  "id": "galaxy_2-light_model-Shapelets_1-beta"
+                },
+                "amps": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameter",
+                  "documentation": "Set of amplitude values for each shapelet function",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": [
+                      103.02872390855208,
+                      24.072032514468916,
+                      -6.531676868079238,
+                      26.17850850498505,
+                      1.352441118264391,
+                      38.27489245560879,
+                      -35.68274064024738,
+                      -0.7192522188334625,
+                      0.2821377903166482,
+                      9.272189557118068
+                    ]
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": [
+                      107.60842187671525,
+                      16.062093145623404,
+                      -12.236190656279433,
+                      19.605019062497714,
+                      1.7751176135487357,
+                      34.393546447087374,
+                      -34.07775756045019,
+                      -1.6643964488192187,
+                      -2.6248198359028305,
+                      2.977551977425948
+                    ],
+                    "median": [
+                      107.90011415408006,
+                      16.11385553594195,
+                      -12.578730033516313,
+                      19.270965947087554,
+                      1.9669876055504063,
+                      33.664663624225255,
+                      -34.11053630385361,
+                      -1.5551887382022846,
+                      -2.4799805745426884,
+                      2.4403645168469157
+                    ],
+                    "percentile_16th": [
+                      104.77808219392992,
+                      18.801747843074164,
+                      -13.967120550388742,
+                      17.97354026117791,
+                      3.010220798763585,
+                      31.64932649576819,
+                      -33.27220763487294,
+                      -2.8417561506377975,
+                      -0.9748910376459043,
+                      0.7268355721384798
+                    ],
+                    "percentile_84th": [
+                      110.52000737903913,
+                      13.088886958004093,
+                      -10.63249290274204,
+                      21.171614280958526,
+                      0.7410444586763394,
+                      36.79191436796804,
+                      -34.891567419239045,
+                      -0.4780764242642298,
+                      -4.231732413401309,
+                      4.7964143672102235
+                    ]
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$A$",
+                  "id": "galaxy_2-light_model-Shapelets-amps"
+                },
+                "center_x": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelets center along x coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": 0.007847550026012905
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": 0.027844576750958815,
+                    "median": 0.0289335849883565,
+                    "percentile_16th": 0.03339681708459946,
+                    "percentile_84th": 0.021996618862619277
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$x_0$",
+                  "id": "galaxy_2-light_model-Shapelets_1-center_x"
+                },
+                "center_y": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameter",
+                  "documentation": "Shapelets center along y coordinates",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": -0.07103672300452858
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": -0.05878851129661986,
+                    "median": -0.0591168234995821,
+                    "percentile_16th": -0.06408194133974537,
+                    "percentile_84th": -0.052970167845146084
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$y_0$",
+                  "id": "galaxy_2-light_model-Shapelets_1-center_y"
+                }
+              },
+              "id": "galaxy_2-light_model-Shapelets_1"
+            },
+            {
+              "py/object": "coolest.template.classes.profiles.light.LensedPS",
+              "type": "LensedPS",
+              "documentation": "Set of lensed point sources",
+              "parameters": {
+                "ra_list": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "RA positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": [
+                      0.7709158326830448
+                    ]
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": [
+                      0.7065125215878153
+                    ],
+                    "median": [
+                      0.7045051147003569
+                    ],
+                    "percentile_16th": [
+                      0.7172075255874433
+                    ],
+                    "percentile_84th": [
+                      0.6947856437736474
+                    ]
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$ra$",
+                  "id": "galaxy_2-light_model-LensedPS_2-ra_list"
+                },
+                "dec_list": {
+                  "py/object": "coolest.template.classes.parameter.NonLinearParameterSet",
+                  "documentation": "DEC positions of the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": null,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": [
+                      -0.6976306953932367
+                    ]
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": [
+                      -0.6997585006607466
+                    ],
+                    "median": [
+                      -0.6988734896832836
+                    ],
+                    "percentile_16th": [
+                      -0.7080832955985319
+                    ],
+                    "percentile_84th": [
+                      -0.6896635925586312
+                    ]
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$dec$",
+                  "id": "galaxy_2-light_model-LensedPS_2-dec_list"
+                },
+                "amps": {
+                  "py/object": "coolest.template.classes.parameter.LinearParameterSet",
+                  "documentation": "Set of amplitude values for the lensed point sources",
+                  "units": null,
+                  "definition_range": {
+                    "py/object": "coolest.template.classes.parameter.DefinitionRange",
+                    "min_value": 0.0,
+                    "max_value": null
+                  },
+                  "fixed": false,
+                  "point_estimate": {
+                    "py/object": "coolest.template.classes.parameter.PointEstimate",
+                    "value": [
+                      0.028828994280635834
+                    ]
+                  },
+                  "posterior_stats": {
+                    "py/object": "coolest.template.classes.probabilities.PosteriorStatistics",
+                    "mean": [
+                      0.01880928403392135
+                    ],
+                    "median": [
+                      0.017135052262383463
+                    ],
+                    "percentile_16th": [
+                      0.005968817434649664
+                    ],
+                    "percentile_84th": [
+                      0.032212972598745046
+                    ]
+                  },
+                  "prior": {
+                    "py/object": "coolest.template.classes.probabilities.Prior",
+                    "type": null,
+                    "documentation": ""
+                  },
+                  "latex_str": "$A$",
+                  "id": "galaxy_2-light_model-LensedPS_2-amps"
+                }
+              },
+              "id": "galaxy_2-light_model-LensedPS_2"
+            }
+          ]
+        }
+      },
+      {
+        "py/object": "coolest.template.classes.galaxy.Galaxy",
+        "type": "galaxy",
+        "name": "another source",
+        "redshift": 1.5,
+        "mass_model": {
+          "py/object": "coolest.template.classes.mass_light_model.MassModel",
+          "py/seq": []
+        },
+        "documentation": "",
+        "light_model": {
+          "py/object": "coolest.template.classes.mass_light_model.LightModel",
+          "py/seq": [
+            {
+              "py/object": "coolest.template.classes.profiles.light.PixelatedRegularGrid",
+              "type": "PixelatedRegularGrid",
+              "documentation": "A pixelated profile defined on a regular Cartesian grid",
+              "parameters": {
+                "pixels": {
+                  "py/object": "coolest.template.classes.parameter.PixelatedRegularGridParameter",
+                  "documentation": "Pixel values",
+                  "fits_file": {
+                    "py/object": "coolest.template.classes.fits_file.FitsFile",
+                    "path": "test_image.fits"
+                  },
+                  "field_of_view_x": {
+                    "py/tuple": [
+                      -3.0,
+                      1.0
+                    ]
+                  },
+                  "field_of_view_y": {
+                    "py/tuple": [
+                      -2.0,
+                      2.0
+                    ]
+                  },
+                  "num_pix_x": 100,
+                  "num_pix_y": 100
+                }
+              },
+              "id": "galaxy_3-light_model-PixelatedRegularGrid_0"
+            }
+          ]
+        }
+      }
+    ]
+  },
+  "observation": {
+    "py/object": "coolest.template.classes.observation.Observation",
+    "pixels": {
+      "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+      "fits_file": {
+        "py/object": "coolest.template.classes.fits_file.FitsFile",
+        "path": "test_image.fits"
+      },
+      "documentation": "",
+      "field_of_view_x": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "field_of_view_y": {
+        "py/tuple": [
+          -4.0,
+          4.0
+        ]
+      },
+      "num_pix_x": 100,
+      "num_pix_y": 100
+    },
+    "exposure_time": null,
+    "mag_zero_point": null,
+    "mag_sky_brightness": null,
+    "noise": {
+      "py/object": "coolest.template.classes.noise.NoiseMap",
+      "type": "NoiseMap",
+      "noise_map": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_image.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -4.0,
+            4.0
+          ]
+        },
+        "num_pix_x": 100,
+        "num_pix_y": 100
+      },
+      "documentation": "Noise characterized by a noise map, which contains diagonal elements of the data covariance matrix"
+    },
+    "documentation": "Defines the observation itself, that is the image pixels, the exposure time,\n    the noise model and/or properties, the magnitude zero-point and sky brightness."
+  },
+  "instrument": {
+    "py/object": "coolest.template.classes.instrument.Instrument",
+    "name": "some instrument",
+    "band": "F160W",
+    "pixel_size": 0.08,
+    "readout_noise": 4,
+    "psf": {
+      "py/object": "coolest.template.classes.psf.PixelatedPSF",
+      "type": "PixelatedPSF",
+      "description": "a PSF kernel",
+      "pixels": {
+        "py/object": "coolest.template.classes.grid.PixelatedRegularGrid",
+        "fits_file": {
+          "py/object": "coolest.template.classes.fits_file.FitsFile",
+          "path": "test_psf.fits"
+        },
+        "documentation": "",
+        "field_of_view_x": {
+          "py/tuple": [
+            -3.96,
+            3.96
+          ]
+        },
+        "field_of_view_y": {
+          "py/tuple": [
+            -3.96,
+            3.96
+          ]
+        },
+        "num_pix_x": 99,
+        "num_pix_y": 99
+      },
+      "documentation": ""
+    },
+    "documentation": "Defines the instrument used for the observation.\n    This includes the name of the telescope and detector, the filter, \n    the pixel size, the readout noise and the point spread function (PSF)."
+  },
+  "cosmology": {
+    "py/object": "coolest.template.classes.cosmology.Cosmology",
+    "H0": 73.0,
+    "Om0": 0.3,
+    "astropy_name": "FlatLambdaCDM",
+    "documentation": "Defines the cosmological model. \n    Currently, only FlatLambdaCDM from astropy is supported, based on H0 and Omega_m."
+  },
+  "standard": "COOLEST",
+  "meta": {},
+  "exclude_keys": [
+    "documentation",
+    "id",
+    "latex_str",
+    "units",
+    "fixed",
+    "definition_range",
+    "likelihoods",
+    "regularizations",
+    "exclude_keys"
+  ]
+}
\ No newline at end of file
diff --git a/test/test_Util/test_coolest.py b/test/test_Util/test_coolest.py
new file mode 100644
index 000000000..667bc23a6
--- /dev/null
+++ b/test/test_Util/test_coolest.py
@@ -0,0 +1,239 @@
+import pytest
+import numpy.testing as npt
+import numpy as np
+import unittest
+import os
+
+from lenstronomy.Util.coolest_interface import create_lenstronomy_from_coolest,update_coolest_from_lenstronomy,create_kwargs_mcmc_from_chain_list
+from lenstronomy.Util.coolest_read_util import degree_coolest_to_radian_lenstronomy,ellibounds_coolest_to_lenstronomy, shearbounds_coolest_to_lenstronomy
+from lenstronomy.Util.coolest_update_util import shapelet_amp_lenstronomy_to_coolest, folding_coolest
+
+from lenstronomy.LensModel.lens_model import LensModel
+from lenstronomy.LightModel.light_model import LightModel
+from lenstronomy.Data.imaging_data import ImageData
+from lenstronomy.Data.psf import PSF
+import lenstronomy.Util.image_util as image_util
+from lenstronomy.ImSim.image_model import ImageModel
+from lenstronomy.Plots.model_plot import ModelPlot
+
+from lenstronomy.Workflow.fitting_sequence import FittingSequence
+
+
+class TestCOOLESTinterface(object):
+
+    def test_load(self):
+        path = os.getcwd()
+        if path[-11:] == 'lenstronomy':
+            path+='/test/test_Util'
+        kwargs_out = create_lenstronomy_from_coolest(path+"/coolest_template")
+        print(kwargs_out)
+        kwargs_out = create_lenstronomy_from_coolest(path + "/coolest_template_pemd")
+        kwargs_out = create_lenstronomy_from_coolest(path + "/coolest_template_pemd_random")
+        return
+
+    def test_update(self):
+        path = os.getcwd()
+        if path[-11:] == 'lenstronomy':
+            path+='/test/test_Util'
+        kwargs_result={"kwargs_lens":[{'gamma1':0.1,'gamma2':-0.05,'ra_0':0.,'dec_0':0.},
+                                      {'theta_E': 0.7, 'e1': -0.15, 'e2': 0.01,
+                                       'center_x': 0.03, 'center_y': 0.01}],
+                       "kwargs_source":[{'amp':15.,'R_sersic':0.11,'n_sersic':3.6,'center_x':0.02,
+                                         'center_y':-0.03,'e1':0.1,'e2':-0.2},
+                                        {'amp': np.array([70., 33., 2.1, 3.9, 15., -16., 2.8, -1.7, -4.1, 0.2]),
+                                         'n_max': 3, 'beta': 0.1, 'center_x': 0.1, 'center_y': 0.0}],
+                       "kwargs_lens_light":[{'amp':11.,'R_sersic':0.2,'n_sersic':3.,'center_x':0.03,
+                                             'center_y':0.01,'e1':-0.15,'e2':0.01},
+                                            {'amp':12.,'R_sersic':0.02,'n_sersic':6.,'center_x':0.03,
+                                             'center_y':0.01,'e1':0.,'e2':-0.15}],
+                       "kwargs_ps":[{'point_amp':np.array([0.1]),'ra_image':np.array([0.25]), 'dec_image':np.array([0.2])}]}
+        update_coolest_from_lenstronomy(path+"/coolest_template",kwargs_result,ending="_update")
+        kwargs_out = create_lenstronomy_from_coolest(path+"/coolest_template_update")
+        npt.assert_almost_equal(kwargs_out['kwargs_params']['lens_model'][0][1]['e1'],
+                                kwargs_result['kwargs_lens'][1]['e1'], decimal=4)
+        npt.assert_almost_equal(kwargs_out['kwargs_params']['lens_model'][0][1]['e2'],
+                                kwargs_result['kwargs_lens'][1]['e2'], decimal=4)
+
+        return
+
+    def test_full(self):
+        # use read json ; create an image ; create noise ; do fit (PSO for result + MCMC for chain)
+        # create the kwargs mcmc ; upadte json
+        path = os.getcwd()
+        if path[-11:] == 'lenstronomy':
+            path+='/test/test_Util'
+
+        kwargs_out = create_lenstronomy_from_coolest(path+"/coolest_template")
+
+        # IMAGE specifics
+        background_rms = .005  # background noise per pixel
+        exp_time = 500.  # exposure time (arbitrary units, flux per pixel is in units #photons/exp_time unit)
+        # PSF : easier for test to create a gaussian PSF
+        fwhm = 0.05  # full width at half maximum of PSF
+        psf_type = 'GAUSSIAN'  # 'GAUSSIAN', 'PIXEL', 'NONE'
+
+        # lensing quantities to create an image
+        lens_model_list = kwargs_out['kwargs_model']['lens_model_list']
+        kwargs_sie = {'theta_E': .66, 'center_x': 0.05, 'center_y': 0, 'e1': -0.1,
+                        'e2': 0.1}  # parameters of the deflector lens model
+        kwargs_shear = {'gamma1': 0.0, 'gamma2': -0.05}  # shear values to the source plane
+        kwargs_lens = [kwargs_shear, kwargs_sie]
+        lens_model_class = LensModel(lens_model_list)
+
+        # Sersic parameters in the initial simulation for the source
+        source_model_list = kwargs_out['kwargs_model']['source_light_model_list']
+        kwargs_sersic = {'amp': 16, 'R_sersic': 0.1, 'n_sersic': 3.5, 'e1': -0.1, 'e2': 0.1,
+                         'center_x': 0.1, 'center_y': 0}
+        kwargs_shapelets = {'amp': np.array([ 70.,  33.,   2.1,   3.9 ,  15., -16.,   2.8,  -1.7, -4.1, 0.2]),
+                            'n_max': 3, 'beta': 0.1, 'center_x': 0.1, 'center_y': 0.0}
+        kwargs_source = [kwargs_sersic, kwargs_shapelets]
+        source_model_class = LightModel(source_model_list)
+
+        # Sersic parameters in the initial simulation for the lens light
+        lens_light_model_list = kwargs_out['kwargs_model']['lens_light_model_list']
+        kwargs_sersic_lens1 = {'amp': 16, 'R_sersic': 0.6, 'n_sersic': 2.5, 'e1': -0.1, 'e2': 0.1, 'center_x': 0.05,
+                              'center_y': 0}
+        kwargs_sersic_lens2 = {'amp': 3, 'R_sersic': 0.7, 'n_sersic': 3, 'e1': -0.1, 'e2': 0.1, 'center_x': 0.05,
+                               'center_y': 0}
+        kwargs_lens_light = [kwargs_sersic_lens1,kwargs_sersic_lens2]
+        lens_light_model_class = LightModel(lens_light_model_list)
+
+        numPix = 100
+        kwargs_out['kwargs_data']['background_rms']=background_rms
+        kwargs_out['kwargs_data']['exposure_time'] = exp_time
+        kwargs_out['kwargs_data']['image_data'] = np.zeros((numPix,numPix))
+        kwargs_out['kwargs_data'].pop('noise_map')
+
+        data_class = ImageData(**kwargs_out['kwargs_data'])
+        #PSF
+        pixel_scale = kwargs_out['kwargs_data']['transform_pix2angle'][1][1] / kwargs_out['kwargs_psf']['point_source_supersampling_factor']
+        kwargs_psf = {'psf_type': 'GAUSSIAN', 'fwhm': fwhm, 'pixel_size': pixel_scale, 'truncation': 3}
+        kwargs_out['kwargs_psf'] = kwargs_psf
+        psf_class = PSF(**kwargs_out['kwargs_psf'])
+
+        kwargs_numerics = {'supersampling_factor': 1, 'supersampling_convolution': False}
+
+
+        imageModel = ImageModel(data_class, psf_class, lens_model_class=lens_model_class,
+                                source_model_class = source_model_class, lens_light_model_class = lens_light_model_class,
+                                kwargs_numerics = kwargs_numerics)
+
+        # generate image
+        image_model = imageModel.image(kwargs_lens, kwargs_source, kwargs_lens_light=kwargs_lens_light, kwargs_ps=None)
+
+        poisson = image_util.add_poisson(image_model, exp_time=exp_time)
+        bkg = image_util.add_background(image_model, sigma_bkd=background_rms)
+        image_real = image_model + poisson + bkg
+
+        data_class.update_data(image_real)
+        kwargs_out['kwargs_data']['image_data'] = image_real
+
+        # MODELING
+        # Notes :
+        # All the lines above were meant to create a mock image
+        # The following is basically the only lines of code you will need
+        # (after running the "create_lenstronomy_from_coolest" function) when you actually do the
+        # modeling on a pre-existing image (with associated noise and psf proveded)
+        band_list = [kwargs_out['kwargs_data'], kwargs_out['kwargs_psf'], kwargs_numerics]
+        multi_band_list = [band_list]
+        kwargs_data_joint = {'multi_band_list': multi_band_list, 'multi_band_type': 'single-band'}
+        kwargs_constraints = {'joint_lens_with_light':[[0,1,['center_x','center_y','e1','e2']],
+                                                       [1,1,['center_x','center_y','e1','e2']]]}
+        kwargs_likelihood = {'check_bounds':True, 'check_positive_flux': True}
+
+        fitting_seq = FittingSequence(kwargs_data_joint, kwargs_out['kwargs_model'],
+                                      kwargs_constraints, kwargs_likelihood,
+                                      kwargs_out['kwargs_params'])
+
+        n_particules = 200
+        n_iterations = 10
+        wr = 5
+        n_run_mcmc = 10
+        n_burn_mcmc = 10
+        fitting_kwargs_list = [['PSO', {'sigma_scale': 1., 'n_particles': n_particules, 'n_iterations': n_iterations}],
+                               ['MCMC', {'n_burn': n_burn_mcmc, 'n_run': n_run_mcmc, 'walkerRatio': wr,
+                                         'sigma_scale': 0.01}]
+                               ]
+        chain_list = fitting_seq.fit_sequence(fitting_kwargs_list)
+        kwargs_result = fitting_seq.best_fit()
+
+        modelPlot = ModelPlot(kwargs_data_joint['multi_band_list'], kwargs_out['kwargs_model'], kwargs_result)
+        modelPlot._imageModel.image_linear_solve(inv_bool=True, **kwargs_result)
+        #the last 2 lines are meant for solving the linear parameters
+
+        # use the function to save mcmc chains in userfriendly mode
+        # kwargs_mcmc = create_kwargs_mcmc_from_chain_list(chain_list,kwargs_out['kwargs_model'],kwargs_out['kwargs_params'],
+        #                                    kwargs_out['kwargs_data'],kwargs_out['kwargs_psf'],kwargs_numerics,
+        #                                    kwargs_constraints,idx_chain=1)
+
+        kwargs_mcmc = create_kwargs_mcmc_from_chain_list(chain_list, kwargs_out['kwargs_model'],
+                                                         kwargs_out['kwargs_params'],
+                                                         kwargs_out['kwargs_data'], kwargs_out['kwargs_psf'],
+                                                         kwargs_numerics,
+                                                         kwargs_constraints, idx_chain=1, likelihood_threshold= -100000)
+        # save the results (aka update the COOLEST json)
+        update_coolest_from_lenstronomy(path+"/coolest_template",kwargs_result, kwargs_mcmc)
+
+        return
+
+    def test_pemd(self):
+        path = os.getcwd()
+        if path[-11:] == 'lenstronomy':
+            path+='/test/test_Util'
+        kwargs_out = create_lenstronomy_from_coolest(path+"/coolest_template_pemd")
+        print(kwargs_out)
+
+        # kwargs_results to update the COOLEST template
+        kwargs_result={"kwargs_lens":[{'theta_E': 0.7, 'e1': -0.15, 'e2': 0.01, 'gamma': 2.1,
+                                       'center_x': 0.03, 'center_y': 0.01}],
+                       "kwargs_source":[{'amp':15.,'R_sersic':0.11,'n_sersic':3.6,'center_x':0.02,
+                                         'center_y':-0.03,'e1':0.1,'e2':-0.2}],
+                       "kwargs_lens_light":[{'amp':11.,'R_sersic':0.2,'n_sersic':3.,'center_x':0.03,
+                                             'center_y':0.01,'e1':-0.15,'e2':0.01}]}
+        # kwargs_mcmc to update the COOLEST template. In real cases, this list would be much bigger
+        # as each element is a result from a given point at a given iteration of a MCMC chain
+
+        kwargs_mcmc={"args_lens":[[{'theta_E': 0.68, 'e1': -0.10, 'e2': -0.04, 'gamma': 1.9,
+                                    'center_x': 0.02, 'center_y': 0.10}],[{'theta_E': 0.65, 'e1': -0.10, 'e2': -0.04, 'gamma': 1.9,
+                                    'center_x': 0.03, 'center_y': 0.01}],[{'theta_E': 0.65, 'e1': -0.10, 'e2': -0.04, 'gamma': 1.9,
+                                    'center_x': 0.03, 'center_y': 0.01}],[{'theta_E': 0.65, 'e1': -0.10, 'e2': -0.04, 'gamma': 1.9,
+                                    'center_x': 0.03, 'center_y': 0.01}]],
+                       "args_source":[[{'amp':15.,'R_sersic':0.11,'n_sersic':3.6,'center_x':0.02,
+                                        'center_y':-0.03,'e1':0.1,'e2':-0.2}],[{'amp':15.,'R_sersic':0.11,'n_sersic':3.6,'center_x':0.02,
+                                        'center_y':-0.03,'e1':0.1,'e2':-0.2}],[{'amp':15.,'R_sersic':0.11,'n_sersic':3.6,'center_x':0.02,
+                                        'center_y':-0.03,'e1':0.1,'e2':-0.2}],[{'amp':15.,'R_sersic':0.11,'n_sersic':3.6,'center_x':0.02,
+                                        'center_y':-0.03,'e1':0.1,'e2':-0.2}]],
+                       "args_lens_light":[[{'amp':11.,'R_sersic':0.2,'n_sersic':3.,'center_x':0.03,
+                                            'center_y':0.01,'e1':-0.15,'e2':0.01}],[{'amp':11.,'R_sersic':0.2,'n_sersic':3.,'center_x':0.03,
+                                            'center_y':0.01,'e1':-0.15,'e2':0.01}],[{'amp':11.,'R_sersic':0.2,'n_sersic':3.,'center_x':0.03,
+                                            'center_y':0.01,'e1':-0.15,'e2':0.01}],[{'amp':11.,'R_sersic':0.2,'n_sersic':3.,'center_x':0.03,
+                                            'center_y':0.01,'e1':-0.15,'e2':0.01}]]}
+        update_coolest_from_lenstronomy(path+"/coolest_template_pemd",kwargs_result,ending="_update",
+                                        kwargs_mcmc=kwargs_mcmc)
+        kwargs_out = create_lenstronomy_from_coolest(path+"/coolest_template_pemd_update")
+        print(kwargs_out)
+        npt.assert_almost_equal(kwargs_out['kwargs_params']['lens_model'][0][0]['e1'],
+                                kwargs_result['kwargs_lens'][0]['e1'], decimal=4)
+        npt.assert_almost_equal(kwargs_out['kwargs_params']['lens_model'][0][0]['e2'],
+                                kwargs_result['kwargs_lens'][0]['e2'], decimal=4)
+
+        return
+    def test_util_functions(self):
+        radian = degree_coolest_to_radian_lenstronomy(None)
+        radian = degree_coolest_to_radian_lenstronomy(-120.)
+        npt.assert_almost_equal(radian,np.pi/6., decimal=4)
+
+        radian = degree_coolest_to_radian_lenstronomy(120.)
+        npt.assert_almost_equal(radian,5*np.pi/6., decimal=4)
+
+        ellibounds_coolest_to_lenstronomy(0.6,1.0,None,None)
+        shearbounds_coolest_to_lenstronomy(0.0, 0.1, -90., None)
+
+        shapelet_amp_lenstronomy_to_coolest(None)
+        folding_coolest(np.array([-95.,95.]))
+        folding_coolest(-95.)
+        folding_coolest(95.)
+
+
+        return
+
diff --git a/test_requirements.txt b/test_requirements.txt
index 865114e4b..1976a3d23 100644
--- a/test_requirements.txt
+++ b/test_requirements.txt
@@ -1,2 +1,3 @@
 # requirements for tests
-colossus
\ No newline at end of file
+colossus
+git+https://github.com/aymgal/coolest@main#egg=coolest
\ No newline at end of file