teareduce 0.6.8__py3-none-any.whl → 0.7.0__py3-none-any.whl

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.
@@ -139,7 +139,7 @@ VALID_CLEANING_METHODS = {
139
139
  "PyCosmic": "pycosmic",
140
140
  "deepCR": "deepcr",
141
141
  "maskfill": "maskfill",
142
- "auxdata": "auxdata",
142
+ "Auxiliary data": "auxdata",
143
143
  }
144
144
 
145
145
  # Maximum pixel distance to consider when finding closest CR pixel
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Universidad Complutense de Madrid
2
+ # Copyright 2025-2026 Universidad Complutense de Madrid
3
3
  #
4
4
  # This file is part of teareduce
5
5
  #
@@ -9,6 +9,7 @@
9
9
 
10
10
  """Interpolation editor dialog for interpolation parameters."""
11
11
 
12
+ import os
12
13
  import tkinter as tk
13
14
  from tkinter import messagebox
14
15
  from tkinter import ttk
@@ -33,7 +34,8 @@ class InterpolationEditor:
33
34
  last_maskfill_operator,
34
35
  last_maskfill_smooth,
35
36
  last_maskfill_verbose,
36
- auxdata,
37
+ auxfile_list,
38
+ extension_auxfile_list,
37
39
  cleandata_lacosmic,
38
40
  cleandata_pycosmic,
39
41
  cleandata_deepcr,
@@ -63,8 +65,10 @@ class InterpolationEditor:
63
65
  The last used maskfill smooth parameter.
64
66
  last_maskfill_verbose : bool
65
67
  The last used maskfill verbose parameter.
66
- auxdata : array-like or None
67
- Auxiliary data for cleaning, if available.
68
+ auxfile_list : list of str
69
+ List of paths to auxiliary FITS files.
70
+ extension_auxfile_list : list of str
71
+ List of FITS extensions for auxiliary files.
68
72
  cleandata_lacosmic : array-like or None
69
73
  Cleaned data from L.A.Cosmic, if available.
70
74
  cleandata_pycosmic : array-like or None
@@ -99,8 +103,17 @@ class InterpolationEditor:
99
103
  The root Tkinter window.
100
104
  last_dilation : int
101
105
  The last used dilation parameter.
102
- auxdata : array-like or None
103
- Auxiliary data for cleaning, if available.
106
+ auxfile_list : list of str
107
+ List of paths to auxiliary FITS files.
108
+ extension_auxfile_list : list of str
109
+ List of FITS extensions for auxiliary files.
110
+ naux : int
111
+ Number of auxiliary files.
112
+ extension_auxfile_list : list of str
113
+ List of file names and extensions for auxiliary files.
114
+ auxiliary_data_index : int or None
115
+ Index of the selected auxiliary data option, ranging
116
+ from 1 to naux + 3 (mean, median, minimum), or None if not selected.
104
117
  cleandata_lacosmic : array-like or None
105
118
  Cleaned data from L.A.Cosmic, if available.
106
119
  cleandata_pycosmic : array-like or None
@@ -135,7 +148,22 @@ class InterpolationEditor:
135
148
  self.root = root
136
149
  self.root.title("Cleaning Parameters")
137
150
  self.last_dilation = last_dilation
138
- self.auxdata = auxdata
151
+ self.auxfile_list = auxfile_list
152
+ self.naux = len(auxfile_list)
153
+ if len(extension_auxfile_list) != self.naux:
154
+ raise ValueError("Length of extension_auxfile_list must match length of auxfile_list.")
155
+ self.extension_auxfile_list = extension_auxfile_list
156
+ if self.naux == 0:
157
+ self.auxdata_options = []
158
+ else:
159
+ self.auxdata_options = [
160
+ f"{os.path.basename(self.auxfile_list[i])}[{self.extension_auxfile_list[i]}]" for i in range(self.naux)
161
+ ]
162
+ if self.naux > 1:
163
+ self.auxdata_options.extend(
164
+ ["MEAN of auxiliary data", "MEDIAN of auxiliary data", "MINIMUM of auxiliary data"]
165
+ )
166
+ self.auxiliary_data_index = None # to be set when OK is pressed
139
167
  self.cleandata_lacosmic = cleandata_lacosmic
