Skip to content

Commit 57c846b

Browse files
committed
FIx two docs issues (one with surviving mass calculation, one with noise modelling example). Very minor change to noise modelling plotting to make the labelling more appropriate.
1 parent 338e9da commit 57c846b

File tree

3 files changed

+86
-15
lines changed

3 files changed

+86
-15
lines changed

docs/source/library_gen/complex_library_generation.ipynb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
" calculate_mass_weighted_age,\n",
6161
" calculate_muv,\n",
6262
" calculate_sfh_quantile,\n",
63-
" calculate_surviving_mass,\n",
6463
" draw_from_hypercube,\n",
6564
" generate_random_DB_sfh,\n",
6665
")"
@@ -479,7 +478,6 @@
479478
" \"sfh_quant_75\": (calculate_sfh_quantile, 0.75, True), # Calculate SFH quantile at 75%\n",
480479
" \"UV\": (calculate_colour, \"U\", \"V\", emission_key, True), # Calculate UV colour (rest-frame)\n",
481480
" \"VJ\": (calculate_colour, \"V\", \"J\", emission_key, True), # Calculate VJ colour (rest-frame)\n",
482-
" \"log_surviving_mass\": (calculate_surviving_mass, grid), # Calculate surviving mass\n",
483481
" \"d4000\": (calculate_d4000, emission_key), # Calculate D4000 using the emission model\n",
484482
" \"beta\": (calculate_beta, emission_key),\n",
485483
" \"balmer_decrement\": (calculate_balmer_decrement, emission_key),\n",

