teareduce 0.2.5__tar.gz → 0.3.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {teareduce-0.2.5/src/teareduce.egg-info → teareduce-0.3.5}/PKG-INFO +2 -1
  2. {teareduce-0.2.5 → teareduce-0.3.5}/pyproject.toml +2 -1
  3. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/__init__.py +2 -0
  4. teareduce-0.3.5/src/teareduce/correct_pincushion_distortion.py +60 -0
  5. teareduce-0.3.5/src/teareduce/numsplines.py +232 -0
  6. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/sdistortion.py +3 -1
  7. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/version.py +1 -1
  8. {teareduce-0.2.5 → teareduce-0.3.5/src/teareduce.egg-info}/PKG-INFO +2 -1
  9. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce.egg-info/SOURCES.txt +2 -0
  10. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce.egg-info/requires.txt +1 -0
  11. {teareduce-0.2.5 → teareduce-0.3.5}/LICENSE.txt +0 -0
  12. {teareduce-0.2.5 → teareduce-0.3.5}/README.md +0 -0
  13. {teareduce-0.2.5 → teareduce-0.3.5}/setup.cfg +0 -0
  14. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/avoid_astropy_warnings.py +0 -0
  15. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/cosmicrays.py +0 -0
  16. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/ctext.py +0 -0
  17. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/draw_rectangle.py +0 -0
  18. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/elapsed_time.py +0 -0
  19. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/imshow.py +0 -0
  20. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/peaks_spectrum.py +0 -0
  21. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/polfit.py +0 -0
  22. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/robust_std.py +0 -0
  23. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/sliceregion.py +0 -0
  24. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/statsummary.py +0 -0
  25. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/wavecal.py +0 -0
  26. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce/zscale.py +0 -0
  27. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce.egg-info/dependency_links.txt +0 -0
  28. {teareduce-0.2.5 → teareduce-0.3.5}/src/teareduce.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: teareduce
3
- Version: 0.2.5
3
+ Version: 0.3.5
4
4
  Summary: Utilities for astronomical data reduction
5
5
  Author-email: Nicolás Cardiel <cardiel@ucm.es>
6
6
  License: GPL-3.0-or-later
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
22
22
  License-File: LICENSE.txt
23
23
  Requires-Dist: astropy
24
24
  Requires-Dist: importlib_resources
25
+ Requires-Dist: lmfit
25
26
  Requires-Dist: matplotlib
26
27
  Requires-Dist: numpy>=1.20
27
28
  Requires-Dist: scipy
@@ -3,7 +3,7 @@
3
3
  # (include packages imported in the different modules; otherwise
4
4
  # a ModuleNotFoundError is raised when using $ pip install -e .)
5
5
  requires = ["setuptools >= 43.0.0", "wheel", "numpy",
6
- "astropy", "matplotlib", "scipy", "tqdm"]
6
+ "astropy", "matplotlib", "scipy", "lmfit", "tqdm"]
7
7
  build-backend = "setuptools.build_meta"
8
8
 
9
9
  [project]
