MUSE Pipeline Reference Manual  2.1.1
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 #pragma omp critical(muse_processing_output_frames)
150  cpl_frameset_insert(aProcessing->outframes, frame);
151  } else {
152  cpl_frame_delete(frame);
153  }
154  return rc;
155 }
156 
157 /*----------------------------------------------------------------------------*/
167 /*----------------------------------------------------------------------------*/
168 cpl_table *
170 {
171  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
172  cpl_frameset *frames = muse_frameset_find(aProcessing->inframes,
173  MUSE_TAG_SKY_LINES, 0, CPL_FALSE);
174  cpl_errorstate es = cpl_errorstate_get();
175  cpl_frame *frame = cpl_frameset_get_position(frames, 0);
176  if (frame == NULL) {
177  cpl_frameset_delete(frames);
178  cpl_errorstate_set(es); /* swallow the illegal input state */
179  cpl_msg_warning(__func__, "No sky lines found in input frameset!");
180  return NULL;
181  }
182  const char *fn = cpl_frame_get_filename(frame);
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_msg_warning(__func__, "Could not load sky lines from \"%s\"", fn);
187  cpl_frameset_delete(frames);
188  return NULL;
189  }
190 
191  // Output message, no matter if unit conversions happen
192  // (so that the following messages can be related to a file name)
193  cpl_msg_info(__func__, "Loaded sky lines from \"%s\"", fn);
194  // Convert tables with unscaled flux into ones with scaled flux.
195  if (lines) {
196  cpl_msg_indent_more();
197  const char *unit = cpl_table_get_column_unit(lines, "flux");
198  if (!unit) {
199  cpl_msg_warning(__func__, "No flux unit given!");
200  }
201  if (unit && strcmp(unit, "erg/(s cm^2 arcsec^2)") == 0) {
202  cpl_msg_info(__func__, "Scaling flux by 1e20.");
203  cpl_table_multiply_scalar(lines, "flux", 1e20);
204  cpl_table_set_column_unit(lines, "flux",
205  "10**(-20)*erg/(s cm^2 arcsec^2)");
206  }
207  if (unit && strcmp(unit, "10**(-20)*erg/(s cm^2 arcsec^2)") != 0) {
208  cpl_msg_warning(__func__, "Unsupported flux unit \"%s\".",
209  cpl_table_get_column_unit(lines, "flux"));
210  }
211  cpl_msg_indent_less();
212  } /* if lines */
213 
214  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
215  cpl_frameset_delete(frames);
216 
217  cpl_table *sky_lines = muse_sky_lines_create(lines, oh_transitions,
218  MUSE_SKY_T_VIBR);
219  cpl_table_delete(oh_transitions);
220  cpl_table_delete(lines);
221  return sky_lines;
222 }
223 
224 /*----------------------------------------------------------------------------*/
237 /*----------------------------------------------------------------------------*/
238 cpl_error_code
239 muse_sky_lines_apply_strength(cpl_table *aLines, const cpl_array *aStrength)
240 {
241  cpl_ensure_code(aLines != NULL, CPL_ERROR_NULL_INPUT);
242  cpl_ensure_code(aStrength != NULL, CPL_ERROR_NULL_INPUT);
243 
244  int *group = cpl_table_get_data_int(aLines, "group");
245  cpl_ensure_code(group != NULL, CPL_ERROR_ILLEGAL_INPUT);
246  double *flux = cpl_table_get_data_double(aLines, "flux");
247  cpl_ensure_code(flux != NULL, CPL_ERROR_ILLEGAL_INPUT);
248  cpl_size nRows = cpl_table_get_nrow(aLines);
249  cpl_size i;
250  for (i = 0; i < nRows; i++, group++, flux++) {
251  *flux *= cpl_array_get(aStrength, *group, NULL);
252  }
253  return CPL_ERROR_NONE;
254 }
255 
256 /*----------------------------------------------------------------------------*/
266 /*----------------------------------------------------------------------------*/
267 cpl_error_code
268 muse_sky_lines_cut(cpl_table *aLines, double aMinflux)
269 {
270  cpl_ensure_code(aLines != NULL, CPL_ERROR_NULL_INPUT);
271  cpl_table_select_all(aLines);
272  cpl_table_and_selected_double(aLines, "flux", CPL_LESS_THAN, aMinflux);
273  cpl_table_erase_selected(aLines);
274  return CPL_ERROR_NONE;
275 }
276 
277 /*----------------------------------------------------------------------------*/
298 /*----------------------------------------------------------------------------*/
299 static cpl_table *
300 muse_sky_lines_create_oh(const cpl_table *aTransitions, double aTemperature)
301 {
302  if ((aTransitions == NULL) || (cpl_table_get_nrow(aTransitions) == 0)) {
304  }
305 
306  const int max_level = cpl_table_get_column_max(aTransitions, "v_u");
307  const int min_level = cpl_table_get_column_min(aTransitions, "v_u");
308  cpl_ensure(max_level-min_level < 100, CPL_ERROR_UNSUPPORTED_MODE, NULL);
309  const int n_transitions = cpl_table_get_nrow(aTransitions);
310 
311  cpl_table *lines = muse_cpltable_new(muse_sky_lines_lines_def, n_transitions);
312 
313  cpl_table_copy_data_string(lines, "name",
314  cpl_table_get_data_string_const(aTransitions,
315  "name"));
316  cpl_table_copy_data_double(lines, "lambda",
317  cpl_table_get_data_double_const(aTransitions,
318  "lambda"));
319  cpl_table_copy_data_int(lines, "group",
320  cpl_table_get_data_int_const(aTransitions, "v_u"));
321  cpl_table_subtract_scalar(lines, "group", min_level);
322 
323  cpl_table_fill_column_window_double(lines, "flux", 0, n_transitions, 0.0);
324 
325  const double k_B = 1.3806504e-23; /* Boltzmann constant [eV/K] */
326  const double hc = 1.98623e-8; /* h * c [Angstrom*erg] */
327  cpl_table_copy_data_double(lines, "flux",
328  cpl_table_get_data_double_const(aTransitions, "E_u"));
329  cpl_table_divide_scalar(lines, "flux", -k_B * aTemperature);
330  cpl_table_exponential_column(lines, "flux", CPL_MATH_E);
331 
332  cpl_table_duplicate_column(lines, "J_u", aTransitions, "J_u");
333  cpl_table_multiply_scalar(lines, "J_u", 2.0);
334  cpl_table_add_scalar(lines, "J_u", 1.0);
335  cpl_table_multiply_columns(lines, "flux", "J_u");
336  cpl_table_erase_column(lines, "J_u");
337 
338  cpl_table_fill_column_window_int(lines, "dq", 0, n_transitions, 0);
339 
340  double *flux = cpl_table_get_data_double(lines, "flux");
341  const int *group = cpl_table_get_data_int_const(lines, "group");
342 
343  double q[max_level - min_level + 1];
344  memset(q, 0, (max_level - min_level + 1)*sizeof(double));
345  int i_transition;
346  for (i_transition = 0; i_transition < n_transitions; i_transition++,
347  group++, flux++) {
348  q[*group] += *flux;
349  }
350 
351  cpl_table_duplicate_column(lines, "A", aTransitions, "A");
352  cpl_table_multiply_columns(lines, "flux", "A");
353  cpl_table_erase_column(lines, "A");
354  cpl_table_multiply_scalar(lines, "flux", 2.0 * hc);
355  cpl_table_divide_columns(lines, "flux", "lambda");
356 
357  flux = cpl_table_get_data_double(lines, "flux");
358  group = cpl_table_get_data_int_const(lines, "group");
359 
360  for (i_transition = 0; i_transition < n_transitions; i_transition++,
361  flux++, group++) {
362  *flux /= q[*group];
363  }
364 
365  cpl_table_multiply_scalar(lines, "flux", 1e20); // use 10^-20 scaling
366  // ad-hoc correction to get flux in the correct range
367  cpl_table_divide_scalar(lines, "flux", 50);
368 
369  muse_sky_lines_reindex_groups(lines);
370  return lines;
371 }
372 
373 /*----------------------------------------------------------------------------*/
387 /*----------------------------------------------------------------------------*/
388 cpl_table *
389 muse_sky_lines_create(const cpl_table *aLines,
390  const cpl_table *aOh_transitions, double aT_vibr)
391 {
392  int group_start = (aLines && cpl_table_get_nrow(aLines) > 0)?
393  cpl_table_get_column_max(aLines, "group") + 1: 0;
394  cpl_table *res = muse_sky_lines_create_oh(aOh_transitions, aT_vibr);
395  cpl_errorstate prestate = cpl_errorstate_get();
396  if (aLines) {
397  cpl_table_add_scalar(res, "group", group_start);
398  cpl_table_insert(res, aLines, 0);
399  }
400  if (!cpl_errorstate_is_equal(prestate)) {
401  cpl_msg_error(__func__, "while cpl_table_insert(): %s, %s",
402  cpl_table_get_column_unit(res, "flux"),
403  cpl_table_get_column_unit(aLines, "flux"));
404  cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
405  cpl_errorstate_set(prestate);
406  }
407  return res;
408 }
409 
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:160
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)