AntarcticaAtmosphere.cxx
1 #include "AntarcticAtmosphere.h"
2 #include "TGraph.h"
3 #include "TAxis.h"
4 #include "Adu5Pat.h"
5 #include "TObjArray.h"
6 #include "TObjString.h"
7 #include "TSystem.h"
8 #include "math.h"
9 #include <algorithm>
10 
11 #ifdef USE_GEOGRAPHIC_LIB
12 #include "GeographicLib/Geoid.hpp"
13 #endif
14 
15 
16 const double REARTH = 6369;
17 const double GMR = 34.163195;
18 
19 
20 /**************************+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
21 ****** US Atmosphere
22  ***************************/
23 
24 
25 /*
26  * It looks like US and INTL atmosphere are basically the same...
27  *
28  * much of this is from http://www.pdas.com/programs/atmos.f90 */
29 const int NTAB= 8;
30 static double htab[] = {0.0, 11.0, 20.0, 32.0, 47.0, 51.0, 71.0, 84.852 };
31 static double ttab[] = { 288.15, 216.65, 216.65, 228.65, 270.65, 270.65, 214.65, 186.946 };
32 static double ptab[] = { 1.0, 2.233611E-1, 5.403295E-2, 8.5666784E-3, 1.0945601E-3, 6.6063531E-4, 3.9046834E-5, 3.68501E-6 };
33 static double gtab[] = {-6.5, 0.0, 1.0, 2.8, 0.0, -2.8, -2.0, 0.0 };
34 
35 static int us_atmosphere(double alt_km, double * rel_rho,double * rel_P, double * rel_T)
36 {
37  int i,j,k;
38  double h;
39  double tgrad, tbase;
40  double tlocal;
41  double deltah;
42  double sigma, delta, theta;
43 
44  h = alt_km * REARTH / (alt_km + REARTH);
45 
46  i = 0;
47  j = NTAB-1;
48 
49  while (j > i+1)
50  {
51  k = (i+j)/2;
52 
53  if (h < htab[k])
54  {
55  j = k;
56  }
57  else
58  {
59  i = k;
60  }
61  }
62 
63  tgrad = gtab[i];
64  tbase =ttab[i];
65  deltah = h - htab[i];
66  tlocal = tbase + tgrad * deltah;
67 
68  theta = tlocal / ttab[0];
69 
70  if (tgrad == 0)
71  {
72  delta = ptab[i] * exp(- GMR*deltah / tbase);
73  }
74  else
75  {
76  delta = ptab[i] * pow(tbase / tlocal , GMR/tgrad);
77  }
78 
79  sigma = delta/theta;
80 
81  *rel_rho = sigma;
82  *rel_P = delta;
83  *rel_T = theta;
84 
85  return 0;
86 }
87 
88 int AntarcticAtmosphere::StandardUS::computeAtmosphere(double h, Pars *p, double phi) const
89 {
90 
91  (void) phi;
92 
93  int ret = us_atmosphere(h/1e3, &p->rho, &p->P, &p->T);
94  p->T *= sea_level_T;
95  p->P *= sea_level_P;
96  p->rho *= 1.225 * sea_level_T / sea_level_P;
97  p->N = 77.6 * p->P / p->T;
98 
99  return ret;
100 }
101 
102 double AntarcticAtmosphere::ArtificialInversion::correction(double h) const
103 {
104  if (h > hmax) return 1;
105  else return 1 - (hmax - h)/hmax * Amax;
106 
107 }
108 
109 
110 int AntarcticAtmosphere::ArtificialInversion::computeAtmosphere(double h, Pars *p, double phi) const
111 {
112  (void) phi;
113  int ret = m.computeAtmosphere(h,p);
114  p->T *= correction(h);
115  p->rho *= correction(h);
116  p->N *= correction(h);
117  return ret;
118 }
119 
120 double AntarcticAtmosphere::ArtificialInversion::get(double h, Par p, double phi) const
121 {
122  (void) phi;
123  //short circuilt easy calculation
124  if (p == REFRACTIVITY) return m.get(h,p) * correction(h);
125  else if (p == TEMPERATURE) return m.get(h,p) * correction(h);
126  else if (p == DENSITY) return m.get(h,p) * correction(h);
127  else return m.get(h,p) ;
128 }
129 
130 
131 
132 int AntarcticAtmosphere::ExponentialRefractivity::computeAtmosphere(double h, Pars *p, double phi) const
133 {
134  (void) phi;
135 
136  int ret = us_atmosphere(h/1e3, &p->rho, &p->P, &p->T);
137  p->T *= sea_level_T;
138  p->P *= sea_level_P;
139  p->rho *= 1.225 * sea_level_T / sea_level_P;
140  p->N = k_A * exp(-k_B * h);
141  return ret;
142 }
143 
144 double AntarcticAtmosphere::ExponentialRefractivity::get(double h, Par p, double phi) const
145 {
146  (void) phi;
147  //short circuilt easy calculation
148  if (p == REFRACTIVITY) return k_A * exp(-k_B* h) ;
149  Pars x;
150  computeAtmosphere(h,&x);
151  switch (p)
152  {
153  case DENSITY:
154  return x.rho;
155  case PRESSURE:
156  return x.P;
157  case TEMPERATURE:
158  return x.T;
159  case REFRACTIVITY:
160  return x.N;
161  default:
162  return -9999;
163  }
164 }
165 
166 
167 
168 #ifdef USE_GEOGRAPHIC_LIB
169 
170 static const GeographicLib::Geoid & geoid2008_1()
171 {
172  static GeographicLib::Geoid g("egm2008-1");
173  return g;
174 }
175 
176 static GeographicLib::Geoid & geoid96_5()
177 {
178  static GeographicLib::Geoid g("egm96-5");
179  return g;
180 }
181 
182 static const GeographicLib::Geoid & getGeoid(AntarcticAtmosphere::Geoid g)
183 {
184  switch (g)
185  {
186  case AntarcticAtmosphere::EGM2008_1:
187  return geoid2008_1();
188  default:
189  return geoid96_5();
190  }
191 }
192 
193 double AntarcticAtmosphere::MSLtoWGS84(double h, double lat, double lon, Geoid g)
194 {
195  return getGeoid(g).ConvertHeight ( lat, lon, h, GeographicLib::Geoid::GEOIDTOELLIPSOID);
196 }
197 
198 double AntarcticAtmosphere::WGS84toMSL(double lat, double lon, double alt, Geoid g)
199 {
200  return getGeoid(g).ConvertHeight(lat,lon,alt, GeographicLib::Geoid::ELLIPSOIDTOGEOID);
201 }
202 
203 double AntarcticAtmosphere::WGS84toMSL(const Adu5Pat * pat, Geoid g)
204 {
205  return getGeoid(g).ConvertHeight ( pat->latitude, pat->longitude, pat->altitude, GeographicLib::Geoid::ELLIPSOIDTOGEOID);
206 }
207 
208 #else
209 
210 double AntarcticAtmosphere::WGS84toMSL(const Adu5Pat * pat, Geoid g )
211 {
212  static int nagged = 0;
213 
214  if (nagged++ < 5)
215  {
216  fprintf(stderr,"Need GeographicLib for MSL conversions\n");
217  }
218  return pat->altitude;
219 
220 }
221 
222 
223 double AntarcticAtmosphere::WGS84toMSL(double lat, double lon, double alt, Geoid g)
224 {
225 
226  static int nagged = 0;
227 
228  if (nagged++ < 5)
229  {
230  fprintf(stderr,"Need GeographicLib for MSL conversions\n");
231  }
232 
233  return alt;
234 }
235 
236 double AntarcticAtmosphere::MSLtoWGS84(double h, double lat, double lon, Geoid g)
237 {
238  static int nagged = 0;
239 
240  if (nagged++ < 5)
241  {
242  fprintf(stderr,"Need GeographicLib for MSL conversions\n");
243  }
244  return h;
245 
246 }
247 
248 
249 #endif
250 
251 
252 TGraph * AntarcticAtmosphere::AtmosphericModel::makeGraph(double hmin, double hmax, int nh, Par p, bool alt_on_x, double phi) const
253 {
254 
255  TGraph * g = new TGraph(nh);
256  g->SetTitle(name());
257  (alt_on_x ? g->GetYaxis() : g->GetXaxis())->SetTitle( p== DENSITY ? "Density (kg/m^3)" :
258  p== PRESSURE ? "Pressure (hPa) " :
259  p== TEMPERATURE ? "Temperature (K)" :
260  "(n - 1) #times 10^{6}");
261 
262  (alt_on_x ? g->GetXaxis() : g->GetYaxis())->SetTitle("Height above MSL (m)");
263 
264 
265  for (int i = 0; i < nh; i++)
266  {
267  double h = hmin + i*(hmax-hmin)/(nh-1);
268 
269  double val = get(h,p,phi);
270  if (alt_on_x)
271  g->SetPoint(i, h,val);
272  else
273  g->SetPoint(i, val,h);
274  }
275 
276  return g;
277 }
278 
279 
280 double AntarcticAtmosphere::AtmosphericModel::get(double h, Par p, double phi) const
281 {
282  Pars x;
283  computeAtmosphere(h,&x,phi);
284  switch (p)
285  {
286  case DENSITY:
287  return x.rho;
288  case PRESSURE:
289  return x.P;
290  case TEMPERATURE:
291  return x.T;
292  case REFRACTIVITY:
293  return x.N;
294  default:
295  return -9999;
296  }
297 }
298 
299 
300 const AntarcticAtmosphere::ExponentialRefractivity & AntarcticAtmosphere::ITURefractivity()
301 {
302  static ExponentialRefractivity er(315,0.1361e-3);
303  return er;
304 }
305 
306 
307 
308 //hopefully the format didn't change...
309 static const char * sp_base = "ftp://amrc.ssec.wisc.edu/pub/southpole/radiosonde";
310 
311 
312 struct atm_meas
313 {
314  double h; // m, msl
315  double P; // hPa
316  double T; // kelvin
317  double N; // refractivity (derived)
318  double rho; // density (derived)
319 
320  bool operator< (const atm_meas & other) const
321  {
322  return h < other.h;
323  }
324 };
325 
326 
327 
328 int AntarcticAtmosphere::SPRadiosonde::computeAtmosphere(double h, Pars *p, double phi) const
329 {
330  (void) phi;
331  p->rho = h > rho.GetX()[rho.GetN()-1] ? rhofit.Eval(h) : rho.Eval(h);
332  p->P = h > P.GetX()[P.GetN()-1] ? Pfit.Eval(h) : P.Eval(h);
333  p->T = h > T.GetX()[T.GetN()-1] ? Tfit.Eval(h) : T.Eval(h);
334  p->N = h > N.GetX()[N.GetN()-1] ? Nfit.Eval(h) : N.Eval(h);
335  return 0;
336 }
337 
338 
339 
340 AntarcticAtmosphere::SPRadiosonde::SPRadiosonde(int year, int mon, int day, bool early)
341  :
342  Nfit("Nfit","expo",10e3,50e3) ,
343  Pfit("Pfit","expo",10e3,50e3) ,
344  Tfit("Tfit","expo",10e3,50e3) ,
345  rhofit("rhofit","expo",10e3,50e3)
346 {
347  int late_time = 12;
348  TString cmd;
349  char * local_dir = getenv("RADIOSONDE_DIR");
350  cmd.Form("%s %s/%d/%02d%02d%02d%02ddat.txt",local_dir ? "cat " : "curl", local_dir ? local_dir : sp_base,year,mon,day,year % 100, early? 0 :late_time);
351  TString data = gSystem->GetFromPipe(cmd.Data());
352  loaded = false;
353 
354  if (year < 2015)
355  {
356  fprintf(stderr,"Haven't implemented parsing older radiosonde profiles yet...\n");
357  return;
358  }
359 
360  my_name.Form("SP Radiosonde %d-%d-%d %s", year,mon,day, early ? " early " : "late");
361 
362  //split into lines
363 
364  TObjArray *lines = data.Tokenize("\n");
365  lines->SetOwner(true);
366 
367  // the south pole data format kindly changes from year to year...
368 
369 
370  std::vector<atm_meas> v;
371 
372 
373  //skip first 9 lines
374  for (int i = 10; i < lines->GetEntries(); i++)
375  {
376  const char * line = ((TObjString*)lines->At(i))->GetString().Data();
377 
378  int s,h,rh,dir;
379  float hPa,T,dwp,speed;
380  sscanf(line,"%d %d %g %g %g %d %g %d", &s,&h,&hPa,&T,&dwp,&rh,&speed,&dir);
381 
382  atm_meas m;
383  m.h = h;
384  m.P = hPa;
385  m.T= T + 273;
386 
387  //I guess we assume it's ice?
388 
389  double EF=1+1e-4 * (2.2 +m.P*(0.00382 + 6.4e-7 * (T*T)));
390  const double a = 6.115;
391  const double b = 23.036;
392  const double c = 279.82;
393  const double d = 333.7;
394  double e_s = EF * a * exp( (( b-T/d)*T)/(T+c));
395 
396  double e= e_s*rh/100.;
397 
398  m.N = 77.6 * m.P / m.T - 5.6 * e / m.T + 3e5 * e / (m.T*m.T);
399 
400  m.rho = (m.P*100) / (287.058 *m.T); //ignore wet for now since the air is plenty dry
401  v.push_back(m);
402 
403  }
404 
405  std::sort(v.begin(), v.end());
406 
407  delete lines;
408 
409  loaded = v.size();
410 
411 
412  for (unsigned i = 0; i < v.size(); i++)
413  {
414 // printf("%g %g %g %g, %g\n",v[i].h,v[i].P, v[i].T, v[i].N, v[i].rho);
415  P.SetPoint(i, v[i].h, v[i].P);
416  T.SetPoint(i, v[i].h, v[i].T);
417  N.SetPoint(i, v[i].h, v[i].N);
418  rho.SetPoint(i, v[i].h, v[i].rho);
419  }
420 
421 #if ROOT_VERSION_CODE >= ROOT_VERSION(6,7,0)
422  P.SetBit(TGraph::kIsSortedX);
423  T.SetBit(TGraph::kIsSortedX);
424  N.SetBit(TGraph::kIsSortedX);
425  rho.SetBit(TGraph::kIsSortedX);
426 #endif
427 
428  N.Fit(&Nfit,"RQ");
429  P.Fit(&Pfit,"RQ");
430  T.Fit(&Tfit,"RQ");
431  rho.Fit(&rhofit,"RQ");
432 }
433 
434 
435 int AntarcticAtmosphere::InterpolatedAtmosphere::computeAtmosphere(double h, Pars * p, double phi) const
436 {
437  //find the two atmospheres to interpolate in between
438 
439  if (atms.size()==0) return 1;
440 
441  if (atms.size()==1)
442  {
443  (*atms.begin()).second->computeAtmosphere(h,p,phi);
444  return 0;
445  }
446 
447  //otherwise let's find the bounds
448 
449  std::set<std::pair<double,const AtmosphericModel*> >::const_iterator it = atms.lower_bound(std::pair<double,const AtmosphericModel*>(phi,0));
450 
451  //this means our phi is smaller than the minimum
452  if (it == atms.begin())
453  {
454  (*atms.begin()).second->computeAtmosphere(h,p,phi);
455  return 0;
456  }
457 
458  //this means our phi is bigger than maximum
459  if (it == atms.end()) //our phi is greater than the last one
460  {
461 
462  (*atms.rbegin()).second->computeAtmosphere(h,p,phi);
463  return 0;
464  }
465 
466 
467  //otherwise we want to interpolate between the two
468  double phi1 = (*it).first;
469  const AtmosphericModel * m1 =(*it).second;
470  it--;
471  double phi0 = (*it).first;
472  const AtmosphericModel * m0 =(*it).second;
473 
474  double frac= phi1==phi0 ? 0.5: (phi-phi0)/(phi1-phi0);
475 
476 // printf("%g | %g %g %g | %g\n", h, phi, phi0, phi1,frac);
477  Pars p0;
478  Pars p1;
479 
480  m0->computeAtmosphere(h,&p0,phi);
481  m1->computeAtmosphere(h,&p1,phi);
482 
483  p->rho = p0.rho * (1-frac) + frac*p1.rho;
484  p->P = p0.P * (1-frac) + frac*p1.P;
485  p->T = p0.T * (1-frac) + frac*p1.T;
486  p->N = p0.N * (1-frac) + frac*p1.N;
487 
488  return 0;
489 
490 }
491 
492 
Adu5Pat – The ADU5 Position and Attitude Data.
Definition: Adu5Pat.h:26
Float_t latitude
In degrees.
Definition: Adu5Pat.h:42
Float_t longitude
In degrees.
Definition: Adu5Pat.h:43
SPRadiosonde(int year, int month, int day, bool early=true)
Float_t altitude
In metres.
Definition: Adu5Pat.h:44