Repository: LSSTDESC/sacc (language: python)

Issue:
Implementation for cluster counts, mass and WL profile
This issue is to implement in this repo new tracers and datatypes to be used for cluster abundance and cluster WL analysis. The proposed implementation currently lives in FireCrown ([module](https://github.com/LSSTDESC/firecrown/blob/issue/276/CL_shearprofile_sacc/firecrown/sacc_support.py), [notebook](https://github.com/LSSTDESC/firecrown/blob/issue/276/CL_shearprofile_sacc/examples/cluster_number_counts/demo_sacc_for_clusters.ipynb))  and should be ported in the SACC repo once agreed upon.


Discussion / hints:
This superseeds #82.


Fix patch:
diff --git a/examples/Create_cluster_count_SACC.ipynb b/examples/Create_cluster_count_SACC.ipynb
deleted file mode 100644
index efb5bfd..0000000
--- a/examples/Create_cluster_count_SACC.ipynb
+++ /dev/null
@@ -1,523 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Cluster count-only SACC file creation\n",
-    "\n",
-    "This notebook examplifies the creation of a SACC file for cluster count, using pre-extracted halo catalog from the SkySim dataset. It\n",
-    "- shows how to load the data\n",
-    "- counts halos in the richness and redshift plane\n",
-    "- computes the covariance using jackknife resampling (to be eventually done by TJPCov)\n",
-    "- **save the count and covariance in the SACC format**"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import os\n",
-    "import matplotlib.pyplot as plt\n",
-    "import pandas as pd\n",
-    "import numpy as np\n",
-    "import pyccl as ccl\n",
-    "import sacc"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'0.4.9'"
-      ]
-     },
-     "execution_count": 2,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "sacc.__version__"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Load halo list\n",
-    "This is reading a file prepared from the SkySim dataset and stored at CC-in2p3 or NERSC (uncomment as needed)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "There are 249485 halos with Mfof > 1e13 Msun and z=[0,1.2] in 439.78986 deg2\n"
-     ]
-    }
-   ],
-   "source": [
-    "path_to_cat = '/sps/lsst/users/ccombet/SkySim5000/hdf5' # CC-in2p3\n",
-    "# path_to_cat = '/global/u1/c/ccombet/SkySim5000/hdf5' # NERSC\n",
-    "\n",
-    "size = 'image' # other options are 'image' (440 deg2) and 'small' (57 deg2), 'full' (5000 deg2)\n",
-    "\n",
-    "with pd.HDFStore(os.path.join(path_to_cat,f'skysim_halos_z=0-1.20_mfof_gt_1.00e+13_{size}.hdf5')) as store:\n",
-    "    dm_halos = store['skysim']\n",
-    "    metadata = store.get_storer('skysim').attrs.metadata\n",
-    "\n",
-    "cosmo_ss  = metadata['cosmo']\n",
-    "sky_area = metadata['sky_area']\n",
-    "N_cl = len(dm_halos['halo_mass'])\n",
-    "print(f'There are {N_cl} halos with Mfof > 1e13 Msun and z=[0,1.2] in {sky_area} deg2')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Count halos in the $N_{\\rm richness} \\times N_z$ richness-redshift plane"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<matplotlib.colorbar.Colorbar at 0x7eff5d6bad90>"
-      ]
-     },
-     "execution_count": 4,
-     "metadata": {},
-     "output_type": "execute_result"
-    },
-    {
-     "data": {
-      "image/png": "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\n",
-      "text/plain": [
-       "<Figure size 432x288 with 2 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "N_richness = 5 # number of richness bins\n",
-    "N_z = 4 # number of redshift bins\n",
-    "\n",
-    "data_lz, z_edges, richness_edges, im  = plt.hist2d(dm_halos['redshift'], dm_halos['richness'],\n",
-    "                                       bins=[N_z, N_richness], cmin=1, range=[[0,0.8],[0,100]]);\n",
-    "\n",
-    "plt.colorbar()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Covariance\n",
-    "\n",
-    "Here we are using a jackknife resampling method to compute the count covariance from the data (adapted from C. Payerne's [code](https://github.com/LSSTDESC/CLCosmo_Sim/blob/issue/15/binned_cosmological_analysis/cluster_abundance/CL_COUNT_DATAOPS_cluster_abundance_covariance.py) in the CLCosmoSim repo). Eventually, this will be replaced by the theoretical covariance provided by `TJPCov`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# Adapted from Constantin Payerne's code in the CLCOSMO_sim repo\n",
-    "\n",
-    "import healpy\n",
-    "from itertools import combinations\n",
-    "def compute_jackknife_covariance(catalog = None, \n",
-    "                                proxy_colname = 'richness', redshift_colname = 'redshift',\n",
-    "                                ra_colname = 'ra', dec_colname = 'dec',\n",
-    "                                proxy_edges = richness_edges, z_edges = z_edges,        \n",
-    "                                hp_power = 7, N_delete = 1):\n",
-    "        r\"\"\"\n",
-    "        Attributes:\n",
-    "        -----------\n",
-    "        proxy_colname: str\n",
-    "            name of the proxy column\n",
-    "        redshift_colname: str\n",
-    "            name of the redshift column\n",
-    "        ra_colname: str\n",
-    "            name of the ra column\n",
-    "        dec_colname: str\n",
-    "            name of the dec column\n",
-    "        proxy_edges: array\n",
-    "            edges of the mass proxy bins\n",
-    "        z_edges: array\n",
-    "            edges of the redshift bins\n",
-    "        hp_power: int\n",
-    "            defines the number of healpix pixels. Healpix Nside = 2**hp_power\n",
-    "        N_delete: int\n",
-    "            number of jackknife region to delete each repetition\n",
-    "        Returns:\n",
-    "        --------\n",
-    "        cov_N: array\n",
-    "            Jackknife covariance matrix\n",
-    "        \"\"\"\n",
-    "        proxy, redshift = np.array(catalog[proxy_colname]), np.array(catalog[redshift_colname])\n",
-    "        ra, dec =  np.array(catalog[ra_colname]), np.array(catalog[dec_colname])\n",
-    "\n",
-    "        index = np.arange(len(proxy))\n",
-    "        healpix = healpy.ang2pix(2**hp_power, ra, dec, nest=True, lonlat=True)\n",
-    "        healpix_list_unique = np.unique(healpix)\n",
-    "        print(f'Number of JK regions: {len(healpix_list_unique)}')\n",
-    "        healpix_combination_delete = list(combinations(healpix_list_unique, N_delete))\n",
-    "        data_jack = []\n",
-    "        \n",
-    "        for i, hp_list_delete in enumerate(healpix_combination_delete):\n",
-    "            if (i/2000.).is_integer():\n",
-    "                print(i) # to track progress\n",
-    "            mask_in_area = np.isin(healpix, hp_list_delete)\n",
-    "            mask_out_area = np.invert(mask_in_area)\n",
-    "            data,_,_ = np.histogram2d(redshift[mask_out_area],proxy[mask_out_area],\n",
-    "                                            bins=[z_edges, proxy_edges])\n",
-    "            data_jack.append(data.flatten())\n",
-    "        \n",
-    "        data_jack = np.array(data_jack)\n",
-    "        N = np.stack((data_jack.astype(float)), axis = 1)\n",
-    "        n_jack = len(healpix_combination_delete)\n",
-    "        cov_N = (n_jack - 1) * np.cov(N, bias = False,ddof=0)\n",
-    "        coeff = (n_jack - N_delete) / (N_delete * n_jack)\n",
-    "        \n",
-    "        return cov_N * coeff"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Number of JK regions: 2154\n",
-      "0\n",
-      "2000\n"
-     ]
-    }
-   ],
-   "source": [
-    "covariance = compute_jackknife_covariance(dm_halos)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "**Correlation matrix - the \"large blocks\" correspond to the $N_z$ redshift bins. In each redshift bin are the $N_{\\rm richness}$ richness bins.**"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<matplotlib.image.AxesImage at 0x7eff5ccb2a60>"
-      ]
-     },
-     "execution_count": 7,
-     "metadata": {},
-     "output_type": "execute_result"
-    },
-    {
-     "data": {
-      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQUAAAD4CAYAAADl7fPiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAAAV20lEQVR4nO3dfYxc5XXH8e+Z2VevDTbYLMZ2g0ktWjcUk1g0CLUyISHGJSGJ0tSoKjSJapKGqlGaKJRWSf6oKtoqiZRAAKdxACnBoSIOSLF4KUlFIoUEQ82Li11cx8TrdW0MeNf22t6d2dM/9traZ5lZnzsvu+PV7yNZOzv37HOfe2f27Myd4/OYuyMiclJhuicgIq1FSUFEEkoKIpJQUhCRhJKCiCTapnsClcw/p+gXLmkPxW47sCA87mhn/JMWG7ZwbDS1Wjk+pJXisXlSe9ux+DkodcfPQbkjPodCnmPLwfM8ZMHHYrQYH9NG47G55PmAMPhcGBl4g9LQ0YpnrCWTwoVL2vnVY0tCsb/3zb8Kjzu0dCQc2/1qLCkBlGfFHrX2wfiztutgjl/eWfFx5794PBx78JKucOzhpfHfiK7XcmSxHL/onuMXuGMgFjd8VnzMtqF4bJ7jyvMHotwdi/v1d79WdZvePohIoq6kYGarzWyHme00s1srbDcz+0a2/QUze2c9+xOR5qs5KZhZEbgTuBZYDtxgZssnhF0LLMv+rQPuqnV/IjI16nmlcDmw0913ufswsBG4fkLM9cD9PuZpYK6ZLaxjnyLSZPUkhUXAnnHf92X35Y0BwMzWmdkWM9vy2us5LtOLSEPVkxQqXT+deMk8EjN2p/t6d1/p7isXnJvjMrKINFQ9SaEPGP+54WKgv4YYEWkh9SSFZ4BlZrbUzDqAtcAjE2IeAW7MPoV4NzDg7vvq2KeINFnNxUvuXjKzW4DHgCKwwd23mdmnsu13A5uBNcBOYAj4eP1TFpFmslZsstK9cIlf+InPhWK3/fW3wuNe9k/x6see/4tf7DyyMHYNZHBZfMzO1+PXVUY7cjyGOUpxZ/fFY3t/sj8enIMNx6tQOTEcDi3tPxCK6//CFeExCyfCobQdz/GY5QiNltJv/9HXGXptT8W6SlU0ikhCSUFEEkoKIpJQUhCRhJKCiCSUFEQkoaQgIgklBRFJKCmISEJJQUQSLdm4dbTTw01W85Qu/9dt8ZLoFbfHx+08FKtD9c54jfFoezxfF07Eu4DO3hOvmR24OBzKkRt7w7GFUny+bUfjc7Ac5cAdgxeF4jrfjA9ayFGRnafMudweP1/tx2LPscm6WeuVgogklBREJKGkICIJJQURSSgpiEhCSUFEEkoKIpKoZ4WoJWb2UzN72cy2mdnfVIhZZWYDZrY1+/el+qYrIs1WT/FSCfhbd3/OzOYAz5rZE+7+3xPifubu19WxHxGZQjW/UnD3fe7+XHb7MPAyVVZ/EpEzR0PKnM3sQuAy4JcVNl9hZs8ztgjM5919W5Ux1jG2CC1tZ82j+9X20L7zdF3OU7q89dZ4SfSa934sFDd40fzwmPOfjx/XwEXxzs/ztg+FY0fm9MTH/cDecOzCWYPh2O2vnxeOPXqsMxw7VI79PTx7Tvx8HTrSHY4tFOMl7yPD8V/TYlvseVN6tvr+677QaGazgYeAz7r7xEf7OeBt7n4p8E3gR9XGGb9sXLEn/mQUkcaqKymYWTtjCeF77v7DidvdfdDdj2S3NwPtZhb/cykiU66eTx8M+A7wsrt/rUrM+VkcZnZ5tr/Xa92niDRfPdcUrgT+HHjRzLZm990G/BacWjbuo8CnzawEHAPWeisuSSUip9SzluTPqbzU/PiYO4A7at2HiEw9VTSKSEJJQUQSSgoiklBSEJGEkoKIJFqymzMFKM+KfXJ5ZGG8xDfadRnipcsAm//jwVDc+z9yY3jM0c74cbUfjceOnBUrHwdY9PCecGz5ngPh2DcuWRaO7d3xajjWj58IxxZ7F8TGPHt2eMzevbvDseVDA+HYtiWLw7E+ECshf+1I9XOlVwoiklBSEJGEkoKIJJQURCShpCAiCSUFEUkoKYhIQklBRBJKCiKSaMmKRitD++CkrRpOGVwWb3DqnfFmmXmarEYrFR/74f3hMdf++j3h2PO74o1Qdx85Nxx72dx4RePGh1aFY9vivVApv/+ScGwh/lSgpz9W3TrUG3seAhRK8XNbGAmHMhovWKXteOy4Rh56vOo2vVIQkYSSgogk6u3mvNvMXsyWhNtSYbuZ2TfMbKeZvWBm76xnfyLSfI24pnCVux+ssu1aYFn27w+Au7KvItKimv324Xrgfh/zNDDXzBY2eZ8iUod6k4IDj5vZs9mybxMtAsZfwu6jynqTZrbOzLaY2ZbS0NE6pyUitar37cOV7t5vZucBT5jZdnd/atz2Sp/nVPzMxN3XA+sBuhcu0doQItOkrlcK7t6ffT0AbAIunxDSBywZ9/1ixhaaFZEWVc+ycT1mNufkbeAa4KUJYY8AN2afQrwbGHD3fTXPVkSarp63D73ApmypyDbg++7+qJl9Ck4tG7cZWAPsBIaAj9c3XRFptnqWjdsFXFrh/rvH3XbgM3nHthJ0HYxdVih3xWtAR9vjL4zmPx+vmY02Wc1Turxx6U/Cse96Nt5k1j1etnusFG/y2vVGOJTCSI5LRofjoRavYqf9aCy4pz9+voo5SpfznAPP8Xq+UIqNO9n+VdEoIgklBRFJKCmISEJJQUQSSgoiklBSEJGEkoKIJJQURCShpCAiCSUFEUm0ZDdnClCaFSsvHe2Il4sWTsRLVgcuipdPtx+NxebpupyndPnZdz0Yjv3tn8b/+8lwKf70sO5wKHTFH4c8Ot+MPxeKJ2Kx/avi++/aH3/O5CldHu1s/HO89MvqcXqlICIJJQURSSgpiEhCSUFEEkoKIpJQUhCRhJKCiCTqadx6cbZc3Ml/g2b22Qkxq8xsYFzMl+qesYg0VT09GncAKwDMrAjsZazN+0Q/c/frat2PiEytRr19uBr4X3d/tUHjicg0aVSZ81rggSrbrjCz5xlbBObz7r6tUlC27Nw6gM7Oucx/8Xhox3vO6wxPcvaeeLnovO1D4diRs2Jdj3cfOTc8Zp6uy3lKl3de9d1w7EU/vDkc+7sbfxOOHZ03OxxbOBJ7HgDg8cd3dN/+UNyi9rc0LK+q1B1vJ91xOEfr6RzNrzsGYi2l902y/7pfKZhZB/BB4N8rbH4OeJu7Xwp8E/hRtXHcfb27r3T3lR0dPfVOS0Rq1Ii3D9cCz7n7W1Kvuw+6+5Hs9mag3czmN2CfItIkjUgKN1DlrYOZnW/ZElJmdnm2v9cbsE8RaZK6rimY2SzgfcDN4+4bv2zcR4FPm1kJOAaszVaNEpEWVVdScPch4NwJ941fNu4O4I569iEiU0sVjSKSUFIQkYSSgogklBREJKGkICKJluzmXOo2Dl7SFYqd3Rf/hHPg4vgcRubEqyoXPbwnFHfZ3FgcwLFSrHQa8nVdzlO6vOsj94Rjl3b+ZTi2OBjvemzleLl3OUfX445Di0JxC7aW42MOxkuXrUmfzJdmxc6tF9TNWUSClBREJKGkICIJJQURSSgpiEhCSUFEEkoKIpJQUhCRhJKCiCSUFEQk0ZJlzuUOOLw0VjJ68V0HwuMeubE3HDvvA3vDseV7YnPY+NCq8Jhdb4RDse54bJ6uy3lKl3/9x98Ox94/GG/T2VMYDsee33YoHLu3NC8Ud1vPDeExi8fjf2NH41XsWI6K6NKs2O/N8DaVOYtI0GmTgpltMLMDZvbSuPvOMbMnzOyV7GvFtGtmq81sh5ntNLNbGzlxEWmOyCuFe4HVE+67FXjS3ZcBT2bfJ7Kl5O5krAX8cuAGM1te12xFpOlOmxTc/Slg4jvc64H7stv3AR+q8KOXAzvdfZe7DwMbs58TkRZW6zWFXnffB5B9Pa9CzCJgfAOBvuw+EWlhzbzQWOnyZtXrqGa2zsy2mNmW8tGjTZyWiEym1qSw38wWAmRfK30m1wcsGff9YsYWma1o/FqSxR6tJSkyXWpNCo8AN2W3bwIerhDzDLDMzJZmi9CuzX5ORFpY5CPJB4BfABebWZ+ZfRK4HXifmb3C2LJxt2exF5jZZgB3LwG3AI8BLwMPVluGXkRax2krGt29WknX1RVi+4E1477fDGyueXYiMuVassy5UIKu1xp/DbRQincGXjhrMBz7xiXLQnFtQ+EhKYzkqG3tih/X6LzZ4dg8XZfzlC7feNbBcOzX3rgoHLtreEE4dtRjz6/CSPzcFobjsXlql/OMa+XYcdkk1dAqcxaRhJKCiCSUFEQkoaQgIgklBRFJKCmISEJJQUQSSgoiklBSEJGEkoKIJFqyzBmo3I2hUtjwSHjIthxtGra/XqlvTGW9O14NxZXff0l8AofjoXkUjhwPx1o5Xl6bp+tyntLlz52zKxz7jwd/Jxzbf2JuKK7cFS9HLh6Ln69glTUAox3xOZS6Y7E+yVT1SkFEEkoKIpJQUhCRhJKCiCSUFEQkoaQgIgklBRFJ1LqW5L+a2XYze8HMNpnZ3Co/u9vMXjSzrWa2pYHzFpEmqXUtySeAd7j77wP/A/zdJD9/lbuvcPeVtU1RRKZSTWtJuvvjWQt3gKcZW+hFRGaARpQ5fwL4QZVtDjxuZg7c4+7rqw1iZuuAdQBtZ8/Do42ET8TLa3M00OXosc5wrB8/EYorlOP7n6zb7kSdb+Y4MI/Hljvjsee3HQrH5um6nKd0+R/mbw/H/vPrsQ7cs38Tv+xmpdPHnOSHc3SJjj29ACjNjo1bmGSudSUFM/t7oAR8r0rIle7eb2bnAU+Y2fbslcdbZAljPUDXBUtyPMtFpJFq/vTBzG4CrgP+zL3yn59scRjc/QCwibHl6UWkhdWUFMxsNfBF4IPuXnGJEzPrMbM5J28D1wAvVYoVkdZR61qSdwBzGHtLsNXM7s5iT60lCfQCPzez54FfAT9290ebchQi0jC1riX5nSqxp9aSdPddwKV1zU5EppwqGkUkoaQgIgklBRFJKCmISEJJQUQSLdnNuVCGjoFYbGn/gfC4HYPxLsJD5Xi+LPbGynZ7+uOFmu1H43XOxRPxcUf37Q/HdhxaFI7dW5oXn0OOVsbRrssQL10G+OK5r4TiNr12dXjMQryxOKWueJlz2/H44zt8IjbuZCXZeqUgIgklBRFJKCmISEJJQUQSSgoiklBSEJGEkoKIJJQURCShpCAiiZasaBwtwvBZsdj+L1wRHjdPg9Oz51RsKFWRnz07FDfUG69i6+mPx/avCoeyqD3e4mLB1nin2dt6KrXdqKwwEj+2clf8McvTZDVaqfj0v9wdHvM/j8X3v6B4NBxbIH4OisHuxB/95cFJ9iciMo6Sgogkal027itmtjfrz7jVzNZU+dnVZrbDzHaa2a2NnLiINEety8YBfD1bDm6Fu2+euNHMisCdwLXAcuAGM1tez2RFpPlqWjYu6HJgp7vvcvdhYCNwfQ3jiMgUqueawi3ZqtMbzKzSf6ZfBOwZ931fdl9FZrbOzLaY2ZbyUPzKrIg0Vq1J4S7g7cAKYB/w1QoxlT53qvp5ibuvd/eV7r6yOKunxmmJSL1qSgruvt/dy+4+CnybysvB9QFLxn2/GOivZX8iMnVqXTZu4bhvP0zl5eCeAZaZ2VIz6wDWAo/Usj8RmTqnrWjMlo1bBcw3sz7gy8AqM1vB2NuB3cDNWewFwL+5+xp3L5nZLcBjQBHY4O7bmnEQItI4TVs2Lvt+M/CWjytPx0ahLVhlbPFK3FyNNQ8d6Q7H9u7dHdt/6dzwmMUcc+3aXwzHlrrjDWE7BnM0jz0ef9FZGI6XORePxWMna0b6ljkEz2+e0uVVOc7tk8dipfEAxRxlzsMeey4M+5tVt6miUUQSSgoiklBSEJGEkoKIJJQURCShpCAiCSUFEUkoKYhIQklBRBJKCiKSaMluzkDl/3hdQdvxeAlonthCMV6yWj40EBszR+lyYSQ+V8+R2jsOx4/LPD6H0fb4HAh2HIZ8x+aH4yXRpa5YbJ6uy3lKl6/ujtfnPzrUGY7tstiTzCYpndYrBRFJKCmISEJJQUQSSgoiklBSEJGEkoKIJJQURCQR6dG4AbgOOODu78ju+wFwcRYyFzjk7isq/Oxu4DBQBkruvrIhsxaRpokUL90L3AHcf/IOd//Tk7fN7KvAZNU7V7l79XWvRaSlRBq3PmVmF1baZmYGfAx4T4PnJSLTpN4y5z8E9rv7K1W2O/C4mTlwj7uvrzaQma0D1gG0z5kX78wbr5il3B4vgx0Zjp+atiWLQ3Gj8abLucp7RztznIQcoXnkqFzO1c15tCNHafqJ+ByiJe+FHCcsT9flPKXLq2fFD+zxoVi9uU/y/wjqTQo3AA9Msv1Kd+83s/OAJ8xse7Zg7VtkCWM9QHfvkiY9dUXkdGr+9MHM2oCPAD+oFpOtA4G7HwA2UXl5ORFpIfV8JPleYLu791XaaGY9Zjbn5G3gGiovLyciLeS0SSFbNu4XwMVm1mdmn8w2rWXCWwczu8DMTq4I1Qv83MyeB34F/NjdH23c1EWkGWpdNg53/4sK951aNs7ddwGX1jk/EZliqmgUkYSSgogklBREJKGkICIJJQURSbRmN+cClLtjoe1H4sO2H4t3Mi62xbvt+sBgKC5XN+lSnvLeeNlwx0C8pXRpVrwuuzQrR5focvxvUak7fh5Ks+PnYTh4zoo56reHPX6+ol2XIV66DHDNrNi4ZxXUzVlEgpQURCShpCAiCSUFEUkoKYhIQklBRBJKCiKSUFIQkYSSgogklBREJGHurdcj1cxeA16dcPd8YCauHzFTjwtm7rHNhON6m7svqLShJZNCJWa2ZSauMDVTjwtm7rHN1OM6SW8fRCShpCAiiTMpKVRdXeoMN1OPC2busc3U4wLOoGsKIjI1zqRXCiIyBZQURCTR8knBzFab2Q4z22lmt073fBrJzHab2YtmttXMtkz3fGplZhvM7ICZvTTuvnPM7AkzeyX7Om8651irKsf2FTPbmz1uW81szXTOsdFaOimYWRG4E7gWWA7cYGbLp3dWDXeVu684wz/3vhdYPeG+W4En3X0Z8GT2/ZnoXt56bABfzx63Fe6+ucL2M1ZLJwXGVqne6e673H0Y2AhcP81zkgnc/SngjQl3Xw/cl92+D/jQVM6pUaoc24zW6klhEbBn3Pd92X0zhQOPm9mzZrZuuifTYL3uvg8g+3reNM+n0W4xsxeytxdn5Fujalo9KVTqwz2TPkO90t3fydjbo8+Y2R9N94Qk5C7g7cAKYB/w1WmdTYO1elLoA5aM+34x0D9Nc2m4bJVu3P0AsImxt0szxX4zWwiQfT0wzfNpGHff7+5ldx8Fvs3MetxaPik8Aywzs6Vm1gGsBR6Z5jk1hJn1mNmck7eBa4CXJv+pM8ojwE3Z7ZuAh6dxLg11MtllPszMetxadIWojLuXzOwW4DGgCGxw923TPK1G6QU2mRmMPQ7fd/dHp3dKtTGzB4BVwHwz6wO+DNwOPGhmnwR+A/zJ9M2wdlWObZWZrWDsrexu4Obpml8zqMxZRBKt/vZBRKaYkoKIJJQURCShpCAiCSUFEUkoKYhIQklBRBL/D9pQPRDS+xaIAAAAAElFTkSuQmCC\n",
-      "text/plain": [
-       "<Figure size 432x288 with 1 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "plt.imshow(np.corrcoef(covariance))\n",
-    "#plt.imshow((covariance))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Store everything in a SACC file"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "s_count = sacc.Sacc()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# First we build the cluster count tracer\n",
-    "# Here we will store the richness and redshift bin information in metadata,\n",
-    "\n",
-    "for i in np.arange(N_z):\n",
-    "    for j in np.arange(N_richness):\n",
-    "        name = f'clusters_{i}_{j}'\n",
-    "        metadata = {'Mproxy_name': 'richness',\n",
-    "                    'Mproxy_min': richness_edges[j], 'Mproxy_max': richness_edges[j+1],\n",
-    "                    'z_name': 'redshift',\n",
-    "                    'z_min': z_edges[i], 'z_max': z_edges[i+1],\n",
-    "                    'sky_area': metadata['sky_area']\n",
-    "                    \n",
-    "        }\n",
-    "        s_count.add_tracer('misc', name, metadata=metadata)\n",
-    "        "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### First the count values"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# One of our identifiers is a standard type name that is predefined\n",
-    "cluster_count = sacc.standard_types.cluster_mass_count_wl\n",
-    "\n",
-    "for i in range(N_z): \n",
-    "    for j in range(N_richness): \n",
-    "        # Cluster number counts data\n",
-    "        tracer = f'clusters_{i}_{j}'\n",
-    "        value = data_lz[i][j]\n",
-    "        s_count.add_data_point(cluster_count, (tracer,), int(value), err=1.)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_0',), value=2777, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_1',), value=614, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_2',), value=143, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_3',), value=72, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_4',), value=35, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_0',), value=15167, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_1',), value=3925, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_2',), value=975, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_3',), value=426, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_4',), value=273, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_0',), value=28347, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_1',), value=9244, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_2',), value=1915, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_3',), value=838, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_4',), value=496, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_0',), value=37108, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_1',), value=14251, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_2',), value=2821, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_3',), value=1138, err=1.0)\n",
-      "DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_4',), value=693, err=1.0)\n"
-     ]
-    }
-   ],
-   "source": [
-    "for d in s_count.data:\n",
-    "    print(d)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "{'Mproxy_name': 'richness', 'Mproxy_min': 0.0, 'Mproxy_max': 20.0, 'z_name': 'redshift', 'z_min': 0.0, 'z_max': 0.2, 'sky_area': 439.78986}\n",
-      "{'Mproxy_name': 'richness', 'Mproxy_min': 0.0, 'Mproxy_max': 20.0, 'z_name': 'redshift', 'z_min': 0.2, 'z_max': 0.4, 'sky_area': 439.78986}\n",
-      "{'Mproxy_name': 'richness', 'Mproxy_min': 40.0, 'Mproxy_max': 60.0, 'z_name': 'redshift', 'z_min': 0.2, 'z_max': 0.4, 'sky_area': 439.78986}\n"
-     ]
-    }
-   ],
-   "source": [
-    "print(s_count.tracers['clusters_0_0'].metadata)\n",
-    "print(s_count.tracers['clusters_1_0'].metadata)\n",
-    "print(s_count.tracers['clusters_1_2'].metadata)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Then the add the covariance and save the file"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "s_count.add_covariance(covariance)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "s_count.to_canonical_order()\n",
-    "s_count.save_fits(\"clusters.sacc\", overwrite=True)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Read in the SACC file and check content"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "s2 = sacc.Sacc.load_fits(\"./clusters.sacc\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_0',), value=2777, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_1',), value=614, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_2',), value=143, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_3',), value=72, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_0_4',), value=35, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_0',), value=15167, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_1',), value=3925, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_2',), value=975, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_3',), value=426, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_1_4',), value=273, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_0',), value=28347, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_1',), value=9244, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_2',), value=1915, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_3',), value=838, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_2_4',), value=496, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_0',), value=37108, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_1',), value=14251, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_2',), value=2821, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_3',), value=1138, err=1.0),\n",
-       " DataPoint(data_type='cluster_mass_count_wl', tracers=('clusters_3_4',), value=693, err=1.0)]"
-      ]
-     },
-     "execution_count": 16,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "s2.data"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<matplotlib.image.AxesImage at 0x7eff5ca5a3a0>"
-      ]
-     },
-     "execution_count": 17,
-     "metadata": {},
-     "output_type": "execute_result"
-    },
-    {
-     "data": {
-      "image/png": "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\n",
-      "text/plain": [
-       "<Figure size 432x288 with 1 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "plt.imshow(np.corrcoef(s2.covariance.covmat))"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "mydesc",
-   "language": "python",
-   "name": "mydesc"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.8"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/examples/demo_sacc_for_clusters_N+M.ipynb b/examples/demo_sacc_for_clusters_N+M.ipynb
new file mode 100644
index 0000000..8942a1b
--- /dev/null
+++ b/examples/demo_sacc_for_clusters_N+M.ipynb
@@ -0,0 +1,615 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "7f562b0e-7fe0-41ca-8760-5ed56753742c",
+   "metadata": {},
+   "source": [
+    "# SACC file example for binned cluster analysis"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "53ee68af-3079-4711-9056-36f63630e1c6",
+   "metadata": {},
+   "source": [
+    "This demo notebook examplifies storing information for a binned cluster analysis into a sacc format. Steps are the following:\n",
+    "- generate a mock cluster catalog in richness-redshift bins. This is done using NumCosmo functionalities and requires Numcosmo v>=0.17\n",
+    "- store all this information using the SACC framework. We present to cases:\n",
+    "    - store **number counts and mean mass** in a single SACC file;\n",
+    "    - store **number counts and radial shear profile** in a single SACC file\n",
+    "\n",
+    "For the latter, in each richness-redshift bin, we generate a mock radial shear profile using CLMM. This is meant to correspond to the stacked shear profile in that bin.\n",
+    "\n",
+    "This should be straightforward to adapt to store counts, mean mass and radial profile in individual sacc file instead."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7f10789d-5ee5-4ee4-ba3c-b4605909b22b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import math\n",
+    "import itertools\n",
+    "import numpy as np\n",
+    "from astropy.table import Table\n",
+    "from astropy.io import fits\n",
+    "from scipy import stats\n",
+    "import matplotlib.pyplot as plt\n",
+    "\n",
+    "# This require Numcosmo v>=0.17\n",
+    "from numcosmo_py import Nc\n",
+    "from numcosmo_py import Ncm\n",
+    "\n",
+    "import sacc as sacc\n",
+    "\n",
+    "os.environ['CLMM_MODELING_BACKEND'] = 'nc' # Need to use NumCosmo as CLMM's backend as well.\n",
+    "import clmm\n",
+    "from clmm import Cosmology"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6f59eaa4-e6f9-40f9-9497-966f81dee10b",
+   "metadata": {},
+   "source": [
+    "## Prepare NumCosmo cosmology"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8fad6f65-7a43-4965-8e27-01fbc4f7afe5",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "H0 = 71.0\n",
+    "Ob0 = 0.0448\n",
+    "Odm0 = 0.22\n",
+    "n_s = 0.963\n",
+    "sigma8 = 0.8\n",
+    "\n",
+    "Ncm.cfg_init()\n",
+    "#cosmo = Nc.HICosmoDEXcdm() # not (yet) supported by CLMM --> using Nc.HICosmoDECpl() instead\n",
+    "cosmo = Nc.HICosmoDECpl()\n",
+    "reion = Nc.HIReionCamb.new()\n",
+    "prim = Nc.HIPrimPowerLaw.new()\n",
+    "\n",
+    "cosmo.add_submodel(reion)\n",
+    "cosmo.add_submodel(prim)\n",
+    "\n",
+    "dist = Nc.Distance.new(2.0)\n",
+    "\n",
+    "tf = Nc.TransferFunc.new_from_name(\"NcTransferFuncEH\")\n",
+    "\n",
+    "psml = Nc.PowspecMLTransfer.new(tf)\n",
+    "\n",
+    "# psml = Nc.PowspecMLCBE.new ()\n",
+    "psml.require_kmin(1.0e-6)\n",
+    "psml.require_kmax(1.0e3)\n",
+    "\n",
+    "psf = Ncm.PowspecFilter.new(psml, Ncm.PowspecFilterType.TOPHAT)\n",
+    "psf.set_best_lnr0()\n",
+    "\n",
+    "cosmo.props.H0 = H0\n",
+    "cosmo.props.Omegab = Ob0\n",
+    "cosmo.props.Omegac = Odm0\n",
+    "\n",
+    "cosmo.omega_x2omega_k()\n",
+    "cosmo.param_set_by_name(\"Omegak\", 0.0)\n",
+    "\n",
+    "prim.props.n_SA = n_s\n",
+    "\n",
+    "old_amplitude = math.exp(prim.props.ln10e10ASA)\n",
+    "prim.props.ln10e10ASA = math.log((sigma8 / cosmo.sigma8(psf)) ** 2 * old_amplitude)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a7177d4b-7e34-46f6-9bdf-f11d9d0034b9",
+   "metadata": {},
+   "source": [
+    "## Generate a mock cluster catalog using NumCosmo\n",
+    "\n",
+    "NumCosmo has a built-in functionality to generate mock cluster catalogs, from a given halo mass function (HMF) and with a log-normal richness-mass relation. \n",
+    "- For the HMF, the code below uses the Tinker (2008) definition. \n",
+    "- For the richness-mass relation, six parameters can be specified as the NumCosmo functionality allows for the mean relation and scatter of the log-normal distribution to have a linear dependences with both log(mass) and log(1+z) (that's 2 parameters and a normalisation, for both the mean relation and the scatter). \n",
+    "\n",
+    "Below we use the Murata et al. (2019) parameters (from their Table 1), but neglecting the quadratic dependence in redshift they use (this is inconsistent, but sufficient to generate this mock dataset)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "472f1c1a-2aa4-4f01-8988-45f958f5aed4",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Define the sky area and the richness and redshift ranges\n",
+    "area = 5000. # deg2\n",
+    "lnRl = 0.0\n",
+    "lnRu = 6.0\n",
+    "zl = 0.2\n",
+    "zu = 0.65\n",
+    "\n",
+    "cluster_z = Nc.ClusterRedshift.new_from_name(\n",
+    "    f\"NcClusterRedshiftNodist{{'z-min': <{zl:22.15e}>, 'z-max':<{zu:22.15e}>}}\"\n",
+    ")\n",
+    "\n",
+    "cluster_m = Nc.ClusterMass.new_from_name(\n",
+    "    f\"NcClusterMassAscaso{{'M0':<{3.0e14 / 0.71:22.15e}>,'z0':<0.6>, \"\n",
+    "    f\"'lnRichness-min':<{lnRl:22.15e}>, 'lnRichness-max':<{lnRu:22.15e}>}}\"\n",
+    ")\n",
+    "\n",
+    "# mean richness-mass relation parameters\n",
+    "cluster_m.param_set_by_name(\"mup0\", 3.15) # normalisation\n",
+    "cluster_m.param_set_by_name(\"mup1\", 0.86 / np.log(10)) # mass dependence, adapted to match the log10 definition used in NumCosmo\n",
+    "cluster_m.param_set_by_name(\"mup2\", -0.21 / np.log(10)) # redshift dependence, adapted to match the log10 definition used in NumCosmo\n",
+    "# richness-mass scatter parameter\n",
+    "cluster_m.param_set_by_name(\"sigmap0\", 0.33) # normalisation\n",
+    "cluster_m.param_set_by_name(\"sigmap1\", -0.08 / np.log(10)) # mass dependence, adapted to match the log10 definition used in NumCosmo\n",
+    "cluster_m.param_set_by_name(\"sigmap2\", 0.03 / np.log(10)) # redshift dependence, adapted to match the log10 definition used in NumCosmo\n",
+    "\n",
+    " \n",
+    "# Numcosmo Mass Function\n",
+    "# First, define the multiplicity function. \n",
+    "mulf = Nc.MultiplicityFuncTinker.new() # Tinker (2008)\n",
+    "mulf.set_linear_interp(True)  # This reproduces the linear interpolation done in CCL\n",
+    "mulf.set_mdef(Nc.MultiplicityFuncMassDef.MEAN)\n",
+    "mulf.set_Delta(200)\n",
+    "\n",
+    "# Second, construct a filtered power spectrum\n",
+    "hmf = Nc.HaloMassFunction.new(dist, psf, mulf)\n",
+    "hmf.set_area_sd(area)\n",
+    "\n",
+    "# Cluster Abundance Obj\n",
+    "ca = Nc.ClusterAbundance.new(hmf, None)\n",
+    "\n",
+    "# Number Counts object\n",
+    "ncount = Nc.DataClusterNCount.new(\n",
+    "    ca, \"NcClusterRedshiftNodist\", \"NcClusterMassAscaso\"\n",
+    ")\n",
+    "ca.prepare(cosmo, cluster_z, cluster_m)\n",
+    "mset = Ncm.MSet.new_array([cosmo, cluster_z, cluster_m])\n",
+    "\n",
+    "rng = Ncm.RNG.seeded_new(None, 32)\n",
+    "ncount.init_from_sampling(mset, area * ((np.pi / 180) ** 2), rng)\n",
+    "\n",
+    "ncount.catalog_save(\"ncount_rich.fits\", True)\n",
+    "ncdata_fits = fits.open(\"ncount_rich.fits\")\n",
+    "ncdata_data = ncdata_fits[1].data \n",
+    "ncdata_Table = Table(ncdata_data)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3c919b49-ee2a-4500-948a-d852169aa1c3",
+   "metadata": {},
+   "source": [
+    "## SACC for number counts and mean mass in the $N_{\\rm richness} \\times N_z$ richness-redshift plane\n",
+    "- count halos and compute mean mass in each bin, directly from the data generated at the previous step\n",
+    "- associate a \"mock\" shear profile, generated using CLMM from the mean mass in the bin, mock diagonal inter-radial bin covariance. Very rough/quick solution to get some \"shear profile data\".\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2179c307-8f4f-4194-9d2f-26b9258dad54",
+   "metadata": {},
+   "source": [
+    "### Counts and mean mass\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b0db89c8-f549-497f-bce8-f33b79166701",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "data_table = ncdata_Table[ncdata_Table[\"LNM_OBS\"] > 2]\n",
+    "cluster_z = data_table[\"Z_OBS\"]\n",
+    "cluster_lnm = data_table[\"LNM_OBS\"]\n",
+    "cluster_richness = cluster_lnm / np.log(10.0)\n",
+    "cluster_logM = np.log10(np.exp(data_table[\"LNM_TRUE\"]))\n",
+    "\n",
+    "N_richness = 4  # number of richness bins\n",
+    "N_z = 3  # number of redshift bins\n",
+    "\n",
+    "cluster_counts, z_edges, richness_edges, _ = stats.binned_statistic_2d(\n",
+    "    cluster_z, cluster_richness, cluster_logM, \"count\", bins=[N_z, N_richness]\n",
+    ")\n",
+    "\n",
+    "mean_logM = stats.binned_statistic_2d(\n",
+    "    cluster_z,\n",
+    "    cluster_richness,\n",
+    "    cluster_logM,\n",
+    "    \"mean\",\n",
+    "    bins=[z_edges, richness_edges],\n",
+    ").statistic\n",
+    "\n",
+    "std_logM = stats.binned_statistic_2d(\n",
+    "    cluster_z, cluster_richness, cluster_logM, \"std\", bins=[z_edges, richness_edges]\n",
+    ").statistic\n",
+    "\n",
+    "var_mean_logM = std_logM**2 / cluster_counts \n",
+    "\n",
+    "covariance = np.diag(\n",
+    "    np.concatenate((cluster_counts.flatten(), var_mean_logM.flatten()))\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "950a2ae9-e433-4fcf-a125-503c17f212c4",
+   "metadata": {},
+   "source": [
+    "### Saving to SACC"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b3144e1f-5b5c-420c-83d5-2e7f34d447c6",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s_count = sacc.Sacc()\n",
+    "bin_z_labels = []\n",
+    "bin_richness_labels = []\n",
+    "\n",
+    "survey_name = \"NC_mock_redshift_richness\"\n",
+    "s_count.add_tracer(\"survey\", survey_name, area)\n",
+    "\n",
+    "for i, z_bin in enumerate(zip(z_edges[:-1], z_edges[1:])):\n",
+    "    z_lower, z_upper = z_bin\n",
+    "    bin_z_label = f\"bin_z_{i}\"\n",
+    "    s_count.add_tracer(\"bin_z\", bin_z_label, z_lower, z_upper)\n",
+    "    bin_z_labels.append(bin_z_label)\n",
+    "\n",
+    "for i, richness_bin in enumerate(zip(richness_edges[:-1], richness_edges[1:])):\n",
+    "    richness_lower, richness_upper = richness_bin\n",
+    "    bin_richness_label = f\"bin_rich_{i}\"\n",
+    "    s_count.add_tracer(\n",
+    "        \"bin_richness\", bin_richness_label, richness_lower, richness_upper\n",
+    "    )\n",
+    "    bin_richness_labels.append(bin_richness_label)\n",
+    "\n",
+    "cluster_count = sacc.standard_types.cluster_counts\n",
+    "cluster_mass = sacc.standard_types.cluster_mean_log_mass\n",
+    "\n",
+    "counts_and_edges = zip(\n",
+    "    cluster_counts.flatten(), itertools.product(bin_z_labels, bin_richness_labels)\n",
+    ")\n",
+    "\n",
+    "\n",
+    "mean_logM_and_edges = zip(\n",
+    "    mean_logM.flatten(), itertools.product(bin_z_labels, bin_richness_labels)\n",
+    ")\n",
+    "\n",
+    "\n",
+    "for counts, (bin_z_label, bin_richness_label) in counts_and_edges:\n",
+    "    s_count.add_data_point(\n",
+    "        cluster_count, (survey_name, bin_z_label, bin_richness_label), int(counts)\n",
+    "    )\n",
+    "    \n",
+    "for bin_mean_logM, (bin_z_label, bin_richness_label) in mean_logM_and_edges:\n",
+    "    s_count.add_data_point(\n",
+    "        cluster_mass,\n",
+    "        (survey_name, bin_z_label, bin_richness_label),\n",
+    "        bin_mean_logM,\n",
+    "    )\n",
+    "\n",
+    "# Then the add the covariance and save the file\n",
+    "s_count.add_covariance(covariance)\n",
+    "s_count.to_canonical_order()\n",
+    "s_count.save_fits(\"cluster_redshift_richness_sacc_data.fits\", overwrite=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "36896c28-cc63-49bf-87d1-e9c7431ea4d9",
+   "metadata": {},
+   "source": [
+    "### Loading SACC file and check contents"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "49dfb4d9-ab69-48f0-9005-f0363263d2d7",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s2 = sacc.Sacc.load_fits(\"./cluster_redshift_richness_sacc_data.fits\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "34090216-8645-4b69-b85d-4a5498497a9e",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s2.get_data_types()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0aa582db-74f9-4b85-bc25-711644109c3c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s2.data"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "193116ae-001f-4cb0-9df7-0a0694febed5",
+   "metadata": {},
+   "source": [
+    "#### Look at covariance. \n",
+    "Plotting log10 of the covariance for display purposes. The first upper left block correspond to the count covariance. \n",
+    "The lower right block to the mean mass covariance. No off-diagonal terms were considered in this simple example. This is just a place holder to show how the SACC file is filled in."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "14b1d729-d470-42b0-a679-98180c60708c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.imshow(np.log10(s2.covariance.covmat))\n",
+    "plt.colorbar()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "961fdda8-0431-4c32-b8e5-a054a6a3e7fc",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## SACC for number counts and shear profile in the $N_{\\rm richness} \\times N_z$ richness-redshift plane\n",
+    "\n",
+    "\n",
+    "Rather than saving the mean mass into SACC, one may want to save the 'stacked' shear profile in a richness-redshift bin. This possible as exemplified below. "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e57ead74-2612-459b-9b4b-4e07a8392614",
+   "metadata": {},
+   "source": [
+    "### Prepare new sacc, filling it with counts (same as above)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6226ebf5-5ced-4886-bbed-c04284043f75",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s_count2 = sacc.Sacc()\n",
+    "bin_z_labels = []\n",
+    "bin_richness_labels = []\n",
+    "\n",
+    "survey_name = \"NC_mock_redshift_richness\"\n",
+    "s_count2.add_tracer(\"survey\", survey_name, area)\n",
+    "\n",
+    "for i, z_bin in enumerate(zip(z_edges[:-1], z_edges[1:])):\n",
+    "    z_lower, z_upper = z_bin\n",
+    "    bin_z_label = f\"bin_z_{i}\"\n",
+    "    s_count2.add_tracer(\"bin_z\", bin_z_label, z_lower, z_upper)\n",
+    "    bin_z_labels.append(bin_z_label)\n",
+    "\n",
+    "for i, richness_bin in enumerate(zip(richness_edges[:-1], richness_edges[1:])):\n",
+    "    richness_lower, richness_upper = richness_bin\n",
+    "    bin_richness_label = f\"bin_rich_{i}\"\n",
+    "    s_count2.add_tracer(\n",
+    "        \"bin_richness\", bin_richness_label, richness_lower, richness_upper\n",
+    "    )\n",
+    "    bin_richness_labels.append(bin_richness_label)\n",
+    "\n",
+    "cluster_count = sacc.standard_types.cluster_counts\n",
+    "counts_and_edges = zip(\n",
+    "    cluster_counts.flatten(), itertools.product(bin_z_labels, bin_richness_labels)\n",
+    ")\n",
+    "for counts, (bin_z_label, bin_richness_label) in counts_and_edges:\n",
+    "    s_count2.add_data_point(\n",
+    "        cluster_count, (survey_name, bin_z_label, bin_richness_label), int(counts)\n",
+    "    )\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3decd4bb-2560-40bf-b461-b84cd7f87cd4",
+   "metadata": {},
+   "source": [
+    "### Use CLMM to generate a mock shear profile"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "cd235d47-bbc4-4b74-9276-56aa5fac14b0",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Setting things up for CLMM\n",
+    "cosmo_clmm = Cosmology()\n",
+    "cosmo_clmm._init_from_cosmo(cosmo)\n",
+    "moo = clmm.Modeling(massdef=\"mean\", delta_mdef=200, halo_profile_model=\"nfw\")\n",
+    "moo.set_cosmo(cosmo_clmm)\n",
+    "# assuming the same concentration for all masses. Not realistic, but avoid having to call a mass-concentration relation.\n",
+    "moo.set_concentration(4)\n",
+    "\n",
+    "# we'll need the mean redshift of the clusters in the redshift bin\n",
+    "mean_z = stats.binned_statistic_2d(\n",
+    "    cluster_z,\n",
+    "    cluster_richness,\n",
+    "    cluster_z,\n",
+    "    \"mean\",\n",
+    "    bins=[z_edges, richness_edges],\n",
+    ").statistic\n",
+    "\n",
+    "radius_edges = clmm.make_bins(0.3, 6., nbins=6, method=\"evenlog10width\") # 6 radial bins log-spaced between 0.3 and 6 Mpc"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b37e9e68-1459-4f37-9d0c-e713535a027f",
+   "metadata": {},
+   "source": [
+    "### Add the shear profile values to the SACC file\n",
+    "\n",
+    "To do so, on top of the redshift bin tracer and the richness bin tracer already used for the counts and mean mass, we need a radial bin tag for each data point, along with its value."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c912d717-eb26-4072-b9ba-281befa7ff9b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "bin_radius_list = []\n",
+    "radius_centers = []\n",
+    "for i, radius_bin in enumerate(zip(radius_edges[:-1], radius_edges[1:])):\n",
+    "    radius_lower, radius_upper = radius_bin\n",
+    "    bin_radius_list.append((radius_lower, radius_upper))\n",
+    "    radius_centers.append(radius_lower + (radius_upper - radius_lower)/2)\n",
+    "cluster_shear = sacc.standard_types.cluster_shear"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f075780d-fc7b-48db-a925-1be98db349cd",
+   "metadata": {},
+   "source": [
+    "Then we use CLMM to generate a mock shear profile in each richness and redshift bin, using the mean cluster mass and mean redhift in the bin as input."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "433d8c0e-4c4c-4214-a72d-0b011f2541fc",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "redshifts_masses_and_edges = zip(\n",
+    "    mean_z.flatten(), mean_logM.flatten(),itertools.product(bin_z_labels, bin_richness_labels)\n",
+    ")\n",
+    "for redshift, log_mass, (bin_z_label, bin_richness_label) in redshifts_masses_and_edges:\n",
+    "    mass = 10**log_mass\n",
+    "    moo.set_mass(mass)\n",
+    "    profile = moo.eval_excess_surface_density(radius_centers, redshift)\n",
+    "    for i in range(0,len(bin_radius_list)-1):\n",
+    "        s_count2.add_data_point(\n",
+    "            cluster_shear, (survey_name, bin_z_label, bin_richness_label), profile[i], radius_bin=bin_radius_list[i]\n",
+    "    )\n",
+    "        "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1e275694-a991-4ed1-9c82-d213b609bdfa",
+   "metadata": {},
+   "source": [
+    "This is what the data now looks like:\n",
+    "- 4 richness x 3 redshift bins \"number counts\" datapoints\n",
+    "- 4 richness x 3 redshift x 6 radius bins \"shear profile\" datapoints"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "04db6a66-6b33-45d7-8a7f-5ab00890523c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "s_count2.data"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b641b9c2-b261-4b80-a9ad-a4b22f5255ac",
+   "metadata": {},
+   "source": [
+    "### Santity check - plotting the shear profile for two richness bins, to check that the high richness bin as higher shear (i.e. ordering of data is OK)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "34a6cbe8-5cd8-4326-9453-b7322e8f1a87",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "DS0_points = []\n",
+    "DS0_r_center = []\n",
+    "DS2_points = []\n",
+    "DS2_r_center = []\n",
+    "\n",
+    "DS0 = s_count2.get_data_points(data_type='cluster_shear', tracers=('NC_mock_redshift_richness', 'bin_z_1', 'bin_rich_0'))\n",
+    "DS2 = s_count2.get_data_points(data_type='cluster_shear', tracers=('NC_mock_redshift_richness', 'bin_z_1', 'bin_rich_2'))\n",
+    "for data_i in DS0:\n",
+    "    DS0_points.append(data_i.value)\n",
+    "    r_bin = data_i.get_tag('radius_bin')\n",
+    "    DS0_r_center.append(r_bin[0] + (r_bin[1] - r_bin[0])/2)\n",
+    "for data_i in DS2:\n",
+    "    DS2_points.append(data_i.value)\n",
+    "    r_bin = data_i.get_tag('radius_bin')\n",
+    "    DS2_r_center.append(r_bin[0] + (r_bin[1] - r_bin[0])/2)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "209ca0b4-a743-40da-a956-1688291682cc",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.loglog(DS0_r_center, DS0_points, label='bin_rich_0', marker='x')\n",
+    "plt.loglog(DS2_r_center, DS2_points, label='bin_rich_2', marker='x')\n",
+    "plt.xlabel('R [Mpc]')\n",
+    "plt.ylabel('$\\Delta\\Sigma$ [M$_\\odot \\;$Mpc$^{-2}$]')\n",
+    "plt.legend()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e927a990-36a8-482a-8f58-13fcd4aff2e2",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python (firecrown2.0)",
+   "language": "python",
+   "name": "firecrown"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/sacc/data_types.py b/sacc/data_types.py
index 5e5f057..c337a66 100644
--- a/sacc/data_types.py
+++ b/sacc/data_types.py
@@ -32,7 +32,10 @@
     "xi_plus_im": ['theta'],
     "xi_minus_re": ['theta'],
     "xi_minus_im": ['theta'],
-    "count": []
+    "count": [],
+    "cluster_counts": [],
+    "cluster_mean_log_mass": [],
+    "cluster_shear": ["radius_bin"]
 }
 
 required_tags_verbose = {
diff --git a/sacc/tracers.py b/sacc/tracers.py
index 7ccbd2d..548ed55 100644
--- a/sacc/tracers.py
+++ b/sacc/tracers.py
@@ -708,7 +708,7 @@ def to_tables(cls, instance_list):
         from tables_io.convUtils import convertToApTables
 
         tables = []
-        
+
         for tracer in instance_list:
             table_dict = tracer.ensemble.build_tables()
             ap_tables = convertToApTables(table_dict)
@@ -718,13 +718,13 @@ def to_tables(cls, instance_list):
             meta_table.meta['SACCTYPE'] = 'tracer'
             meta_table.meta['SACCCLSS'] = cls.tracer_type
             meta_table.meta['SACCNAME'] = tracer.name
-            meta_table.meta['SACCQTTY'] = tracer.quantity            
+            meta_table.meta['SACCQTTY'] = tracer.quantity
             meta_table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}:meta'
 
             data_table.meta['SACCTYPE'] = 'tracer'
             data_table.meta['SACCCLSS'] = cls.tracer_type
             data_table.meta['SACCNAME'] = tracer.name
-            data_table.meta['SACCQTTY'] = tracer.quantity            
+            data_table.meta['SACCQTTY'] = tracer.quantity
             data_table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}:data'
 
             for kk, vv in tracer.metadata.items():
@@ -735,7 +735,7 @@ def to_tables(cls, instance_list):
                 ancil_table.meta['SACCTYPE'] = 'tracer'
                 ancil_table.meta['SACCCLSS'] = cls.tracer_type
                 ancil_table.meta['SACCNAME'] = tracer.name
-                ancil_table.meta['SACCQTTY'] = tracer.quantity            
+                ancil_table.meta['SACCQTTY'] = tracer.quantity
                 ancil_table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}:ancil'
 
                 tables.append(ancil_table)
@@ -768,7 +768,7 @@ def from_tables(cls, table_list):
             table_key = f'{tokens[0]}:{tokens[1]}:{tokens[2]}'
             table_type = f'{tokens[3]}'
             if table_key not in sorted_dict:
-                sorted_dict[table_key] = {table_type:table_}
+                sorted_dict[table_key] = {table_type: table_}
             else:
                 sorted_dict[table_key][table_type] = table_
 
@@ -786,3 +786,314 @@ def from_tables(cls, table_list):
                                 quantity=quantity,
                                 metadata=metadata)
         return tracers
+
+
+class BinZTracer(BaseTracer, tracer_type="bin_z"):  # type: ignore
+    """A tracer for a single redshift bin. The tracer shall
+    be used for binned data where we want a desired quantity
+    per interval of redshift, such that we only need the data
+    for a given interval instead of at individual redshifts."""
+
+    def __init__(self, name: str, lower: float, upper: float, **kwargs):
+        """
+        Create a tracer corresponding to a single redshift bin.
+
+        :param name: The name of the tracer
+        :param lower: The lower bound of the redshift bin
+        :param upper: The upper bound of the redshift bin
+        """
+        super().__init__(name, **kwargs)
+        self.lower = lower
+        self.upper = upper
+
+    def __eq__(self, other) -> bool:
+        """Test for equality.  If :python:`other` is not a
+        :python:`BinZTracer`, then it is not equal to :python:`self`.
+        Otherwise, they are equal if names, and the z-range of the bins,
+        are equal."""
+        if not isinstance(other, BinZTracer):
+            return False
+        return (
+            self.name == other.name
+            and self.lower == other.lower
+            and self.upper == other.upper
+        )
+
+    @classmethod
+    def to_tables(cls, instance_list):
+        """Convert a list of BinZTracers to a single astropy table
+
+        This is used when saving data to a file.
+        One table is generated with the information for all the tracers.
+
+        :param instance_list: List of tracer instances
+        :return: List with a single astropy table
+        """
+
+        names = ["name", "quantity", "lower", "upper"]
+
+        cols = [
+            [obj.name for obj in instance_list],
+            [obj.quantity for obj in instance_list],
+            [obj.lower for obj in instance_list],
+            [obj.upper for obj in instance_list],
+        ]
+
+        table = Table(data=cols, names=names)
+        table.meta["SACCTYPE"] = "tracer"
+        table.meta["SACCCLSS"] = cls.tracer_type
+        table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}"
+        return [table]
+
+    @classmethod
+    def from_tables(cls, table_list):
+        """Convert an astropy table into a dictionary of tracers
+
+        This is used when loading data from a file.
+        One tracer object is created for each "row" in each table.
+
+        :param table_list: List of astropy tables
+        :return: Dictionary of tracers
+        """
+        tracers = {}
+
+        for table in table_list:
+            for row in table:
+                name = row["name"]
+                quantity = row["quantity"]
+                lower = row["lower"]
+                upper = row["upper"]
+                tracers[name] = cls(name, quantity=quantity, lower=lower, upper=upper)
+        return tracers
+
+class BinLogMTracer(BaseTracer, tracer_type="bin_logM"):  # type: ignore
+    """A tracer for a single log-mass bin. The tracer shall
+    be used for binned data where we want a desired quantity
+    per interval of log(mass), such that we only need the data
+    for a given interval instead of at individual masses."""
+
+    def __init__(self, name: str, lower: float, upper: float, **kwargs):
+        """
+        Create a tracer corresponding to a single log-mass bin.
+
+        :param name: The name of the tracer
+        :param lower: The lower bound of the log-mass bin
+        :param upper: The upper bound of the log-mass bin
+        """
+        super().__init__(name, **kwargs)
+        self.lower = lower
+        self.upper = upper
+
+    def __eq__(self, other) -> bool:
+        """Test for equality.  If :python:`other` is not a
+        :python:`BinLogMTracer`, then it is not equal to :python:`self`.
+        Otherwise, they are equal if names, and the z-range of the bins,
+        are equal."""
+        if not isinstance(other, BinLogMTracer):
+            return False
+        return (
+            self.name == other.name
+            and self.lower == other.lower
+            and self.upper == other.upper
+        )
+
+    @classmethod
+    def to_tables(cls, instance_list):
+        """Convert a list of BinLogMTracers to a single astropy table
+
+        This is used when saving data to a file.
+        One table is generated with the information for all the tracers.
+
+        :param instance_list: List of tracer instances
+        :return: List with a single astropy table
+        """
+
+        names = ["name", "quantity", "lower", "upper"]
+
+        cols = [
+            [obj.name for obj in instance_list],
+            [obj.quantity for obj in instance_list],
+            [obj.lower for obj in instance_list],
+            [obj.upper for obj in instance_list],
+        ]
+        table = Table(data=cols, names=names)
+        table.meta["SACCTYPE"] = "tracer"
+        table.meta["SACCCLSS"] = cls.tracer_type
+        table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}"
+        return [table]
+
+    @classmethod
+    def from_tables(cls, table_list):
+        """Convert an astropy table into a dictionary of tracers
+
+        This is used when loading data from a file.
+        One tracer object is created for each "row" in each table.
+
+        :param table_list: List of astropy tables
+        :return: Dictionary of tracers
+        """
+        tracers = {}
+
+        for table in table_list:
+            for row in table:
+                name = row["name"]
+                quantity = row["quantity"]
+                lower = row["lower"]
+                upper = row["upper"]
+                tracers[name] = cls(name, quantity=quantity, lower=lower, upper=upper)
+        return tracers
+
+         
+class BinRichnessTracer(BaseTracer, tracer_type="bin_richness"):  # type: ignore
+    """A tracer for a single richness bin. The tracer shall
+    be used for binned data where we want a desired quantity
+    per interval of log(richness), such that we only need the data
+    for a given interval instead of at individual richness."""
+
+    def __eq__(self, other) -> bool:
+        """Test for equality. If :python:`other` is not a
+        :python:`BinRichnessTracer`, then it is not equal to :python:`self`.
+        Otherwise, they are equal if names and the richness-range of the
+        bins, are equal."""
+        if not isinstance(other, BinRichnessTracer):
+            return False
+        return (
+            self.name == other.name
+            and self.lower == other.lower
+            and self.upper == other.upper
+        )
+
+    def __init__(self, name: str, lower: float, upper: float, **kwargs):
+        """
+        Create a tracer corresponding to a single richness bin.
+
+        :param name: The name of the tracer
+        :param lower: The lower bound of the richness bin in log10.
+        :param upper: The upper bound of the richness bin in log10.
+        """
+        super().__init__(name, **kwargs)
+        self.lower = lower
+        self.upper = upper
+
+    @classmethod
+    def to_tables(cls, instance_list):
+        """Convert a list of BinZTracers to a list of astropy tables
+
+        This is used when saving data to a file.
+        One table is generated with the information for all the tracers.
+
+        :param instance_list: List of tracer instances
+        :return: List with a single astropy table
+        """
+        names = ["name", "quantity", "lower", "upper"]
+
+        cols = [
+            [obj.name for obj in instance_list],
+            [obj.quantity for obj in instance_list],
+            [obj.lower for obj in instance_list],
+            [obj.upper for obj in instance_list],
+        ]
+
+        table = Table(data=cols, names=names)
+        table.meta["SACCTYPE"] = "tracer"
+        table.meta["SACCCLSS"] = cls.tracer_type
+        table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}"
+        return [table]
+
+    @classmethod
+    def from_tables(cls, table_list):
+        """Convert an astropy table into a dictionary of tracers
+
+        This is used when loading data from a file.
+        One tracer object is created for each "row" in each table.
+
+        :param table_list: List of astropy tables
+        :return: Dictionary of tracers
+        """
+        tracers = {}
+
+        for table in table_list:
+            for row in table:
+                name = row["name"]
+                quantity = row["quantity"]
+                lower = row["lower"]
+                upper = row["upper"]
+                tracers[name] = cls(
+                    name,
+                    quantity=quantity,
+                    lower=lower,
+                    upper=upper,
+                )
+        return tracers
+
+
+class SurveyTracer(BaseTracer, tracer_type="survey"):  # type: ignore
+    """A tracer for the survey definition. It shall 
+    be used to filter data related to a given survey
+    and to provide the survey sky-area of analysis."""
+
+    def __eq__(self, other) -> bool:
+        """Test for equality. If :python:`other` is not a
+        :python:`SurveyTracer`, then it is not equal to :python:`self`.
+        Otherwise, they are equal if names and the sky-areas are equal."""
+        if not isinstance(other, SurveyTracer):
+            return False
+        return self.name == other.name and self.sky_area == other.sky_area
+
+    def __init__(self, name: str, sky_area: float, **kwargs):
+        """
+        Create a tracer corresponding to the survey definition.
+
+        :param name: The name of the tracer
+        :param sky_area: The survey's sky area in square degrees
+        """
+        super().__init__(name, **kwargs)
+        self.sky_area = sky_area
+
+    @classmethod
+    def to_tables(cls, instance_list):
+        """Convert a list of SurveyTracer to a list of astropy tables
+
+        This is used when saving data to a file.
+        One table is generated with the information for all the tracers.
+
+        :param instance_list: List of tracer instances
+        :return: List of astropy tables with one table
+        """
+        names = ["name", "quantity", "sky_area"]
+
+        cols = [
+            [obj.name for obj in instance_list],
+            [obj.quantity for obj in instance_list],
+            [obj.sky_area for obj in instance_list],
+        ]
+
+        table = Table(data=cols, names=names)
+        table.meta["SACCTYPE"] = "tracer"
+        table.meta["SACCCLSS"] = cls.tracer_type
+        table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}"
+        return [table]
+
+    @classmethod
+    def from_tables(cls, table_list):
+        """Convert an astropy table into a dictionary of tracers
+
+        This is used when loading data from a file.
+        One tracer object is created for each "row" in each table.
+
+        :param table_list: List of astropy tables
+        :return: Dictionary of tracers
+        """
+        tracers = {}
+
+        for table in table_list:
+            for row in table:
+                name = row["name"]
+                quantity = row["quantity"]
+                sky_area = row["sky_area"]
+                tracers[name] = cls(
+                    name,
+                    quantity=quantity,
+                    sky_area=sky_area,
+                )
+        return tracers


Test patch:
diff --git a/test/test_cluster_data_tracers.py b/test/test_cluster_data_tracers.py
new file mode 100644
index 0000000..fe6b296
--- /dev/null
+++ b/test/test_cluster_data_tracers.py
@@ -0,0 +1,148 @@
+"""
+Tests for function supporting SACC.
+
+"""
+
+from sacc.tracers import (
+    BinZTracer,
+    BinLogMTracer,
+    BinRichnessTracer,
+    SurveyTracer,
+)
+
+
+def test_make_binztracer():
+    tracer = BinZTracer.make("bin_z", name="fred", lower=0.5, upper=1.0)
+    assert isinstance(tracer, BinZTracer)
+    assert tracer.quantity == "generic"
+    assert tracer.name == "fred"
+    assert tracer.lower == 0.5
+    assert tracer.upper == 1.0
+
+
+def test_binztracer_equality():
+    a = BinZTracer.make("bin_z", name="fred", lower=0.5, upper=1.0)
+    b = BinZTracer.make("bin_z", name="fred", lower=0.5, upper=1.0)
+    c = BinZTracer.make("bin_z", name="wilma", lower=0.5, upper=1.0)
+    d = BinZTracer.make("bin_z", name="fred", lower=0.6, upper=1.0)
+    e = BinZTracer.make("bin_z", name="fred", lower=0.5, upper=1.1)
+    assert a == b
+    assert a != "fred"
+    assert a != c
+    assert a != d
+    assert a != e
+
+
+def test_binztracer_tables():
+    a = BinZTracer.make("bin_z", name="fred", lower=0.5, upper=1.0)
+    b = BinZTracer.make("bin_z", name="wilma", lower=1.0, upper=1.5)
+    tables = BinZTracer.to_tables([a, b])
+    assert len(tables) == 1  # all BinZTracers are written to a single table
+
+    d = BinZTracer.from_tables(tables)
+    assert len(d) == 2  # this list of tables recovers both BinZTracers
+    assert d["fred"] == a
+    assert d["wilma"] == b
+
+
+def test_make_binlogmtracer():
+    tracer = BinLogMTracer.make("bin_logm", name="fred", lower=13.0, upper=15.0)
+    assert isinstance(tracer, BinLogMTracer)
+    assert tracer.quantity == "generic"
+    assert tracer.name == "fred"
+    assert tracer.lower == 13.0
+    assert tracer.upper == 15.0
+
+
+def test_binlogmtracer_equality():
+    a = BinLogMTracer.make("bin_logm", name="fred", lower=13.0, upper=15.0)
+    b = BinLogMTracer.make("bin_logm", name="fred", lower=13.0, upper=15.0)
+    c = BinLogMTracer.make("bin_logm", name="wilma", lower=13.0, upper=15.0)
+    d = BinLogMTracer.make("bin_logm", name="fred", lower=14.0, upper=15.0)
+    e = BinLogMTracer.make("bin_logm", name="fred", lower=13.0, upper=15.1)
+    assert a == b
+    assert a != "fred"
+    assert a != c
+    assert a != d
+    assert a != e
+
+
+def test_binlogmtracer_tables():
+    a = BinLogMTracer.make("bin_logm", name="fred", lower=13.0, upper=15.0)
+    b = BinLogMTracer.make("bin_logm", name="wilma", lower=14.0, upper=15.5)
+    tables = BinLogMTracer.to_tables([a, b])
+    assert len(tables) == 1
+
+    d = BinLogMTracer.from_tables(tables)
+    assert len(d) == 2
+    assert d["fred"] == a
+    assert d["wilma"] == b
+
+
+def test_make_binrichness_tracer():
+    tracer = BinRichnessTracer.make(
+        "bin_richness",
+        name="barney",
+        lower=0.25,
+        upper=1.0,
+    )
+    assert isinstance(tracer, BinRichnessTracer)
+    assert tracer.quantity == "generic"
+    assert tracer.name == "barney"
+    assert tracer.upper == 1.0
+    assert tracer.lower == 0.25
+
+
+def test_binrichnesstracer_equality():
+    a = BinRichnessTracer.make("bin_richness", name="fred", lower=0.5, upper=1.0)
+    b = BinRichnessTracer.make("bin_richness", name="fred", lower=0.5, upper=1.0)
+    c = BinRichnessTracer.make("bin_richness", name="wilma", lower=0.5, upper=1.0)
+    d = BinRichnessTracer.make("bin_richness", name="fred", lower=0.6, upper=1.0)
+    e = BinRichnessTracer.make("bin_richness", name="fred", lower=0.5, upper=1.1)
+    assert a == b
+    assert a != "fred"
+    assert a != c
+    assert a != d
+    assert a != e
+
+
+def test_binrichnesstracer_tables():
+    a = BinRichnessTracer.make("bin_richness", name="barney", lower=0.0, upper=0.5)
+    b = BinRichnessTracer.make("bin_richness", name="betty", lower=1.25, upper=2.0)
+    tables = BinRichnessTracer.to_tables([a, b])
+    assert len(tables) == 1
+    d = BinRichnessTracer.from_tables(tables)
+    assert len(d) == 2  # this list of tables recovers both BinRichnessTracers
+    assert d["barney"] == a
+    assert d["betty"] == b
+
+
+def test_make_surveytracer():
+    tracer = SurveyTracer.make("survey", name="bullwinkle", sky_area=1.0)
+    assert isinstance(tracer, SurveyTracer)
+    assert tracer.quantity == "generic"
+    assert tracer.name == "bullwinkle"
+    assert tracer.sky_area == 1.0
+
+
+def test_surveytracer_equality():
+    a = SurveyTracer.make("survey", name="bullwinkle", sky_area=1.0)
+    b = SurveyTracer.make("survey", name="bullwinkle", sky_area=1.0)
+    c = SurveyTracer.make("survey", name="rocky", sky_area=1.0)
+    d = SurveyTracer.make("survey", name="boris", sky_area=2.0)
+
+    assert a == b
+    assert a != "bullwinkle"
+    assert a != c
+    assert a != d
+
+
+def test_surveytracer_tables():
+    a = SurveyTracer.make("survey", name="bullwinkle", sky_area=1.0)
+    b = SurveyTracer.make("survey", name="rocky", sky_area=2.0)
+    tables = SurveyTracer.to_tables([a, b])
+    assert len(tables) == 1
+    d = SurveyTracer.from_tables(tables)
+    assert len(d) == 2
+    assert d["bullwinkle"] == a
+    assert d["rocky"] == b