docs/source/noise_modelling/noise_models.ipynb

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"source": [
3838
"import matplotlib.pyplot as plt\n",
3939
"import numpy as np\n",
40-
"from unyt import Jy, nJy\n",
40+
"from unyt import nJy\n",
4141
"\n",
4242
"from synference import DepthUncertaintyModel\n",
4343
"\n",
@@ -158,7 +158,9 @@
158158
"source": [
159159
"This noise model is implemented in the ```AsinhEmpiricalUncertaintyModel``` class. It works similarly to the ```GeneralEmpiricalUncertaintyModel```, but applies the asinh transformation to the fluxes before fitting the noise model. It only excepts input fluxes in Jy units, and whilst the ```apply_noise``` method will accept the ```out_flux_unit``` argument for consistency with other noise models, the output fluxes will always be in asinh magnitudes.\n",
160160
"\n",
161-
"For this model, the asinh softening parameter is not set directly, but in multiples of the median standard deviation of the flux uncertainties in the training data. This is set using the ```f_b_factor``` argument when initializing the model. For example, setting ```f_b_factor=1.0``` will set the softening parameter to the median flux uncertainty, while ```f_b_factor=2.0``` will set it to twice the median flux uncertainty. By default, ```f_b_factor=5.0```, so the softening parameter is set to five times the median flux uncertainty (aka the $5\\sigma$ detection limit)."
161+
"For this model, the asinh softening parameter is not set directly, but in multiples of the median standard deviation of the flux uncertainties in the training data. This is set using the ```f_b_factor``` argument when initializing the model. For example, setting ```f_b_factor=1.0``` will set the softening parameter to the median flux uncertainty, while ```f_b_factor=2.0``` will set it to twice the median flux uncertainty. By default, ```f_b_factor=5.0```, so the softening parameter is set to five times the median flux uncertainty (aka the $5\\sigma$ detection limit).\n",
162+
"\n",
163+
"For this example we setup a more realstic scenario, with a catalogue of 50,000 fake sources with a fixed background level, a fractioanl error and an overall lognormal flux distribution."
162164
]
163165
},
164166
{
@@ -169,19 +171,86 @@
169171
"outputs": [],
170172
"source": [
171173
"from synference import AsinhEmpiricalUncertaintyModel\n",
174+
"from synference.utils import f_jy_to_asinh\n",
175+
"\n",
176+
"# --- 1. Define Realistic Survey Parameters ---\n",
177+
"N_SOURCES: int = 50_000\n",
178+
"\n",
179+
"# Background noise limit (e.g., 100 uJy).\n",
180+
"# This dominates the error for faint sources.\n",
181+
"BACKGROUND_NOISE_JY: float = 0.0001\n",
182+
"\n",
183+
"# Fractional/calibration error (e.g., 2%).\n",
184+
"# This dominates the error for bright sources.\n",
185+
"FRACTIONAL_ERROR: float = 0.02\n",
186+
"\n",
187+
"# Log-normal distribution parameters for \"true\" fluxes.\n",
188+
"# We set the median flux to be near the noise floor.\n",
189+
"FLUX_MEDIAN_JY: float = 0.00015\n",
190+
"FLUX_LOG_SIGMA: float = 1.5 # Width of the log-normal distribution\n",
191+
"\n",
192+
"# --- 2. Generate \"True\" Fluxes ---\n",
193+
"# Use a log-normal distribution: many faint sources, few bright ones\n",
194+
"true_flux_jy = np.random.lognormal(\n",
195+
" mean=np.log(FLUX_MEDIAN_JY), sigma=FLUX_LOG_SIGMA, size=N_SOURCES\n",
196+
")\n",
197+
"\n",
198+
"# --- 3. Calculate \"Ideal\" Error for Each True Flux ---\n",
199+
"# This is the characteristic error model: sqrt(bg_noise^2 + (frac_err * flux)^2)\n",
200+
"ideal_error_jy = np.sqrt(BACKGROUND_NOISE_JY**2 + (FRACTIONAL_ERROR * true_flux_jy) ** 2)\n",
201+
"\n",
202+
"# --- 4. Simulate \"Observed\" Fluxes by Scattering by the Error ---\n",
203+
"# The observed flux is the true flux plus Gaussian noise\n",
204+
"observed_flux_jy = true_flux_jy + np.random.normal(loc=0.0, scale=ideal_error_jy, size=N_SOURCES)\n",
172205
"\n",
173-
"# Generate mock data\n",
174-
"fluxes = np.random.uniform(0.1, 100, size=10_000) * Jy\n",
175-
"errors = np.random.normal(0.0, 5.0, size=10_000) * Jy\n",
206+
"# The \"observed error\" is the error the pipeline *reports*.\n",
207+
"# We assume the pipeline correctly estimates the ideal error.\n",
208+
"observed_error_jy = ideal_error_jy\n",
176209
"\n",
210+
"# Plot a flux histogram\n",
211+
"plt.hist(true_flux_jy, label=\"Fluxes\", bins=100)\n",
212+
"plt.yscale(\"log\")\n",
213+
"plt.xlabel(\"Flux [Jy]\")"
214+
]
215+
},
216+
{
217+
"cell_type": "markdown",
218+
"id": "5dc934bb",
219+
"metadata": {},
220+
"source": [
221+
"From this realistic dataset we can generate our `AsinhEmpiricalUncertaintyModel`. Note that some datapoints fall below the softening parameter - these are originally negative fluxes which can be represented by asinh magnitudes."
222+
]
223+
},
224+
{
225+
"cell_type": "code",
226+
"execution_count": null,
227+
"id": "2db14a1e",
228+
"metadata": {},
229+
"outputs": [],
230+
"source": [
177231
"noise_model = AsinhEmpiricalUncertaintyModel(\n",
178-
" observed_phot_jy=fluxes,\n",
179-
" observed_phot_errors_jy=errors,\n",
232+
" observed_phot_jy=observed_flux_jy,\n",
233+
" observed_phot_errors_jy=observed_error_jy,\n",
180234
")\n",
181235
"\n",
182236
"print(noise_model.b)\n",
183237
"\n",
184-
"noise_model.plot()"
238+
"# plot noise_model.b\n",
239+
"fig, ax = plt.subplots()\n",
240+
"noise_model.plot(ax=ax)\n",
241+
"\n",
242+
"\n",
243+
"plt.axvline(f_jy_to_asinh(noise_model.b))"
244+
]
245+
},
246+
{
247+
"cell_type": "code",
248+
"execution_count": null,
249+
"id": "9b18c966",
250+
"metadata": {},
251+
"outputs": [],
252+
"source": [
253+
"?truncnorm"
185254
]
186255
},
187256
{

src/synference/noise_models.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ def _from_hdf5_group(cls, hdf5_group: h5py.Group) -> "DepthUncertaintyModel":
210210

211211
class SpectralUncertaintyModel(UncertaintyModel):
212212
"""Applies uncertanties to a spectrum based on a fixed error kernel or a provided table."""
213+
213214
def __init__(self, error_kernel: np.ndarray, **kwargs: Any):
214215
"""Initializes the model with a fixed error kernel.
215216
@@ -240,7 +241,7 @@ def apply_noise(
240241
return noisy_flux, self.error_kernel
241242

242243
return noisy_flux
243-
244+
244245
def serialize_to_hdf5(self, hdf5_group: h5py.Group):
245246
"""Serializes the model to an HDF5 group."""
246247
attrs = hdf5_group.attrs
@@ -256,7 +257,6 @@ def _from_hdf5_group(cls, hdf5_group: h5py.Group) -> "SpectralUncertaintyModel":
256257
error_kernel=error_kernel,
257258
return_noise=hdf5_group.attrs["return_noise"],
258259
)
259-
260260

261261

262262
class EmpiricalUncertaintyModel(UncertaintyModel, ABC):
@@ -321,7 +321,7 @@ def _compute_bins_from_data(
321321

322322
def plot(self, ax: Optional[plt.Axes] = None):
323323
"""Plots the binned median error and standard deviation."""
324-
fig, ax = plt.subplots() if ax is None else (None, ax)
324+
fig, ax = plt.subplots() if ax is None else (ax.get_figure(), ax)
325325
if self.bin_centers is None or len(self.bin_centers) < 2:
326326
raise AttributeError("Binned data not found. Cannot plot.")
327327

@@ -335,10 +335,14 @@ def plot(self, ax: Optional[plt.Axes] = None):
335335
alpha=0.7,
336336
)
337337

338-
ax.set_xlabel("Flux")
338+
f = "Flux"
339+
if isinstance(self, AsinhEmpiricalUncertaintyModel):
340+
f = "Mag [asinh]"
341+
342+
ax.set_xlabel(f)
339343
ax.set_ylabel("Error")
340344
ax.legend()
341-
plt.show()
345+
return fig
342346

343347
def _create_interpolators(self):
344348
if self.bin_centers is None or len(self.bin_centers) < 2:

0 commit comments

Comments
 (0)