140
168
  self.cleandata_pycosmic = cleandata_pycosmic
141
169
  self.cleandata_deepcr = cleandata_deepcr
@@ -158,6 +186,11 @@ class InterpolationEditor:
158
186
  self.create_widgets()
159
187
  center_on_parent(child=self.root, parent=self.root.master)
160
188
 
189
+ def on_combo_auxdata_change(self, event):
190
+ """Handle changes in the auxiliary data combobox selection."""
191
+ self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
192
+ self.action_on_method_change()
193
+
161
194
  def create_widgets(self):
162
195
  """Create the widgets for the dialog."""
163
196
  # Main frame
@@ -196,7 +229,7 @@ class InterpolationEditor:
196
229
  if self.cleandata_deepcr is None:
197
230
  state = "disabled"
198
231
  # Skip auxdata method if auxdata is not available
199
- elif interp_method == "auxdata" and self.auxdata is None:
232
+ elif interp_method == "Auxiliary data" and self.naux == 0:
200
233
  state = "disabled"
201
234
  tk.Radiobutton(
202
235
  main_frame,
@@ -210,6 +243,21 @@ class InterpolationEditor:
210
243
  if column > 6:
211
244
  column = 0
212
245
  row += 1
246
+ # Add combobox to choose auxdata if auxdata method is selected and naux > 0:
247
+ if self.naux > 0:
248
+ self.auxdata_var = tk.StringVar(value=self.auxdata_options[0])
249
+ self.auxdata_combobox = ttk.Combobox(
250
+ main_frame,
251
+ textvariable=self.auxdata_var,
252
+ values=self.auxdata_options,
253
+ state="readonly",
254
+ width=40,
255
+ )
256
+ self.auxdata_combobox.grid(row=row, column=column, columnspan=4, sticky="w", padx=5, pady=5)
257
+ self.auxdata_combobox.config(state="disabled")
258
+ # --- Bind the selection event to the handler
259
+ self.auxdata_combobox.bind("<<ComboboxSelected>>", self.on_combo_auxdata_change)
260
+
213
261
  row += 1
214
262
 
215
263
  # Separator
@@ -335,6 +383,7 @@ class InterpolationEditor:
335
383
  def on_ok(self):
336
384
  """Handle the OK button click event."""
337
385
  self.cleaning_method = VALID_CLEANING_METHODS[self.cleaning_method_var.get()]
386
+
338
387
  try:
339
388
  self.npoints = int(self.entry_npoints.get())
340
389
  except ValueError:
@@ -434,6 +483,11 @@ class InterpolationEditor:
434
483
  return
435
484
  self.__dict__[key] = value
436
485
 
486
+ if self.cleaning_method == "auxdata":
487
+ # set auxiliary_data_index to the index of the selected auxdata option
488
+ # ranging from 1 to naux + 3 (mean, median, minimum)
489
+ self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
490
+
437
491
  self.root.destroy()
438
492
 
439
493
  def on_cancel(self):
@@ -446,7 +500,16 @@ class InterpolationEditor:
446
500
  def action_on_method_change(self):
447
501
  """Handle changes in the selected cleaning method."""
448
502
  selected_method = self.cleaning_method_var.get()
449
- print(f"Selected cleaning method: [red bold]{selected_method}[/red bold]")
503
+ if selected_method == "Auxiliary data":
504
+ self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
505
+ print(
506
+ f"Selected cleaning method: [red bold]{selected_method}[/red bold]: {self.auxdata_options[self.auxiliary_data_index - 1]}"
507
+ )
508
+ self.auxdata_combobox.config(state="readonly")
509
+ else:
510
+ print(f"Selected cleaning method: [red bold]{selected_method}[/red bold]")
511
+ self.auxdata_combobox.config(state="disabled")
512
+
450
513
  if selected_method in ["x interp.", "y interp."]:
451
514
  self.entry_npoints.config(state="normal")
452
515
  self.entry_degree.config(state="normal")
@@ -475,7 +538,7 @@ class InterpolationEditor:
475
538
  self.entry_maskfill_operator.config(state="disabled")
476
539
  self.entry_maskfill_smooth.config(state="disabled")
477
540
  self.entry_maskfill_verbose.config(state="disabled")
478
- elif selected_method in ["L.A.Cosmic", "PyCosmic", "deepCR", "auxdata"]:
541
+ elif selected_method in ["L.A.Cosmic", "PyCosmic", "deepCR", "Auxiliary data"]:
479
542
  self.entry_npoints.config(state="disabled")
480
543
  self.entry_degree.config(state="disabled")
481
544
  self.entry_maskfill_size.config(state="disabled")
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Universidad Complutense de Madrid
2
+ # Copyright 2025-2026 Universidad Complutense de Madrid
3
3
  #
4
4
  # This file is part of teareduce
5
5
  #
@@ -9,7 +9,9 @@
9
9
 
10
10
  """Define the ReviewCosmicRay class."""
11
11
 
12
+ import os
12
13
  import tkinter as tk
14
+ from tkinter import ttk
13
15
  from tkinter import messagebox
14
16
  from tkinter import simpledialog
15
17
 
@@ -55,8 +57,14 @@ class ReviewCosmicRay(ImageDisplay):
55
57
  root,
56
58
  root_width,
57
59
  root_height,
60
+ input_fits,
58
61
  data,
59
62
  auxdata,
63
+ auxfile_list,
64
+ extension_auxfile_list,
65
+ auxdata_mean,
66
+ auxdata_median,
67
+ auxdata_min,
60
68
  cleandata_lacosmic,
61
69
  cleandata_pycosmic,
62
70
  cleandata_deepcr,
@@ -82,10 +90,22 @@ class ReviewCosmicRay(ImageDisplay):
82
90
  The width of the root window. The review window is scaled accordingly.
83
91
  root_height : int
84
92
  The height of the root window. The review window is scaled accordingly.
93
+ input_fits : str
94
+ Path to the FITS file to be cleaned.
85
95
  data : 2D numpy array
86
96
  The original image data.
87
97
  auxdata : 2D numpy array or None
88
98
  The auxiliary image data.
99
+ auxfile_list : list of str
100
+ List of auxiliary FITS file names.
101
+ extension_auxfile_list : list of str
102
+ List of FITS extensions for auxiliary files.
103
+ auxdata_mean : 2D numpy array or None
104
+ The mean auxiliary image data.
105
+ auxdata_median : 2D numpy array or None
106
+ The median auxiliary image data.
107
+ auxdata_min : 2D numpy array or None
108
+ The minimum auxiliary image data.
89
109
  cleandata_lacosmic: 2D numpy array or None
90
110
  The cleaned image data from L.A.Cosmic.
91
111
  cleandata_pycosmic: 2D numpy array or None
@@ -160,10 +180,22 @@ class ReviewCosmicRay(ImageDisplay):
160
180
  The scaling factor for the width of the review window.
161
181
  factor_height : float
162
182
  The scaling factor for the height of the review window.
183
+ input_fits : str
184
+ Path to the FITS file to be cleaned.
163
185
  data : 2D numpy array
164
186
  The original image data.
165
187
  auxdata : 2D numpy array or None
166
188
  The auxiliary image data.
189
+ auxfile_list : list of str
190
+ List of auxiliary FITS file names.
191
+ extension_auxfile_list : list of str
192
+ List of FITS extensions for auxiliary files.
193
+ auxdata_mean : 2D numpy array or None
194
+ The mean auxiliary image data.
195
+ auxdata_median : 2D numpy array or None
196
+ The median auxiliary image data.
197
+ auxdata_min : 2D numpy array or None
198
+ The minimum auxiliary image data.
167
199
  cleandata_lacosmic: 2D numpy array or None
168
200
  The cleaned image data from L.A.Cosmic.
169
201
  cleandata_pycosmic: 2D numpy array or None
@@ -200,17 +232,46 @@ class ReviewCosmicRay(ImageDisplay):
200
232
  self.root.title(f"Review Cosmic Rays (TEA version {VERSION})")
201
233
  self.factor_width = root_width / DEFAULT_TK_WINDOW_SIZE_X
202
234
  self.factor_height = root_height / DEFAULT_TK_WINDOW_SIZE_Y
235
+ self.input_fits = input_fits
203
236
  self.auxdata = auxdata
204
- if self.auxdata is not None:
205
- # self.root.geometry("1000x760+100+100") # This does not work in Fedora
237
+ self.naux = len(self.auxdata)
238
+ if self.naux > 0:
239
+ # self.root.geometry("1000x800+100+100") # This does not work in Fedora
206
240
  window_width = int(1000 * self.factor_width + 0.5)
207
- window_height = int(760 * self.factor_height + 0.5)
241
+ window_height = int(800 * self.factor_height + 0.5)
208
242
  self.root.minsize(window_width, window_height)
209
243
  else:
210
- # self.root.geometry("900x760+100+100") # This does not work in Fedora
211
- window_width = int(900 * self.factor_width + 0.5)
212
- window_height = int(760 * self.factor_height + 0.5)
244
+ # self.root.geometry("950x800+100+100") # This does not work in Fedora
245
+ window_width = int(950 * self.factor_width + 0.5)
246
+ window_height = int(800 * self.factor_height + 0.5)
213
247
  self.root.minsize(window_width, window_height)
248
+ if len(auxfile_list) != self.naux:
249
+ raise ValueError("Length of auxfile_list must match length of auxdata.")
250
+ if len(extension_auxfile_list) != self.naux:
251
+ raise ValueError("Length of extension_auxfile_list must match length of auxdata.")
252
+ self.auxfile_list = auxfile_list
253
+ self.extension_auxfile_list = extension_auxfile_list
254
+ self.extension_auxfile_list = extension_auxfile_list
255
+ if self.naux == 0:
256
+ self.auxdata_options = []
257
+ self.auxiliary_data_index = None
258
+ else:
259
+ self.auxdata_options = [
260
+ f"#[{i+1}]: {os.path.basename(self.auxfile_list[i])}[{self.extension_auxfile_list[i]}]"
261
+ for i in range(self.naux)
262
+ ]
263
+ if self.naux > 1:
264
+ self.auxdata_options.extend(
265
+ [
266
+ f"#[{self.naux+1}]: MEAN of auxiliary data",
267
+ f"#[{self.naux+2}]: MEDIAN of auxiliary data",
268
+ f"#[{self.naux+3}]: MINIMUM of auxiliary data",
269
+ ]
270
+ )
271
+ self.auxiliary_data_index = 0 # first auxiliary data by default
272
+ self.auxdata_mean = auxdata_mean
273
+ self.auxdata_median = auxdata_median
274
+ self.auxdata_min = auxdata_min
214
275
  self.root.update_idletasks()
215
276
  self.root.geometry("+100+100")
216
277
  self.data = data
@@ -247,6 +308,22 @@ class ReviewCosmicRay(ImageDisplay):
247
308
  self.create_widgets()
248
309
  center_on_parent(child=self.root, parent=self.root.master, offset_x=50, offset_y=50)
249
310
 
311
+ def on_combo_auxdata_select(self, event):
312
+ """Handle selection of auxiliary data from the combobox."""
313
+ selected_option = self.auxdata_var.get()
314
+ self.auxiliary_data_index = self.auxdata_options.index(selected_option) + 1
315
+ self.update_display()
316
+
317
+ # Return focus to the Matplotlib figure to keep keyboard shortcuts working
318
+ # (requires a tiny delay to avoid conflicts with the combobox event handling)
319
+ def give_focus_to_canvas():
320
+ self.canvas.get_tk_widget().focus_set()
321
+
322
+ if event is None:
323
+ self.root.after(10, give_focus_to_canvas)
324
+ else:
325
+ event.widget.after(10, give_focus_to_canvas)
326
+
250
327
  def create_widgets(self):
251
328
  """Create the GUI widgets for the review window."""
252
329
  # Define instance of TrackedTkButton, that facilitates to show help information
@@ -290,6 +367,15 @@ class ReviewCosmicRay(ImageDisplay):
290
367
  )
