MUSE Pipeline Reference Manual  1.0.2
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 {
50  if (!cpl_fits_find_extension(aFile, "OH_TRANSITIONS")) {
51  return NULL;
52  }
53  return muse_cpltable_load(aFile, "OH_TRANSITIONS",
55 }
56 
57 /*----------------------------------------------------------------------------*/
68 /*----------------------------------------------------------------------------*/
69 static cpl_error_code
70 muse_sky_lines_reindex_groups(cpl_table *aLines)
71 {
72  cpl_ensure_code(aLines, CPL_ERROR_NULL_INPUT);
73  cpl_size n_rows = cpl_table_get_nrow(aLines);
74  if (n_rows > 0) {
75  int i;
76  int i_max = cpl_table_get_column_max(aLines, "group");
77  int new_ids[i_max + 1];
78  for (i = 0; i <= i_max; i++) {
79  new_ids[i] = -1;
80  }
81  i_max = 0;
82  cpl_size i_row;
83  for (i_row = 0; i_row < n_rows; i_row++) {
84  int old_id = cpl_table_get_int(aLines, "group", i_row, NULL);
85  if (new_ids[old_id] < 0) {
86  new_ids[old_id] = i_max++;
87  }
88  cpl_table_set_int(aLines, "group", i_row, new_ids[old_id]);
89  }
90  }
91  return CPL_ERROR_NONE;
92 }
93 
94 /*----------------------------------------------------------------------------*/
106 /*----------------------------------------------------------------------------*/
107 cpl_error_code
108 muse_sky_lines_set_range(cpl_table *aLines, double aLow, double aHigh)
109 {
110  cpl_ensure_code(aLines, CPL_ERROR_NULL_INPUT);
111 #pragma omp critical(cpl_table_select)
112  cpl_table_unselect_all(aLines);
113  cpl_table_or_selected_double(aLines, "lambda", CPL_LESS_THAN, aLow);
114  cpl_table_or_selected_double(aLines, "lambda", CPL_GREATER_THAN, aHigh);
115  cpl_table_erase_selected(aLines);
116  muse_sky_lines_reindex_groups(aLines);
117 
118  return CPL_ERROR_NONE;
119 }
120 
121 /*----------------------------------------------------------------------------*/
134 /*----------------------------------------------------------------------------*/
135 cpl_error_code
136 muse_sky_lines_save(muse_processing *aProcessing, const cpl_table *aLines,
137  cpl_propertylist *aHeader)
138 {
139  cpl_ensure_code(aProcessing && aLines && aHeader, CPL_ERROR_NULL_INPUT);
140  cpl_frame *frame = muse_processing_new_frame(aProcessing, -1, aHeader,
141  MUSE_TAG_SKY_LINES,
142  CPL_FRAME_TYPE_TABLE);
143  cpl_ensure_code(frame, CPL_ERROR_ILLEGAL_INPUT);
144  const char *filename = cpl_frame_get_filename(frame);
145  cpl_error_code rc = cpl_propertylist_save(aHeader, filename, CPL_IO_CREATE);
146  rc = muse_cpltable_append_file(aLines, filename,
147  "LINES", muse_sky_lines_lines_def);
148  if (rc == CPL_ERROR_NONE) {
149  cpl_frameset_insert(aProcessing->outframes, frame);
150  } else {
151  cpl_frame_delete(frame);
152  }
153  return rc;
154 }
155 
156 /*----------------------------------------------------------------------------*/
166 /*----------------------------------------------------------------------------*/
167 cpl_table *
169 {
170  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
171  cpl_frameset *frames = muse_frameset_find(aProcessing->inframes,
172  MUSE_TAG_SKY_LINES, 0, CPL_FALSE);
173  cpl_errorstate es = cpl_errorstate_get();
174  cpl_frame *frame = cpl_frameset_get_position(frames, 0);
175  if (frame == NULL) {
176  cpl_frameset_delete(frames);
177  cpl_errorstate_set(es); /* swallow the illegal input state */
178  return NULL;
179  }
180  const char *fn = cpl_frame_get_filename(frame);
181  cpl_msg_info(__func__, "using sky line table %s", fn);
182 
183  cpl_table *oh_transitions = muse_sky_ohtransitions_load(fn);
184  cpl_table *lines = muse_cpltable_load(fn, "LINES", muse_sky_lines_lines_def);
185  if (lines == NULL && oh_transitions == NULL) {
186  cpl_frameset_delete(frames);
187  return NULL;
188  }
189 
190  // Convert tables with unscaled flux into ones with scaled flux.
191  if (lines != NULL) {
192  if (strcmp(cpl_table_get_column_unit(lines, "flux"),
193  "erg/(s cm^2 arcsec^2)") == 0) {
194  cpl_msg_info(__func__, "Scaling flux by 1e20");
195  cpl_table_multiply_scalar(lines, "flux", 1e20);
196  cpl_table_set_column_unit(lines, "flux",
197  "10**(-20)*erg/(s cm^2 arcsec^2)");
198  }
199  if (strcmp(cpl_table_get_column_unit(lines, "flux"),
200  "10**(-20)*erg/(s cm^2 arcsec^2)") != 0) {
201  cpl_msg_warning(__func__, "Unsupported flux unit %s",
202  cpl_table_get_column_unit(lines, "flux"));
203  }
204  }
205 
206  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
207  cpl_frameset_delete(frames);
208 
209  cpl_table *sky_lines = muse_sky_lines_create(lines, oh_transitions,
210  MUSE_SKY_T_VIBR);
211  cpl_table_delete(oh_transitions);
212  cpl_table_delete(lines);
213  return sky_lines;
214 }
215 
216 /*----------------------------------------------------------------------------*/
227 /*----------------------------------------------------------------------------*/
228 cpl_error_code
229 muse_sky_lines_apply_strength(cpl_table *aLines, const cpl_array *aStrength)
230 {
231  cpl_ensure_code(aLines != NULL, CPL_ERROR_NULL_INPUT);
232  cpl_ensure_code(aStrength != NULL, CPL_ERROR_NULL_INPUT);
233 
234  int *group = cpl_table_get_data_int(aLines, "group");
235  double *flux = cpl_table_get_data_double(aLines, "flux");
236  cpl_size nRows = cpl_table_get_nrow(aLines);
237  cpl_size i;
238  for (i = 0; i < nRows; i++, group++, flux++) {
239  *flux *= cpl_array_get(aStrength, *group, NULL);
240  }
241  return CPL_ERROR_NONE;
242 }
243 
244 /*----------------------------------------------------------------------------*/
254 /*----------------------------------------------------------------------------*/
255 cpl_error_code
256 muse_sky_lines_cut(cpl_table *aLines, double aMinflux)
257 {
258  cpl_ensure_code(aLines != NULL, CPL_ERROR_NULL_INPUT);
259  cpl_table_select_all(aLines);
260  cpl_table_and_selected_double(aLines, "flux", CPL_LESS_THAN, aMinflux);
261  cpl_table_erase_selected(aLines);
262  return CPL_ERROR_NONE;
263 }
264 
265 /*----------------------------------------------------------------------------*/
286 /*----------------------------------------------------------------------------*/
287 static cpl_table *
288 muse_sky_lines_create_oh(const cpl_table *aTransitions, double aTemperature)
289 {
290  if ((aTransitions == NULL) || (cpl_table_get_nrow(aTransitions) == 0)) {
292  }
293 
294  const int max_level = cpl_table_get_column_max(aTransitions, "v_u");
295  const int min_level = cpl_table_get_column_min(aTransitions, "v_u");
296  cpl_ensure(max_level-min_level < 100, CPL_ERROR_UNSUPPORTED_MODE, NULL);
297  const int n_transitions = cpl_table_get_nrow(aTransitions);
298 
299  cpl_table *lines = muse_cpltable_new(muse_sky_lines_lines_def, n_transitions);
300 
301  cpl_table_copy_data_string(lines, "name",
302  cpl_table_get_data_string_const(aTransitions,
303  "name"));
304  cpl_table_copy_data_double(lines, "lambda",
305  cpl_table_get_data_double_const(aTransitions,
306  "lambda"));
307  cpl_table_copy_data_int(lines, "group",
308  cpl_table_get_data_int_const(aTransitions, "v_u"));
309  cpl_table_subtract_scalar(lines, "group", min_level);
310 
311  cpl_table_fill_column_window_double(lines, "flux", 0, n_transitions, 0.0);
312 
313  const double k_B = 1.3806504e-23; /* Boltzmann constant [eV/K] */
314  const double hc = 1.98623e-8; /* h * c [Angstrom*erg] */
315  cpl_table_copy_data_double(lines, "flux",
316  cpl_table_get_data_double_const(aTransitions, "E_u"));
317  cpl_table_divide_scalar(lines, "flux", -k_B * aTemperature);
318  cpl_table_exponential_column(lines, "flux", CPL_MATH_E);
319 
320  cpl_table_duplicate_column(lines, "J_u", aTransitions, "J_u");
321  cpl_table_multiply_scalar(lines, "J_u", 2.0);
322  cpl_table_add_scalar(lines, "J_u", 1.0);
323  cpl_table_multiply_columns(lines, "flux", "J_u");
324  cpl_table_erase_column(lines, "J_u");
325 
326  cpl_table_fill_column_window_int(lines, "dq", 0, n_transitions, 0);
327 
328  double *flux = cpl_table_get_data_double(lines, "flux");
329  const int *group = cpl_table_get_data_int_const(lines, "group");
330 
331  double q[max_level - min_level + 1];
332  memset(q, 0, (max_level - min_level + 1)*sizeof(double));
333  int i_transition;
334  for (i_transition = 0; i_transition < n_transitions; i_transition++,
335  group++, flux++) {
336  q[*group] += *flux;
337  }
338 
339  cpl_table_duplicate_column(lines, "A", aTransitions, "A");
340  cpl_table_multiply_columns(lines, "flux", "A");
341  cpl_table_erase_column(lines, "A");
342  cpl_table_multiply_scalar(lines, "flux", 2.0 * hc);
343  cpl_table_divide_columns(lines, "flux", "lambda");
344 
345  flux = cpl_table_get_data_double(lines, "flux");
346  group = cpl_table_get_data_int_const(lines, "group");
347 
348  for (i_transition = 0; i_transition < n_transitions; i_transition++,
349  flux++, group++) {
350  *flux /= q[*group];
351  }
352 
353  cpl_table_multiply_scalar(lines, "flux", 1e20); // use 10^-20 scaling
354  // ad-hoc correction to get flux in the correct range
355  cpl_table_divide_scalar(lines, "flux", 50);
356 
357  muse_sky_lines_reindex_groups(lines);
358  return lines;
359 }
360 
361 /*----------------------------------------------------------------------------*/
375 /*----------------------------------------------------------------------------*/
376 cpl_table *
377 muse_sky_lines_create(const cpl_table *aLines,
378  const cpl_table *aOh_transitions, double aT_vibr)
379 {
380  int group_start = (aLines && cpl_table_get_nrow(aLines) > 0)?
381  cpl_table_get_column_max(aLines, "group") + 1: 0;
382  cpl_table *res = muse_sky_lines_create_oh(aOh_transitions, aT_vibr);
383  cpl_errorstate prestate = cpl_errorstate_get();
384  if (aLines) {
385  cpl_table_add_scalar(res, "group", group_start);
386  cpl_table_insert(res, aLines, 0);
387  }
388  if (!cpl_errorstate_is_equal(prestate)) {
389  cpl_msg_error(__func__, "while cpl_table_insert(): %s, %s",
390  cpl_table_get_column_unit(res, "flux"),
391  cpl_table_get_column_unit(aLines, "flux"));
392  cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
393  cpl_errorstate_set(prestate);
394  }
395  return res;
396 }
397 
cpl_error_code muse_sky_lines_apply_strength(cpl_table *aLines, const cpl_array *aStrength)
Apply the line strengths to the lines.
cpl_error_code muse_sky_lines_save(muse_processing *aProcessing, const cpl_table *aLines, cpl_propertylist *aHeader)
Save sky lines table to file.
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_frame * muse_processing_new_frame(muse_processing *aProcessing, int aIFU, cpl_propertylist *aHeader, const char *aTag, cpl_frame_type aType)
Create a new frame for a result file.
cpl_frameset * outframes
cpl_error_code muse_sky_lines_set_range(cpl_table *aLines, double aLow, double aHigh)
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 *aLines, double aMinflux)
Remove all lines below a certain flux limit.
cpl_frameset * inframes
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:158
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)