00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifdef HAVE_CONFIG_H
00023 #include <config.h>
00024 #endif
00025
00026
00027
00028
00029 #include <math.h>
00030 #include <string.h>
00031
00032 #include "muse_phys.h"
00033
00034 #include "muse_pfits.h"
00035
00036
00040
00041
00044
00052
00053 double
00054 muse_phys_nrindex_owens_saturation_pressure(double temp)
00055 {
00056 return -10474.0 + 116.43*temp - 0.43284*temp*temp + 0.00053840*pow(temp,3);
00057 }
00058
00059
00068
00069 void
00070 muse_phys_nrindex_owens_coeffs(double temp, double rhum, double pres,
00071 double *d1, double *d2)
00072 {
00073
00074 double ps = muse_phys_nrindex_owens_saturation_pressure(temp),
00075 p2 = rhum * ps,
00076 p1 = pres - p2;
00077
00078 *d1 = p1/temp * (1. + p1*(57.90e-8 - 9.3250e-4/temp + 0.25844/temp/temp));
00079 *d2 = p2/temp * (1. + p2*(1. + 3.7e-4*p2)
00080 * (-2.37321e-3 + 2.23366/temp - 710.792/temp/temp
00081 + 7.75141e4/pow(temp,3)));
00082 }
00083
00084
00093
00094 double
00095 muse_phys_nrindex_owens(double l, double d1, double d2)
00096 {
00097 double lisq = 1./(l*l);
00098 return 1. + ((2371.34 + 683939.7/(130. - lisq) + 4547.3/(38.9 - lisq)) * d1
00099 + (6487.31 + 58.058 * lisq - 0.71150 * lisq*lisq
00100 + 0.08851 * lisq*lisq*lisq) * d2) * 1.0e-8;
00101 }
00102
00103
00113
00114 double
00115 muse_phys_nrindex_edlen(double l, double t, double p, double pv)
00116 {
00117
00118 const double A = 8342.54, B = 2406147,
00119 C = 15998, D = 96095.43,
00120 E = 0.601, F = 0.00972, G = 0.003661;
00121 double S = 1./(l*l),
00122
00123
00124 n_5 = 1 + 1e-8 * (A + B / (130 - S) + C / (38.9 - S)),
00125 X = (1 + 1e-8 * (E - F * t) * p) / (1 + G * t),
00126 n_tp = 1 + p * (n_5 - 1) * X / D,
00127 n = n_tp - 10e-10 * (292.75 / (t + 273.15)) * (3.7345 - 0.0401 * S) * pv;
00128 return n;
00129 }
00130
00131
00146
00147 double
00148 muse_phys_nrindex_ciddor(double l, double T, double p, double xv, double xCO2)
00149 {
00150
00151 const double w0 = 295.235, w1 = 2.6422, w2 = -0.03238, w3 = 0.004028,
00152 k0 = 238.0185, k1 = 5792105, k2 = 57.362, k3 = 167917,
00153 a0 = 1.58123e-6, a1 = -2.9331e-8, a2 = 1.1043e-10,
00154 b0 = 5.707e-6, b1 = -2.051e-8,
00155 c0 = 1.9898e-4, c1 = -2.376e-6,
00156 d = 1.83e-11, e = -0.765e-8,
00157 p_R1 = 101325, T_R1 = 288.15,
00158 Z_a = 0.9995922115,
00159 rho_vs = 0.00985938,
00160 R = 8.314472, M_v = 0.018015;
00161 double t = T - 273.15,
00162 S = 1./(l*l),
00163
00164
00165 r_as = 1e-8 * ((k1 / (k0 - S)) + (k3 / (k2 - S))),
00166 r_vs = 1.022e-8 * (w0 + w1 * S + w2 * S*S + w3 * S*S*S),
00167
00168 M_a = 0.0289635 + 1.2011e-8 * (xCO2 - 400),
00169 r_axs = r_as * (1 + 5.34e-7 * (xCO2 - 450)),
00170
00171 Z_m = 1 - (p / T) * (a0 + a1 * t + a2 * t*t + (b0 + b1 * t) * xv
00172 + (c0 + c1 * t) * xv*xv)
00173 + (p/T)*(p/T) * (d + e * xv*xv),
00174 rho_axs = p_R1 * M_a / (Z_a * R * T_R1),
00175 rho_v = xv * p * M_v / (Z_m * R * T),
00176 rho_a = (1 - xv) * p * M_a / (Z_m * R * T),
00177 n = 1 + (rho_a / rho_axs) * r_axs + (rho_v / rho_vs) * r_vs;
00178 return n;
00179 }
00180
00181
00191
00192 double
00193 muse_phys_nrindex_filippenko(double l, double T, double f, double P)
00194 {
00195 double lisq = 1./(l*l);
00196
00197 double nl = 64.328 + 29498.1 / (146 - lisq) + 255.4 / (41 - lisq);
00198
00199 nl *= P / 720.883 * (1 + (1.049 - 0.0157 * T) * 1e-6 * P) / (1 + 0.003661 * T);
00200
00201 nl -= (0.0624 - 0.000680 * lisq) / (1 + 0.003661 * T) * f;
00202
00203 nl = nl * 1e-6 + 1;
00204 return nl;
00205 }
00206
00207
00259
00260 cpl_error_code
00261 muse_phys_air_to_vacuum(muse_pixtable *aPixtable, unsigned int aFlags)
00262 {
00263 cpl_ensure_code(aPixtable && aPixtable->header && aPixtable->table,
00264 CPL_ERROR_NULL_INPUT);
00265 cpl_ensure_code(muse_cpltable_check(aPixtable->table, muse_pixtable_def)
00266 == CPL_ERROR_NONE, CPL_ERROR_DATA_NOT_FOUND);
00267 cpl_ensure_code(aFlags != 0, CPL_ERROR_UNSUPPORTED_MODE);
00268
00269
00270
00271 if (cpl_propertylist_has(aPixtable->header, MUSE_HDR_PT_SPEC_TYPE)) {
00272 const char *spectype = cpl_propertylist_get_string(aPixtable->header,
00273 MUSE_HDR_PT_SPEC_TYPE);
00274 if (spectype && !strncmp(spectype, "WAVE", 4)) {
00275 cpl_msg_warning(__func__, "Pixel table has spectral type \"%s\", not in "
00276 "air wavelengths!", spectype);
00277 return cpl_error_set(__func__, CPL_ERROR_TYPE_MISMATCH);
00278 }
00279 if (spectype && strncmp(spectype, "AWAV", 4)) {
00280 cpl_msg_warning(__func__, "Pixel table has unknown spectral type \"%s\", "
00281 "not in air wavelengths, not doing conversion to vacuum!",
00282 spectype);
00283 return cpl_error_set(__func__, CPL_ERROR_TYPE_MISMATCH);
00284 }
00285 }
00286
00287
00288 double rhum = 0.,
00289 temp = 15. + 273.15,
00290 pres = 1013.25;
00291
00292 cpl_boolean realair = (aFlags & 0x3) >> 1;
00293 if (realair) {
00294
00295 cpl_errorstate prestate = cpl_errorstate_get();
00296 temp = muse_pfits_get_temp(aPixtable->header);
00297 if (!cpl_errorstate_is_equal(prestate)) {
00298 cpl_msg_warning(__func__, "Pixel table header does not contain temperature"
00299 ", no conversion to vacuum: %s", cpl_error_get_message());
00300 return CPL_ERROR_DATA_NOT_FOUND;
00301 }
00302 temp += 273.15;
00303
00304 prestate = cpl_errorstate_get();
00305 rhum = muse_pfits_get_rhum(aPixtable->header);
00306 if (!cpl_errorstate_is_equal(prestate)) {
00307 cpl_msg_warning(__func__, "Pixel table header does not contain relative "
00308 "humidity, no conversion to vacuum: %s",
00309 cpl_error_get_message());
00310 return CPL_ERROR_DATA_NOT_FOUND;
00311 }
00312 rhum /= 100.;
00313
00314 prestate = cpl_errorstate_get();
00315 pres = (muse_pfits_get_pres_start(aPixtable->header)
00316 + muse_pfits_get_pres_end(aPixtable->header)) / 2.;
00317 if (!cpl_errorstate_is_equal(prestate)) {
00318 cpl_msg_warning(__func__, "Pixel table header does not contain pressure "
00319 "values, no conversion to vacuum: %s",
00320 cpl_error_get_message());
00321 return CPL_ERROR_DATA_NOT_FOUND;
00322 }
00323 }
00324
00325
00326 unsigned int method = aFlags & 0xC;
00327
00328
00329
00330 double d1, d2,
00331 fp,
00332 p = pres * 100,
00333 t = temp - 273.15,
00334 pv,
00335 xv;
00336 if (method == MUSE_PHYS_METHOD_OWENS) {
00337 muse_phys_nrindex_owens_coeffs(temp, rhum, pres, &d1, &d2);
00338 cpl_msg_info(__func__, "Air to vacuum conversion for T=%.2f K, RH=%.2f %%, "
00339 "pres=%.1f mbar (%s, Owens)", temp, rhum*100., pres,
00340 realair ? "measured parameters" : "standard air");
00341 } else if (method == MUSE_PHYS_METHOD_EDLEN ||
00342 method == MUSE_PHYS_METHOD_CIDDOR) {
00343
00344
00345
00346
00347 const double T = temp,
00348 K1 = 1.16705214528E+03,
00349 K2 = -7.24213167032E+05,
00350 K3 = -1.70738469401E+01,
00351 K4 = 1.20208247025E+04,
00352 K5 = -3.23255503223E+06,
00353 K6 = 1.49151086135E+01,
00354 K7 = -4.82326573616E+03,
00355 K8 = 4.05113405421E+05,
00356 K9 = -2.38555575678E-01,
00357 K10 = 6.50175348448E+02,
00358 Omega = T + K9 / (T - K10),
00359 A = Omega*Omega + K1 * Omega + K2,
00360 B = K3 * Omega*Omega + K4 * Omega + K5,
00361 C = K6 * Omega*Omega + K7 * Omega + K8,
00362 X = -B + sqrt(B*B - 4 * A * C);
00363 double psv_w = 1e6 * pow(2 * C / X, 4);
00364
00365
00366 const double A1 = -13.928169,
00367 A2 = 34.7078238,
00368 Theta = T / 273.16,
00369 Y = A1 * (1 - pow(Theta, -1.5)) + A2 * (1 - pow(Theta, -1.25));
00370 double psv_i = 611.657 * exp(Y);
00371
00372
00373
00374 if (t > 0.) {
00375 pv = rhum * psv_w;
00376 } else {
00377 pv = rhum * psv_i;
00378 }
00379
00380 const double alpha = 1.00062,
00381 beta = 3.14e-8,
00382 gamma = 5.60e-7;
00383 double f = alpha + beta * p + gamma * t*t;
00384
00385 if (t > 0.) {
00386 xv = rhum * f * psv_w / p;
00387 } else {
00388 xv = rhum * f * psv_i / p;
00389 }
00390 cpl_msg_info(__func__, "Air to vacuum conversion for T=%.2f degC, RH=%.2f "
00391 "%%, p=%.1f Pa (%s, %s)", t, rhum*100., p,
00392 realair ? "measured parameters" : "standard air",
00393 method == MUSE_PHYS_METHOD_EDLEN ? "Edlen" : "Ciddor");
00394 } else {
00395
00396 double ps = muse_phys_nrindex_owens_saturation_pressure(temp);
00397
00398 fp = rhum * ps * MUSE_PHYS_hPa_TO_mmHg;
00399 temp -= 273.15;
00400 pres *= MUSE_PHYS_hPa_TO_mmHg;
00401 cpl_msg_info(__func__, "Air to vacuum conversion for T=%.2f degC, fp=%.3f "
00402 "mmHg, P=%.1f mmHg (%s, Filippenko)", temp, fp, pres,
00403 realair ? "measured parameters" : "standard air");
00404 }
00405
00406 float *lbda = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA);
00407 cpl_size i, nmax = muse_pixtable_get_nrow(aPixtable);
00408 #pragma omp parallel for default(none) \
00409 shared(d1, d2, fp, lbda, method, nmax, p, pres, pv, t, temp, xv)
00410 for (i = 0; i < nmax; i++) {
00411 double nr,
00412 lambda = lbda[i] * 1e-4;
00413 if (method == MUSE_PHYS_METHOD_OWENS) {
00414 nr = muse_phys_nrindex_owens(lambda, d1, d2);
00415 } else if (method == MUSE_PHYS_METHOD_EDLEN) {
00416 nr = muse_phys_nrindex_edlen(lambda, t, p, pv);
00417 } else if (method == MUSE_PHYS_METHOD_CIDDOR) {
00418
00419
00420 nr = muse_phys_nrindex_ciddor(lambda, temp, p, xv, 450.);
00421 } else {
00422 nr = muse_phys_nrindex_filippenko(lambda, temp, fp, pres);
00423 }
00424
00425 lbda[i] *= nr;
00426 }
00427
00428
00429 muse_pixtable_compute_limits(aPixtable);
00430
00431
00432 cpl_propertylist_update_string(aPixtable->header, MUSE_HDR_PT_SPEC_TYPE,
00433 "WAVE");
00434 cpl_propertylist_set_comment(aPixtable->header, MUSE_HDR_PT_SPEC_TYPE,
00435 MUSE_HDR_PT_SPEC_TYPE_COMMENT);
00436
00437 return CPL_ERROR_NONE;
00438 }
00439