291
368
  self.restore_cr_button.pack(side=tk.LEFT, padx=5)
292
369
  self.restore_cr_button.config(state=tk.DISABLED)
370
+ # --- Fix CR button
371
+ self.fix_cr_button = tkbutton.new(
372
+ self.button_frame1,
373
+ text="[f]ix CR",
374
+ command=self.fix_cr_interpolation,
375
+ help_text="Fix current interpolation and allow new one.",
376
+ )
377
+ self.fix_cr_button.pack(side=tk.LEFT, padx=5)
378
+ self.fix_cr_button.config(state=tk.DISABLED)
293
379
  # --- Next button
294
380
  self.next_button = tkbutton.new(
295
381
  self.button_frame1,
@@ -403,24 +489,45 @@ class ReviewCosmicRay(ImageDisplay):
403
489
  help_text="Perform maskfill interpolation for the current cosmic ray.",
404
490
  )
405
491
  self.interp_maskfill_button.pack(side=tk.LEFT, padx=5)
492
+
493
+ # Row 4 of buttons
494
+ self.button_frame4 = tk.Frame(self.root)
495
+ self.button_frame4.pack(pady=5)
406
496
  # --- Interpolation using auxiliary data button
407
497
  self.interp_aux_button = tkbutton.new(
408
- self.button_frame3,
498
+ self.button_frame4,
409
499
  text="[a]ux. data",
410
500
  command=self.use_auxdata,
411
501
  help_text="Use auxiliary data for interpolation of the current cosmic ray.",
412
502
  )