@@ -33,6 +33,7 @@ classifiers = [
33
33
  dependencies = [
34
34
  "astropy",
35
35
  "importlib_resources", # required with python < 3.9
36
+ "lmfit",
36
37
  "matplotlib",
37
38
  "numpy >= 1.20",
38
39
  "scipy",
@@ -8,11 +8,13 @@
8
8
  #
9
9
 
10
10
  from .avoid_astropy_warnings import avoid_astropy_warnings
11
+ from .correct_pincushion_distortion import correct_pincushion_distortion
11
12
  from .cosmicrays import cr2images, apply_cr2images_ccddata, crmedian
12
13
  from .ctext import ctext
13
14
  from .draw_rectangle import draw_rectangle
14
15
  from .elapsed_time import elapsed_time
15
16
  from .imshow import imshow
17
+ from .numsplines import AdaptiveLSQUnivariateSpline
16
18
  from .peaks_spectrum import find_peaks_spectrum, refine_peaks_spectrum
17
19
  from .polfit import polfit_residuals, polfit_residuals_with_sigma_rejection
18
20
  from .robust_std import robust_std
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright 2024 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0+
7
+ # License-Filename: LICENSE.txt
8
+ #
9
+
10
+ from astropy.io import fits
11
+ import numpy as np
12
+ from numpy.polynomial import Polynomial
13
+
14
+
15
+ def correct_pincushion_distortion(coeff_filename, data):
16
+ """Correct pincushion distortion.
17
+
18
+ Parameters
19
+ ----------
20
+ coeff_filename : str
21
+ Name of the FITS file containing the polynomial coefficients
22
+ of the transformation between the Y-coordinates of the central
23
+ image column and the measured Y-coordinates (for each image
24
+ column).
25
+ data : `~numpy.ndarray`
26
+ Array containing the image to be corrected.
27
+
28
+ Returns
29
+ -------
30
+ data_rectified : `~numpy.ndarray`
31
+ Rectified image.
32
+ """
33
+
34
+ # read FITS file with polynomial coefficients
35
+
36
+ with fits.open(coeff_filename) as hdul:
37
+ table = hdul[1].data
38
+
39
+ # check dimensions
40
+ naxis2, naxis1 = data.shape
41
+ if naxis1 != len(table):
42
+ raise ValueError(f'Incompatible dimensions: naxis1:{naxis1} != len(table): {len(table)}')
43
+
44
+ # rectify image
45
+ accum_flux = np.zeros((naxis2 + 1, naxis1))
46
+ accum_flux[1:, :] = np.cumsum(data, axis=0)
47
+ new_y_borders = np.arange(naxis2 + 1) - 0.5
48
+ data_rectified = np.zeros((naxis2, naxis1))
49
+ for i in range(naxis1):
50
+ poly = Polynomial(coef=table[i])
51
+ flux_borders = np.interp(
52
+ x=new_y_borders,
53
+ xp=poly(new_y_borders),
54
+ fp=accum_flux[:, i],
55
+ left=0,
56
+ right=accum_flux[-1, i]
57
+ )
58
+ data_rectified[:, i] = flux_borders[1:] - flux_borders[:-1]
59
+
60
+ return data_rectified
@@ -0,0 +1,232 @@
1
+ #
2
+ # Copyright 2019-2024 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0-or-later
7
+ # License-Filename: LICENSE.txt
8
+ #
9
+
10
+ """Numerical spline fit using different strategies."""
11
+
12
+ from lmfit import Minimizer, Parameters
13
+ import numpy as np
14
+ from scipy.interpolate import LSQUnivariateSpline
15
+
16
+
17
+ def fun_residuals(params, xnor, ynor, w, bbox, k, ext):
18
+ """Compute fit residuals"""
19
+
20
+ spl = LSQUnivariateSpline(
21
+ x=xnor,
22
+ y=ynor,
23
+ t=[item.value for item in params.values()],
24
+ w=w,
25
+ bbox=bbox,
26
+ k=k,
27
+ ext=ext,
28
+ check_finite=False
29
+ )
30
+ return spl.get_residual()
31
+
32
+
33
+ class AdaptiveLSQUnivariateSpline(LSQUnivariateSpline):
34
+ """Extend scipy.interpolate.LSQUnivariateSpline.
35
+
36
+ One-dimensional spline with explicit internal knots.
37
+
38
+ This is actually a wrapper of
39
+ `scipy.interpolate.LSQUnivariateSpline`
40
+ with the addition of using adaptive knot location determined
41
+ numerically (after normalising the x and y arrays before
42
+ the minimisation process).
43
+
44
+ Parameters
45
+ ----------
46
+ x : (N,) array_like
47
+ Input dimension of data points -- must be increasing
48
+ y : (N,) array_like
49
+ Input dimension of data points
50
+ t : (M,) array_like or int
51
+ When integer it indicates the number of equidistant
52
+ interior knots. When array_like it provides the location
53
+ of the interior knots of the spline; must be in ascending
54
+ order and::
55
+
56
+ bbox[0] < t[0] < ... < t[-1] < bbox[-1]
57
+
58
+ w : (N,) array_like, optional
59
+ weights for spline fitting. Must be positive.
60
+ If None (default), weights are all equal.
61
+ bbox : (2,) array_like, optional
62
+ 2-sequence specifying the boundary of the approximation
63
+ interval. If None (default), ``bbox = [x[0], x[-1]]``.
64
+ k : int, optional
65
+ Degree of the smoothing spline. Must be 1 <= `k` <= 5.
66
+ Default is k=3, a cubic spline.
67
+ ext : int or str, optional
68
+ Controls the extrapolation mode for elements
69
+ not in the interval defined by the knot sequence.
70
+
71
+ * if ext=0 or 'extrapolate', return the extrapolated value.
72
+ * if ext=1 or 'zeros', return 0
73
+ * if ext=2 or 'raise', raise a ValueError
74
+ * if ext=3 of 'const', return the boundary value.
75
+
76
+ The default value is 0.
77
+
78
+ check_finite : bool, optional
79
+ Whether to check that the input arrays contain only finite
80
+ numbers, that the x array is increasing and that the
81
+ x and y arrays are 1-D and with the same length.
82
+ Disabling may give a performance gain, but may
83
+ result in problems (crashes, non-termination or non-sensical
84
+ results) if the inputs do contain infinities or NaNs.
85
+ Default is True.
86
+ adaptive : bool, optional
87
+ Whether to optimise knot location following the procedure
88
+ described in Cardiel (2009); see:
89
+ http://cdsads.u-strasbg.fr/abs/2009MNRAS.396..680C
90
+ Default is True.
91
+ tolerance : float, optional
92
+ Tolerance for Nelder-Mead minimisation process.
93
+
94
+ Attributes
95
+ ----------
96
+ _params : instance of Parameters()
97
+ Initial parameters before minimisation.
98
+ _result : Minimizer output
99
+ Result of the minimisation process.
100
+
101
+ See also
102
+ --------
103
+ LSQUnivariateSpline : Superclass
104
+
105
+ """
106
+
107
+ def __init__(self, x, y, t, w=None, bbox=(None, None),
108
+ k=3, ext=0, check_finite=False, adaptive=True,
109
+ tolerance=1E-7):
110
+ """One-dimensional spline with explicit internal knots."""
111
+
112
+ if check_finite:
113
+ # check here the arrays instead of in the base class
114
+ # (note that in the call to super(...).__init(...) the
115
+ # parameter check_finite is set to False)
116
+ w_finite = np.isfinite(x).all() if w is not None else True
117
+ if not np.isfinite(x).all() or not np.isfinite(y).all() or \
118
+ not w_finite:
119
+ raise ValueError('Input(s) must not contain '
120
+ 'NaNs or infs.')
121
+
122
+ if np.asarray(x).ndim != 1:
123
+ raise ValueError('x array must have dimension 1')
124
+
125
+ if np.asarray(y).ndim != 1:
126
+ raise ValueError('y array must have dimension 1')
127
+
128
+ if np.asarray(x).shape != np.asarray(y).shape:
129
+ raise ValueError('x and y arrays must have the same length')
130
+
131
+ if not all(np.diff(x) > 0.0):
132
+ raise ValueError('x array must be strictly increasing')
133
+
134
+ # initial inner knot location (equidistant or fixed)
135
+ try:
136
+ nknots = int(t)
137
+ if nknots > 0:
138
+ xmin = x[0]
139
+ xmax = x[-1]
140
+ deltax = (xmax - xmin) / float(nknots + 1)
141
+ xknot = np.zeros(nknots)
142
+ for i in range(nknots):
143
+ xknot[i] = (xmin + float(i + 1) * deltax)
144
+ else:
145
+ xknot = np.array([])
146
+ except (ValueError, TypeError):
147
+ xknot = np.asarray(t)
148
+ if check_finite:
149
+ if not np.isfinite(xknot).all():
150
+ raise ValueError('Interior knots must not contain '
151
+ 'NaNs or infs.')
152
+ if xknot.ndim != 1:
153
+ raise ValueError('t array must have dimension 1')
154
+ nknots = len(xknot)
155
+
156
+ # adaptive knots
157
+ if nknots > 0 and adaptive:
158
+ xknot_backup = xknot.copy()
159
+
160
+ # normalise the x and y arrays to the [-1, +1] interval
161
+ xmin = x[0]
162
+ xmax = x[-1]
163
+ ymin = np.min(y)
164
+ ymax = np.max(y)
165
+ bx = 2.0 / (xmax - xmin)
166
+ cx = (xmin + xmax) / (xmax - xmin)
167
+ by = 2.0 / (ymax - ymin)
168
+ cy = (ymin + ymax) / (ymax - ymin)
169
+ xnor = bx * np.asarray(x) - cx
170
+ ynor = by * np.asarray(y) - cy
171
+ xknotnor = bx * xknot - cx
172
+ params = Parameters()
173
+ for i in range(nknots):
174
+ if i == 0:
175
+ xminknot = bx * x[0] - cx
176
+ xmaxknot = (xknotnor[i] + xknotnor[i+1]) / 2.0
177
+ elif i == nknots - 1:
178
+ xminknot = (xknotnor[i-1] + xknotnor[i]) / 2.0
179
+ xmaxknot = bx * x[-1] - cx
180
+ else:
181
+ xminknot = (xknotnor[i-1] + xknotnor[i]) / 2.0
182
+ xmaxknot = (xknotnor[i] + xknotnor[i+1]) / 2.0
183
+ params.add(
184
+ name=f'xknot{i:03d}',
185
+ value=xknotnor[i],
186
+ min=xminknot,
187
+ max=xmaxknot,
188
+ vary=True
189
+ )
190
+ self._params = params.copy()
191
+ fitter = Minimizer(
192
+ userfcn=fun_residuals,
193
+ params=params,
194
+ fcn_args=(xnor, ynor, w, bbox, k, ext)
195
+ )
196
+ try:
197
+ self._result = fitter.scalar_minimize(
198
+ method='Nelder-Mead',
199
+ tol=tolerance
200
+ )
201
+ xknot = [item.value for item in self._result.params.values()]
202
+ xknot = (np.asarray(xknot) + cx) / bx
203
+ except ValueError:
204
+ print('Error when fitting adaptive splines. '
205
+ 'Reverting to initial knot location.')
206
+ xknot = xknot_backup.copy()
207
+ self._result = None
208
+ else:
209
+ self._params = None
210
+ self._result = None
211
+
212
+ # final fit
213
+ super(AdaptiveLSQUnivariateSpline, self).__init__(
214
+ x=x,
215
+ y=y,
216
+ t=xknot,
217
+ w=w,
218
+ bbox=bbox,
219
+ k=k,
220
+ ext=ext,
221
+ check_finite=False
222
+ )
223
+
224
+ def get_params(self):
225
+ """Return initial parameters for minimisation process."""
226
+
227
+ return self._params
228
+
229
+ def get_result(self):
230
+ """Return result of minimisation process."""
231
+
232
+ return self._result
@@ -54,6 +54,8 @@ def fit_sdistortion(data, ns_min, ns_max, nc_min, nc_max,
54
54
  -------
55
55
  data_straight : numpy array
56
56
  2D spectroscopic image corrected from S distortion.
57
+ poly_funct_peaks : `~numpy.polynomial.polynomial.Polynomial`
58
+ Fitted polynomial.
57
59
 
58
60
  """
59
61
 
@@ -177,4 +179,4 @@ def fit_sdistortion(data, ns_min, ns_max, nc_min, nc_max,
177
179
  plt.show()
178
180
 
179
181
  # return result
180
- return data_straight
182
+ return data_straight, poly_funct_peaks
@@ -8,7 +8,7 @@
8
8
  # License-Filename: LICENSE.txt
9
9
  #
10
10
 
11
- version = '0.2.5'
11
+ version = '0.3.5'
12
12
 
13
13
 
14
14
  def main():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: teareduce
3
- Version: 0.2.5
3
+ Version: 0.3.5
4
4
  Summary: Utilities for astronomical data reduction
5
5
  Author-email: Nicolás Cardiel <cardiel@ucm.es>
6
6
  License: GPL-3.0-or-later
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
22
22
  License-File: LICENSE.txt
23
23
  Requires-Dist: astropy
24
24
  Requires-Dist: importlib_resources
25
+ Requires-Dist: lmfit
25
26
  Requires-Dist: matplotlib
26
27
  Requires-Dist: numpy>=1.20
27
28
  Requires-Dist: scipy
@@ -3,11 +3,13 @@ README.md
3
3
  pyproject.toml
4
4
  src/teareduce/__init__.py
5
5
  src/teareduce/avoid_astropy_warnings.py
6
+ src/teareduce/correct_pincushion_distortion.py
6
7
  src/teareduce/cosmicrays.py
7
8
  src/teareduce/ctext.py
8
9
  src/teareduce/draw_rectangle.py
9
10
  src/teareduce/elapsed_time.py
10
11
  src/teareduce/imshow.py
12
+ src/teareduce/numsplines.py
11
13
  src/teareduce/peaks_spectrum.py
12
14
  src/teareduce/polfit.py
13
15
  src/teareduce/robust_std.py
@@ -1,5 +1,6 @@
1
1
  astropy
2
2
  importlib_resources
3
+ lmfit
3
4
  matplotlib
4
5
  numpy>=1.20
5
6
  scipy
File without changes
File without changes
File without changes