Skip to content
This repository was archived by the owner on May 11, 2026. It is now read-only.
Merged

Dev #61

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
094daa0
feat: added power_law and straight_line models. Implemented fitting r…
zairving Oct 29, 2025
42fa722
feat: RMS-flux relations are plotted after performing photometry to i…
zairving Oct 29, 2025
e135966
misc: refactored compute_optimal_flux() method of OptimalPhotometer
zairving Oct 29, 2025
a84e209
docs: incremented version number
zairving Oct 29, 2025
a9c050d
misc: refactored some code in AperturePhotometer class
zairving Oct 30, 2025
5e5f3e6
misc: removed unused imports from plots.py
zairving Oct 30, 2025
efff606
misc: added additional header rows to generated data for noise calcul…
zairving Oct 31, 2025
8e0be0f
feat: noise characterisation plots are not generated during catalogue…
zairving Oct 31, 2025
a49fe59
feat: added iterative RMS--flux fitting to account for outliers
zairving Oct 31, 2025
c5bd3e0
misc: several improvements to data generation routines, including def…
zairving Nov 1, 2025
4c57451
misc: improved plotting for noise characterisation
zairving Nov 1, 2025
73a210d
bug: fixed issue in generate.py that was causing sources to exhibit u…
zairving Nov 3, 2025
71e7065
feat: added S/N plot and added error band to RMS-flux fit
zairving Nov 3, 2025
3b72095
misc: moved noise.py from opticam.correctors.noise to opticam.noise
zairving Nov 3, 2025
dc97d50
feat: RMS-flux fits now include error bands calculated using a robust…
zairving Nov 3, 2025
92a45eb
feat: S/N plot is now generated for catalogued sources as part of Red…
zairving Nov 3, 2025
4583213
bug: fixed issue with PSF parameter logging and moved it into a dedic…
zairving Nov 4, 2025
8a93648
docs: docstring improvements
zairving Nov 4, 2025
9eced8d
misc: added scaling factor for converting from counts to magnitudes t…
zairving Nov 4, 2025
dd7fe1c
bug: fixed bug in plot_rms_vs_median_flux() that was preventing varia…
zairving Nov 4, 2025
25b12b5
misc: fit_rms_vs_flux() model error is now given to 5 sigma instead of 1
zairving Nov 4, 2025
7ba4a89
misc: changed handling of local background estimators when performing…
zairving Nov 4, 2025
ea4fb3b
docs: updated tutorials
zairving Nov 4, 2025
976149a
misc: removed return_error parameter from get_data()
zairving Nov 4, 2025
bdfb651
misc: removed error parameter from __call__() method of BaseLocalBack…
zairving Nov 4, 2025
c5f3251
misc: removed return_error parameter from get_data() calls
zairving Nov 4, 2025
0922c1d
feat: changed how noise is propagated to match behaviour of SExtractor
zairving Nov 4, 2025
7e89e5a
misc: changed how noise is propagated to match behaviour of SExtractor
zairving Nov 4, 2025
d0e3e0f
misc: reformatted some functions
zairving Nov 4, 2025
6ebe12c
misc: increased base counts for flat-field images to better mimic rea…
zairving Nov 4, 2025
9ad9bbf
docs: updated tutorials
zairving Nov 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 220 additions & 28 deletions docs/tutorials/applying_corrections.ipynb

Large diffs are not rendered by default.

49 changes: 36 additions & 13 deletions docs/tutorials/backgrounds.ipynb

Large diffs are not rendered by default.

67 changes: 56 additions & 11 deletions docs/tutorials/finders.ipynb

Large diffs are not rendered by default.

281 changes: 161 additions & 120 deletions docs/tutorials/local_backgrounds.ipynb

Large diffs are not rendered by default.

126 changes: 92 additions & 34 deletions docs/tutorials/reduction.ipynb

Large diffs are not rendered by default.

170 changes: 81 additions & 89 deletions docs/tutorials/timing_methods.ipynb

Large diffs are not rendered by default.