413
503
  self.interp_aux_button.pack(side=tk.LEFT, padx=5)
414
- if self.auxdata is None:
504
+ if self.naux == 0:
415
505
  self.interp_aux_button.config(state=tk.DISABLED)
506
+ # --- Add combobox to choose auxdata if auxdata method is selected and naux > 0:
507
+ if self.naux == 0:
508
+ self.auxdata_var = tk.StringVar(value="No auxiliary data available")
509
+ else:
510
+ self.auxdata_var = tk.StringVar(value=self.auxdata_options[self.auxiliary_data_index])
511
+ self.auxdata_combobox = ttk.Combobox(
512
+ self.button_frame4,
513
+ textvariable=self.auxdata_var,
514
+ values=self.auxdata_options,
515
+ state="readonly",
516
+ width=40,
517
+ )
518
+ self.auxdata_combobox.pack(side=tk.LEFT, padx=5)
519
+ if self.naux == 0:
520
+ self.auxdata_combobox.config(state="disabled")
521
+ # --- Bind the selection event to the handler
522
+ self.auxdata_combobox.bind("<<ComboboxSelected>>", self.on_combo_auxdata_select)
416
523
 
417
- # Row 4 of buttons
418
- self.button_frame4 = tk.Frame(self.root)
419
- self.button_frame4.pack(pady=5)
524
+ # Row 5 of buttons
525
+ self.button_frame5 = tk.Frame(self.root)
526
+ self.button_frame5.pack(pady=5)
420
527
  # --- vmin button
