MUSE Pipeline Reference Manual  0.18.5
muse_sky_lines.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2008-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <string.h>
27 
28 #include "muse_sky.h"
29 #include "muse_utils.h"
30 #include "muse_cplwrappers.h"
31 #include "muse_data_format_z.h"
32 
33 /* Vibrational temperature for OH transitions */
34 #define MUSE_SKY_T_VIBR 200.0
35 
39 /*----------------------------------------------------------------------------*/
46 /*----------------------------------------------------------------------------*/
47 static cpl_table *
48 muse_sky_ohtransitions_load(const char *aFile) {
49  if (!cpl_fits_find_extension(aFile, "OH_TRANSITIONS")) {
50  return NULL;
51  }
52  return muse_cpltable_load(aFile, "OH_TRANSITIONS", muse_sky_lines_oh_transitions_def);
53 }
54 
55 /*----------------------------------------------------------------------------*/
64 /*----------------------------------------------------------------------------*/
65 cpl_error_code
66 muse_sky_ohtransitions_save(const cpl_table *aTable, const char *aFile) {
67  return muse_cpltable_append_file(aTable, aFile, "OH_TRANSITIONS",
69 }
70 
71 /*----------------------------------------------------------------------------*/
82 /*----------------------------------------------------------------------------*/
83 static cpl_error_code
84 muse_sky_lines_reindex_groups(cpl_table *aLines) {
85  cpl_ensure_code(aLines, CPL_ERROR_NULL_INPUT);
86  cpl_size n_rows = cpl_table_get_nrow(aLines);
87  if (n_rows > 0) {
88  int i;
89  int i_max = cpl_table_get_column_max(aLines, "group");
90  int new_ids[i_max + 1];
91  for (i = 0; i <= i_max; i++) {
92  new_ids[i] = -1;
93  }
94  i_max = 0;
95  cpl_size i_row;
96  for (i_row = 0; i_row < n_rows; i_row++) {
97  int old_id = cpl_table_get_int(aLines, "group", i_row, NULL);
98  if (new_ids[old_id] < 0) {
99  new_ids[old_id] = i_max++;
100  }
101  cpl_table_set_int(aLines, "group", i_row, new_ids[old_id]);
102  }
103  }
104  return CPL_ERROR_NONE;
105 }
106 
107 /*----------------------------------------------------------------------------*/
120 /*----------------------------------------------------------------------------*/
121 cpl_error_code
122 muse_sky_lines_set_range(cpl_table *aLines, double aLow, double aHigh) {
123  cpl_ensure_code(aLines, CPL_ERROR_NULL_INPUT);
124 #pragma omp critical(cpl_table_select)
125  cpl_table_unselect_all(aLines);
126  cpl_table_or_selected_double(aLines, "lambda", CPL_LESS_THAN, aLow);
127  cpl_table_or_selected_double(aLines, "lambda", CPL_GREATER_THAN, aHigh);
128  cpl_table_erase_selected(aLines);
129  muse_sky_lines_reindex_groups(aLines);
130 
131  return CPL_ERROR_NONE;
132 }
133 
134 /*----------------------------------------------------------------------------*/
144 /*----------------------------------------------------------------------------*/
145 cpl_table *
147  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
148  cpl_frameset *frames = muse_frameset_find(aProcessing->inputFrames,
149  MUSE_TAG_SKY_LINES, 0, CPL_FALSE);
150  cpl_errorstate es = cpl_errorstate_get();
151  cpl_frame *frame = cpl_frameset_get_position(frames, 0);
152  if (frame == NULL) {
153  cpl_frameset_delete(frames);
154  cpl_errorstate_set(es); /* swallow the illegal input state */
155  return NULL;
156  }
157  const char *fn = cpl_frame_get_filename(frame);
158  cpl_msg_info(__func__, "using sky line table %s", fn);
159 
160  cpl_table *oh_transitions = muse_sky_ohtransitions_load(fn);
161  cpl_table *lines = muse_cpltable_load(fn, "LINES", muse_sky_lines_lines_def);
162  if (lines == NULL && oh_transitions == NULL) {
163  cpl_frameset_delete(frames);
164  return NULL;
165  }
166 
167  // Convert tables with unscaled flux into ones with scaled flux.
168  if (lines != NULL) {
169  if (strcmp(cpl_table_get_column_unit(lines, "flux"),
170  "erg/(s cm^2 arcsec^2)") == 0) {
171  cpl_msg_info(__func__, "Scaling flux by 1e20");
172  cpl_table_multiply_scalar(lines, "flux", 1e20);
173  cpl_table_set_column_unit(lines, "flux",
174  "10**(-20)*erg/(s cm^2 arcsec^2)");
175  }
176  if (strcmp(cpl_table_get_column_unit(lines, "flux"),
177  "10**(-20)*erg/(s cm^2 arcsec^2)") != 0) {
178  cpl_msg_warning(__func__, "Unsupported flux unit %s",
179  cpl_table_get_column_unit(lines, "flux"));
180  }
181  }
182 
183  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
184  cpl_frameset_delete(frames);
185 
186  cpl_table *sky_lines = muse_sky_lines_create(lines, oh_transitions,
187  MUSE_SKY_T_VIBR);
188  cpl_table_delete(oh_transitions);
189  cpl_table_delete(lines);
190  return sky_lines;
191 }
192 
193 /*----------------------------------------------------------------------------*/
205 /*----------------------------------------------------------------------------*/
206 cpl_error_code
207 muse_sky_lines_apply_strength(cpl_table *aLines, const cpl_array *aStrength) {
208  cpl_ensure_code(aLines != NULL, CPL_ERROR_NULL_INPUT);
209  cpl_ensure_code(aStrength != NULL, CPL_ERROR_NULL_INPUT);
210 
211  int *group = cpl_table_get_data_int(aLines, "group");
212  double *flux = cpl_table_get_data_double(aLines, "flux");
213  cpl_size nRows = cpl_table_get_nrow(aLines);
214  cpl_size i;
215  for (i = 0; i < nRows; i++, group++, flux++) {
216  *flux *= cpl_array_get(aStrength, *group, NULL);
217  }
218  return CPL_ERROR_NONE;
219 }
220 
221 /*----------------------------------------------------------------------------*/
232 /*----------------------------------------------------------------------------*/
233 cpl_error_code
234 muse_sky_lines_cut(cpl_table *aLines, double aMinflux) {
235  cpl_ensure_code(aLines != NULL, CPL_ERROR_NULL_INPUT);
236  cpl_table_select_all(aLines);
237  cpl_table_and_selected_double(aLines, "flux", CPL_LESS_THAN, aMinflux);
238  cpl_table_erase_selected(aLines);
239  return CPL_ERROR_NONE;
240 }
241 
242 /*----------------------------------------------------------------------------*/
261 /*----------------------------------------------------------------------------*/
262 static cpl_table *
263 muse_sky_lines_create_oh(const cpl_table *aTransitions, double aTemperature) {
264  if ((aTransitions == NULL) || (cpl_table_get_nrow(aTransitions) == 0)) {
266  }
267 
268  const int max_level = cpl_table_get_column_max(aTransitions, "v_u");
269  const int min_level = cpl_table_get_column_min(aTransitions, "v_u");
270  cpl_ensure(max_level-min_level < 100, CPL_ERROR_UNSUPPORTED_MODE, NULL);
271  const int n_transitions = cpl_table_get_nrow(aTransitions);
272 
273  cpl_table *lines = muse_cpltable_new(muse_sky_lines_lines_def, n_transitions);
274 
275  cpl_table_copy_data_string(lines, "name",
276  cpl_table_get_data_string_const(aTransitions,
277  "name"));
278  cpl_table_copy_data_double(lines, "lambda",
279  cpl_table_get_data_double_const(aTransitions,
280  "lambda"));
281  cpl_table_copy_data_int(lines, "group",
282  cpl_table_get_data_int_const(aTransitions, "v_u"));
283  cpl_table_subtract_scalar(lines, "group", min_level);
284 
285  cpl_table_fill_column_window_double(lines, "flux", 0, n_transitions, 0.0);
286 
287  const double k_B = 1.3806504e-23; /* Boltzmann constant [eV/K] */
288  const double hc = 1.98623e-8; /* h * c [Angstrom*erg] */
289  cpl_table_copy_data_double(lines, "flux",
290  cpl_table_get_data_double_const(aTransitions,
291  "E_u"));
292  cpl_table_divide_scalar(lines, "flux", -k_B * aTemperature);
293  cpl_table_exponential_column(lines, "flux", CPL_MATH_E);
294 
295  cpl_table_duplicate_column(lines, "J_u", aTransitions, "J_u");
296  cpl_table_multiply_scalar(lines, "J_u", 2.0);
297  cpl_table_add_scalar(lines, "J_u", 1.0);
298  cpl_table_multiply_columns(lines, "flux", "J_u");
299  cpl_table_erase_column(lines, "J_u");
300 
301  cpl_table_fill_column_window_int(lines, "dq", 0, n_transitions, 0);
302 
303  double *flux = cpl_table_get_data_double(lines, "flux");
304  const int *group = cpl_table_get_data_int_const(lines, "group");
305 
306  double q[max_level - min_level + 1];
307  memset(q, 0, (max_level - min_level + 1)*sizeof(double));
308  int i_transition;
309  for (i_transition = 0; i_transition < n_transitions; i_transition++,
310  group++, flux++) {
311  q[*group] += *flux;
312  }
313 
314  cpl_table_duplicate_column(lines, "A", aTransitions, "A");
315  cpl_table_multiply_columns(lines, "flux", "A");
316  cpl_table_erase_column(lines, "A");
317  cpl_table_multiply_scalar(lines, "flux", 2.0 * hc);
318  cpl_table_divide_columns(lines, "flux", "lambda");
319 
320  flux = cpl_table_get_data_double(lines, "flux");
321  group = cpl_table_get_data_int_const(lines, "group");
322 
323  for (i_transition = 0; i_transition < n_transitions; i_transition++,
324  flux++, group++) {
325  *flux /= q[*group];
326  }
327 
328  cpl_table_multiply_scalar(lines, "flux", 1e20); // use 10^-20 scaling
329  // ad-hoc correction to get flux in the correct range
330  cpl_table_divide_scalar(lines, "flux", 50);
331 
332  muse_sky_lines_reindex_groups(lines);
333  return lines;
334 }
335 
336 /*----------------------------------------------------------------------------*/
349 /*----------------------------------------------------------------------------*/
350 cpl_table *
351 muse_sky_lines_create(const cpl_table *aLines,
352  const cpl_table *aOh_transitions, double aT_vibr)
353 {
354  int group_start = (aLines && cpl_table_get_nrow(aLines) > 0)?
355  cpl_table_get_column_max(aLines, "group") + 1: 0;
356  cpl_table *res = muse_sky_lines_create_oh(aOh_transitions, aT_vibr);
357  cpl_errorstate prestate = cpl_errorstate_get();
358  if (aLines) {
359  cpl_table_add_scalar(res, "group", group_start);
360  cpl_table_insert(res, aLines, 0);
361  }
362  if (!cpl_errorstate_is_equal(prestate)) {
363  cpl_msg_error(__func__, "while cpl_table_insert(): %s, %s",
364  cpl_table_get_column_unit(res, "flux"),
365  cpl_table_get_column_unit(aLines, "flux"));
366  cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
367  cpl_errorstate_set(prestate);
368  }
369  return res;
370 }
371 
cpl_error_code muse_sky_lines_apply_strength(cpl_table *, const cpl_array *)
Apply the line strengths to the lines.
cpl_table * muse_sky_lines_create(const cpl_table *aLines, const cpl_table *aOh_transitions, double aT_vibr)
Create the emission lines from the OH transitions and other lines.
cpl_table * muse_sky_lines_load(muse_processing *aProcessing)
Load the sky data files.
const muse_cpltable_def muse_sky_lines_lines_def[]
const muse_cpltable_def muse_sky_lines_oh_transitions_def[]
cpl_table * muse_cpltable_load(const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Load a table from disk (and check against definition).
cpl_table * muse_cpltable_new(const muse_cpltable_def *aDef, cpl_size aLength)
Create an empty table according to the specified definition.
cpl_error_code muse_sky_lines_set_range(cpl_table *, double, double)
Limit the lines in the table to a wavelength range.
void muse_processing_append_used(muse_processing *aProcessing, cpl_frame *aFrame, cpl_frame_group aGroup, int aDuplicate)
Add a frame to the set of used frames.
cpl_error_code muse_sky_lines_cut(cpl_table *, double)
Remove all lines below a certain flux limit.
static cpl_table * muse_sky_lines_create_oh(const cpl_table *aTransitions, double aTemperature)
Get a list of lines from an OH transition table.
cpl_frameset * muse_frameset_find(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with a certain tag
Definition: muse_utils.c:154
cpl_frameset * inputFrames
cpl_error_code muse_cpltable_append_file(const cpl_table *aTable, const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Save a table to disk (into a FITS extension)