12 changes: 5 additions & 7 deletions opticam/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,11 @@ def align_batch(
background_rmss = {}

for file in batch:
data = np.asarray(
get_data(
file,
flat_corrector=flat_corrector,
rebin_factor=rebin_factor,
return_error=False,
remove_cosmic_rays=remove_cosmic_rays),
data = get_data(
file,
flat_corrector=flat_corrector,
rebin_factor=rebin_factor,
remove_cosmic_rays=remove_cosmic_rays,
)

# calculate and subtract background
Expand Down
10 changes: 8 additions & 2 deletions opticam/background/global_background.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ class BaseBackground(ABC):
Base class for OPTICAM background estimators.
"""

def __init__(self, box_size: int | Tuple[int, int]):
def __init__(
self,
box_size: int | Tuple[int, int],
):
"""
Initialize a background estimator.

Expand All @@ -28,7 +31,10 @@ def __init__(self, box_size: int | Tuple[int, int]):
self.box_size = box_size

@abstractmethod
def __call__(self, image: NDArray) -> Background2D:
def __call__(
self,
image: NDArray,
) -> Background2D:
"""
Compute the 2D background for an image.

Expand Down
5 changes: 0 additions & 5 deletions opticam/background/local_background.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def __init__(
def __call__(
self,
data: NDArray,
error: NDArray,
position: NDArray,
semimajor_axis: float,
semiminor_axis: float,
Expand All @@ -51,8 +50,6 @@ def __call__(
----------
data : NDArray
The image data.
error : NDArray
The error in the image data.
semimajor_axis : float
The (unscaled) semi-major axis of the aperture.
semiminor_axis : float
Expand All @@ -79,7 +76,6 @@ class DefaultLocalBackground(BaseLocalBackground):
def __call__(
self,
data: NDArray,
error: NDArray,
position: NDArray,
semimajor_axis: float,
semiminor_axis: float | None = None,
Expand Down Expand Up @@ -125,7 +121,6 @@ def __call__(
stats = ApertureStats(
data,
annulus,
error=error,
sigma_clip=self.sigma_clip,
)

Expand Down
File renamed without changes.
83 changes: 83 additions & 0 deletions opticam/fitting/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import numpy as np
from numpy.typing import NDArray


def gaussian(
x: NDArray,
amp: float,
mu: float,
std: float,
) -> NDArray:
"""
Gaussian function.

Parameters
----------
x : NDArray
The function inputs.
amplitude : float
The height of the function peak.
mu : float
The mean of the Gaussian.
std : float
The standard deviation of the Gaussian.

Returns
-------
NDArray
The Gaussian evaluated at the inputs.
"""

return amp * np.exp(-.5 * ((x - mu) / std)**2)


def power_law(
x: NDArray,
norm: float,
exp: float,
) -> NDArray:
"""
Power law function.

Parameters
----------
x : NDArray
The function inputs.
norm : float
The normalisation.
exp : float
The exponent.

Returns
-------
NDArray
The power law evaluated at the inputs.
"""

return norm*x**exp


def straight_line(
x: NDArray,
m: float,
c: float,
) -> NDArray:
"""
Straight line function.

Parameters
----------
x : NDArray
The function inputs.
m : float
The gradient.
c : float
The intercept.

Returns
-------
NDArray
The straight line evaluated at the inputs.
"""

return m*x + c
96 changes: 96 additions & 0 deletions opticam/fitting/routines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from typing import Dict

import numpy as np
from numpy.typing import NDArray
from scipy.optimize import curve_fit

from opticam.fitting.models import power_law, straight_line

def fit_rms_vs_flux(
data: Dict,
) -> Dict[str, Dict[str, NDArray]]:
"""
Iteratively fit a straight line (in log space) to the RMS vs flux plots for each catalog. This can be used to
identify variable sources and good comparison sources.

Parameters
----------
data : Dict
The RMS vs flux data.

Returns
-------
Dict[str, Dict[str, NDArray]]
The power law fits for each filter `{filter: {'flux': NDArray, 'rms': NDArray}}`.
"""

pl_fits = {}

for fltr in data.keys():
rms, flux = [], []
for values in data[fltr].values():
rms.append(values['rms'])
flux.append(values['flux'])

order = np.argsort(flux)
x = np.array(flux)[order]
y = np.array(rms)[order]

try:
converged = False
prev, prev_err = None, None
while not converged:
log_x = np.log10(flux)
log_y = np.log10(rms)

popt, pcov = curve_fit(
straight_line,
log_x,
log_y,
)
perr = np.sqrt(np.diag(pcov))

if prev is not None and prev_err is not None:
# assume fit has converged is params are within 20%
# do not use perr since it may be very large
converged = np.allclose(popt, prev, rtol=0.2, atol=0)

# remove largest outliers
model = straight_line(log_x, *popt)
r = log_y - model
i = np.argmax(r)

rms.pop(i)
flux.pop(i)

prev = popt
prev_err = perr
except:
popt, pcov = curve_fit(
straight_line,
np.log10(x),
np.log10(y),
)

# get model prediction band using Monte Carlo method
N = 1000
y_models = np.zeros((N, x.size))
for i in range(N):
rng = np.random.default_rng(i)
a, b = rng.multivariate_normal(popt, pcov)
y_models[i] += power_law(x, 10**a, b)
y_model_err = 5 * np.std(y_models, axis=0) # 5 sigma error

y_model = power_law(
x,
10**popt[1],
popt[0],
)

pl_fits[fltr] = {
'flux': x,
'rms': y_model,
'err': y_model_err,
}

return pl_fits
31 changes: 0 additions & 31 deletions opticam/models/fittable_models.py

This file was deleted.

Loading
Loading