421
528
  vmin, vmax = zscale(self.data)
422
529
  self.vmin_button = tkbutton.new(
423
- self.button_frame4,
530
+ self.button_frame5,
424
531
  text=f"vmin: {vmin:.2f}",
425
532
  command=self.set_vmin,
426
533
  help_text="Set the minimum value for the display scale.",
@@ -429,7 +536,7 @@ class ReviewCosmicRay(ImageDisplay):
429
536
  self.vmin_button.pack(side=tk.LEFT, padx=5)
430
537
  # --- vmax button
431
538
  self.vmax_button = tkbutton.new(
432
- self.button_frame4,
539
+ self.button_frame5,
433
540
  text=f"vmax: {vmax:.2f}",
434
541
  command=self.set_vmax,
435
542
  help_text="Set the maximum value for the display scale.",
@@ -438,7 +545,7 @@ class ReviewCosmicRay(ImageDisplay):
438
545
  self.vmax_button.pack(side=tk.LEFT, padx=5)
439
546
  # --- minmax button
440
547
  self.set_minmax_button = tkbutton.new(
441
- self.button_frame4,
548
+ self.button_frame5,
442
549
  text="minmax [,]",
443
550
  command=self.set_minmax,
444
551
  help_text="Set the display scale to the minimum and maximum data values.",
@@ -446,7 +553,7 @@ class ReviewCosmicRay(ImageDisplay):
446
553
  self.set_minmax_button.pack(side=tk.LEFT, padx=5)
447
554
  # --- zscale button
448
555
  self.set_zscale_button = tkbutton.new(
449
- self.button_frame4,
556
+ self.button_frame5,
450
557
  text="zscale [/]",
451
558
  command=self.set_zscale,
452
559
  help_text="Set the display scale using zscale.",
@@ -454,7 +561,7 @@ class ReviewCosmicRay(ImageDisplay):
454
561
  self.set_zscale_button.pack(side=tk.LEFT, padx=5)
455
562
  # --- Help button
456
563
  self.help_button = tkbutton.new(
457
- self.button_frame4,
564
+ self.button_frame5,
458
565
  text="Help",
459
566
  command=tkbutton.show_help,
460
567
  help_text="Show help information for all buttons.",
@@ -462,7 +569,7 @@ class ReviewCosmicRay(ImageDisplay):
462
569
  self.help_button.pack(side=tk.LEFT, padx=5)
463
570
 
464
571
  # Figure
465
- if self.auxdata is not None:
572
+ if self.naux > 0:
466
573
  self.fig, (self.ax, self.ax_aux) = plt.subplots(
467
574
  ncols=2, figsize=(11 * self.factor_width, 5.5 * self.factor_height), constrained_layout=True
468
575
  )
@@ -546,12 +653,23 @@ class ReviewCosmicRay(ImageDisplay):
546
653
  vmax=vmax,
547
654
  )
548
655
  self.image.set_extent([jmin + 0.5, jmax + 1.5, imin + 0.5, imax + 1.5])
549
- if self.auxdata is not None:
656
+ if self.naux > 0:
657
+ self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
658
+ if 1 <= self.auxiliary_data_index <= self.naux:
659
+ auximg = self.auxdata[self.auxiliary_data_index - 1]
660
+ elif self.auxiliary_data_index == self.naux + 1:
661
+ auximg = self.auxdata_mean
662
+ elif self.auxiliary_data_index == self.naux + 2:
663
+ auximg = self.auxdata_median
664
+ elif self.auxiliary_data_index == self.naux + 3:
665
+ auximg = self.auxdata_min
666
+ else:
667
+ raise ValueError(f"Invalid auxiliary data index: {self.auxiliary_data_index}")
550
668
  self.ax_aux.clear()
551
669
  self.image_aux, _, _ = imshow(
552
670
  self.fig,
553
671
  self.ax_aux,
554
- self.auxdata[self.region],
672
+ auximg[self.region],
555
673
  colorbar=False,
556
674
  xlabel=xlabel,
557
675
  ylabel=ylabel,
@@ -559,7 +677,10 @@ class ReviewCosmicRay(ImageDisplay):
559
677
  vmax=vmax,
560
678
  )
561
679
  self.image_aux.set_extent([jmin + 0.5, jmax + 1.5, imin + 0.5, imax + 1.5])
562
- self.ax_aux.set_title("Auxiliary data")
680
+ dumfname = self.auxdata_options[self.auxiliary_data_index - 1]
681
+ dumi = dumfname.find(":")
682
+ dumc = dumfname[:dumi].replace("[", "").replace("]", "")
683
+ self.ax_aux.set_title(f"Auxiliary data {dumc}:\n{dumfname[dumi+1:].strip()}")
563
684
  # Overplot cosmic ray pixels
564
685
  xlim = self.ax.get_xlim()
565
686
  ylim = self.ax.get_ylim()
@@ -576,7 +697,7 @@ class ReviewCosmicRay(ImageDisplay):
576
697
  if self.first_cr_index == 0:
577
698
  self.ax.set_title("Selecting CR pixels with mouse cursor")
578
699
  else:
579
- self.ax.set_title(f"Cosmic ray #{self.cr_index}/{self.num_features}")
700
+ self.ax.set_title(f"Cosmic ray #{self.cr_index}/{self.num_features}\n{os.path.basename(self.input_fits)}")
580
701
  if self.first_plot:
581
702
  self.first_plot = False
582
703
  self.canvas.draw_idle()
@@ -654,6 +775,7 @@ class ReviewCosmicRay(ImageDisplay):
654
775
  """Set the state of buttons after cleaning a cosmic ray."""
655
776
  self.disable_interpolation_buttons()
656
777
  self.restore_cr_button.config(state=tk.NORMAL)
778
+ self.fix_cr_button.config(state=tk.NORMAL)
657
779
  self.remove_crosses_button.config(state=tk.DISABLED)
658
780
 
659
781
  def interp_x(self):
@@ -800,13 +922,25 @@ class ReviewCosmicRay(ImageDisplay):
800
922
 
801
923
  def use_auxdata(self):
802
924
  """Use auxiliary data to clean a cosmic ray."""
803
- if self.auxdata is None:
925
+ if self.naux == 0:
804
926
  print("Auxiliary data not available.")
805
927
  return
806
928
  print(f"Auxiliary data interpolation of cosmic ray {self.cr_index}")
929
+ self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
930
+ print(f"Using auxiliary data: {self.auxdata_options[self.auxiliary_data_index-1]}")
807
931
  ycr_list, xcr_list = np.where(self.cr_labels == self.cr_index)
932
+ if 1 <= self.auxiliary_data_index <= self.naux:
933
+ replacement_data = self.auxdata[self.auxiliary_data_index - 1]
934
+ elif self.auxiliary_data_index == self.naux + 1:
935
+ replacement_data = self.auxdata_mean
936
+ elif self.auxiliary_data_index == self.naux + 2:
937
+ replacement_data = self.auxdata_median
938
+ elif self.auxiliary_data_index == self.naux + 3:
939
+ replacement_data = self.auxdata_min
940
+ else:
941
+ raise ValueError(f"Invalid auxiliary data index: {self.auxiliary_data_index}.")
808
942
  for iy, ix in zip(ycr_list, xcr_list):
809
- self.data[iy, ix] = self.auxdata[iy, ix]
943
+ self.data[iy, ix] = replacement_data[iy, ix]
810
944
  self.mask_fixed[iy, ix] = True
811
945
  self.num_cr_cleaned += 1
812
946
  self.set_buttons_after_cleaning_cr()
@@ -835,6 +969,17 @@ class ReviewCosmicRay(ImageDisplay):
835
969
  self.enable_interpolation_buttons()
836
970
  self.remove_crosses_button.config(state=tk.NORMAL)
837
971
  self.restore_cr_button.config(state=tk.DISABLED)
972
+ self.fix_cr_button.config(state=tk.DISABLED)
973
+ self.update_display()
974
+
975
+ def fix_cr_interpolation(self):
976
+ """Fix the current interpolation and allow new one."""
977
+ print(f"Fixed interpolation for cosmic ray {self.cr_index}")
978
+ ycr_list, xcr_list = np.where(self.cr_labels == self.cr_index)
979
+ for iy, ix in zip(ycr_list, xcr_list):
980
+ self.cr_labels[iy, ix] = 0
981
+ self.restore_cr_button.config(state=tk.DISABLED)
982
+ self.fix_cr_button.config(state=tk.DISABLED)
838
983
  self.update_display()
839
984
 
840
985
  def continue_cr(self):
@@ -848,6 +993,7 @@ class ReviewCosmicRay(ImageDisplay):
848
993
  return # important: do not remove (to avoid errors)
849
994
  self.first_plot = True
850
995
  self.restore_cr_button.config(state=tk.DISABLED)
996
+ self.fix_cr_button.config(state=tk.DISABLED)
851
997
  self.enable_interpolation_buttons()
852
998
  self.remove_crosses_button.config(state=tk.NORMAL)
853
999
  self.update_display()
@@ -863,6 +1009,9 @@ class ReviewCosmicRay(ImageDisplay):
863
1009
  elif event.key == "r":
864
1010
  if self.restore_cr_button.cget("state") != "disabled":
865
1011
  self.restore_cr()
1012
+ elif event.key == "f":
1013
+ if self.fix_cr_button.cget("state") != "disabled":
1014
+ self.fix_cr_interpolation()
866
1015
  elif event.key == "x":
867
1016
  if self.interp_x_button.cget("state") != "disabled":
868
1017
  self.interp_x()
@@ -896,6 +1045,12 @@ class ReviewCosmicRay(ImageDisplay):
896
1045
  elif event.key == "e":
897
1046
  self.exit_review()
898
1047
  return # important: do not remove (to avoid errors)
1048
+ elif event.key in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]:
1049
+ if self.naux > 1 and int(event.key) <= self.naux + 3:
1050
+ index = int(event.key) - 1
1051
+ if 0 <= index < len(self.auxdata_options):
1052
+ self.auxdata_var.set(self.auxdata_options[index])
1053
+ self.on_combo_auxdata_select(None)
899
1054
  else:
900
1055
  print(f"Key pressed: {event.key}")
901
1056
 
@@ -933,6 +1088,7 @@ class ReviewCosmicRay(ImageDisplay):
933
1088
  self.interp_deepcr_button.config(state=tk.DISABLED)
934
1089
  self.interp_maskfill_button.config(state=tk.DISABLED)
935
1090
  self.interp_aux_button.config(state=tk.DISABLED)
1091
+ self.auxdata_combobox.config(state=tk.DISABLED)
936
1092
 
937
1093
  def enable_interpolation_buttons(self):
938
1094
  """Enable all interpolation buttons."""
@@ -949,5 +1105,6 @@ class ReviewCosmicRay(ImageDisplay):
949
1105
  if self.cleandata_deepcr is not None:
950
1106
  self.interp_deepcr_button.config(state=tk.NORMAL)
951
1107
  self.interp_maskfill_button.config(state=tk.NORMAL)
952
- if self.auxdata is not None:
1108
+ if self.naux > 0:
953
1109
  self.interp_aux_button.config(state=tk.NORMAL)
1110
+ self.auxdata_combobox.config(state="readonly")
@@ -194,7 +194,7 @@ class SimulateCCDExposure:
194
194
 
195
195
  CCD exposures are simulated making use of basic CCD parameters,
196
196
  such as gain, readout noise, bias, dark and flat field.
197
- A data model can also be employed to simulate more realising
197
+ A data model can also be employed to simulate more realistic
198
198
  CCD exposures.
199
199
 
200
200
  The saturated pixels in 'data_model' are returned as 2**bitpix - 1
@@ -230,6 +230,8 @@ class SimulateCCDExposure:
230
230
  Numpy array with the pixel to pixel sensitivity (without units).
231
231
  data_model : Quantity
232
232
  Numpy array with the model of the source to be simulated (ADU).
233
+ Note that this is the model before applying the flatfield.
234
+ The values in this array must be the total ADU for each pixel.
233
235
  seed : int or None
234
236
  Random number generator seed.
235
237
  _rng : np.random.RandomState
teareduce/version.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright 2023-2025 Universidad Complutense de Madrid
3
+ # Copyright 2023-2026 Universidad Complutense de Madrid
4
4
  #
5
5
  # This file is part of teareduce
6
6
  #
@@ -9,7 +9,7 @@
9
9
  #
10
10
  """Module to define the version of the teareduce package."""
11
11
 
12
- VERSION = '0.6.8'
12
+ VERSION = '0.7.0'
13
13
 
14
14
 
15
15
  def main():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teareduce
3
- Version: 0.6.8
3
+ Version: 0.7.0
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