ReFRACtor
dispersion_fit.cc
Go to the documentation of this file.
1 #include "dispersion_fit.h"
2 #include "old_constant.h"
3 #include "polynomial_eval.h"
4 #include "linear_algebra.h"
5 #include <algorithm>
6 
7 using namespace FullPhysics;
8 using namespace blitz;
9 
10 #ifdef HAVE_LUA
11 #include "register_lua.h"
13 .def(luabind::constructor<const boost::shared_ptr<Level1bSampleCoefficient>&>())
14 .def("fit", &DispersionFit::fit)
15 .def("shift", &DispersionFit::shift)
17 #endif
18 
19 // Old hard coded values:
20 // line position of strong, isolated solar line
21 //const static double ABAND_SOLAR_LINE = 12985.16325e0;
22 // this takes into account the offset of the GOSAT band 1 ILS.
23 //const static double ABAND_ILS_OFFSET = 0.203e0;
24 // search range
25 //const static double ABAND_DISP_SEARCH_RANGE[] = { 12983.0, 12988.0 };
26 //const static double DISPERSION_OFFSET_SCALING[] = { 1, 0.48, 0.38 };
27 
28 // For solar doppler correction
29 const static double CON = 6.73951496e-03; // parameter for calculating geocentric from geodetic latitude
30 const static double EARTH_ROT_SPD = 2.0*OldConstant::pi/86164.09054e0; // earth angular rotation frequency [1/sec]
31 const static double RADIUS = 6378.137e0; // Earth equatorial radius, in km
32 
33 //-----------------------------------------------------------------------
35 //-----------------------------------------------------------------------
36 
38 : l1b(Level1b)
39 {
40 }
41 
42 blitz::Array<double, 2> DispersionFit::fit(const blitz::Array<double, 2> disp_initial,
43  const DoubleWithUnit& aband_solar_line_location,
44  const DoubleWithUnit& aband_solar_line_width,
45  const DoubleWithUnit& aband_search_width,
46  const DoubleWithUnit& aband_ils_offset,
47  const ArrayWithUnit<double, 1>& offset_scaling) const
48 {
49  firstIndex i1; secondIndex i2; thirdIndex i3;
50  Range ra = Range::all();
51 
52  // Data where solar line is present
53  Array<double, 1> aband_data = l1b->radiance(0).data();
54 
55  // True location of solar line to fit
56  double wn_s0 = aband_solar_line_location.value + aband_ils_offset.value;
57 
58  // Calculate the Speed of Earth center towards sun
59  double V_cen = 497.2 * sin((l1b->time(0).frac_day_of_year() - 4.1) / 365.25 * 2 * OldConstant::pi); // simple approximate model, good to ~ 10 m/s.
60 
61  // Calculate the rotational component of the solar doppler velocity
62  double sza_r = l1b->solar_zenith(0).convert(Unit("rad")).value;
63  double saz_r = l1b->solar_azimuth(0).convert(Unit("rad")).value;
64  double lat_r = l1b->latitude(0).convert(Unit("rad")).value;
65  double geo_lat = atan(tan(lat_r) / (1.0 + CON));
66  double Rloc = 1000.0 * RADIUS / sqrt(1.0 + CON * std::pow(sin(geo_lat), 2));
67  double V_rot = -EARTH_ROT_SPD * Rloc * sin(sza_r) * cos(saz_r - 0.5 * OldConstant::pi) * cos(geo_lat);
68 
69  double solar_doppler_velocity = V_cen + V_rot;
70 
71  // Modify position of strong solar line to include solar doppler shift
72  // takes into account the solar doppler shift
73  double wn_s = wn_s0 * (1.0e0 - solar_doppler_velocity / OldConstant::speed_of_light.value);
74 
75  // Create aband dispersion array, 1 indexed
76  // Coeffs are in reverse order from that Poly1d expects
77  Array<double, 1> aband_wn(aband_data.rows());
78  Poly1d aband_poly(disp_initial(0, ra).reverse(firstDim));
79  for(int pix = 0; pix < aband_wn.rows(); pix++)
80  aband_wn(pix) = aband_poly(pix+1);
81 
82  // (2) CONSTRUCT THE X, Y FUNCTION TO FIT
83  //
84  // Use intersection of the points above and below range threshold
85  // Make sure to sort the resulting indexes since set.intersection
86  // does not guarantee anything about ordering
87  Array<double, 1>::const_iterator srch_beg =
88  std::lower_bound(aband_wn.begin(), aband_wn.end(), aband_solar_line_location.value - aband_search_width.value);
89  Array<double, 1>::const_iterator srch_end =
90  std::upper_bound(aband_wn.begin(), aband_wn.end(), aband_solar_line_location.value + aband_search_width.value);
91  Range srch_range(srch_beg.position()(0), srch_end.position()(0));
92  Array<double, 1> srch_wn(aband_wn(srch_range));
93  Array<double, 1> srch_data(aband_data(srch_range));
94 
95  if (srch_wn.rows() == 0)
96  throw Exception("Could not find any points in the aband dispersion which encompass the fitting search window");
97 
98  Array<double, 1> x( srch_wn - wn_s );
99 
100  double mxmeas = max(srch_data); // maximum value
101  double m2 = max(mxmeas - srch_data);
102  Array<double, 1> y( (mxmeas - srch_data) / m2 ); // this should look like a gaussian
103 
104  double cont_mean = 0;
105  int cont_count = 0;
106  for(int idx = 0; idx < y.rows(); idx++) {
107  if(y(idx) < 0.1) {
108  cont_mean = cont_mean + y(idx);
109  cont_count++;
110  }
111  }
112  cont_mean = cont_mean / cont_count;
113 
114  y = y - cont_mean; // subtract off the "continuum"
115  int pos = maxIndex(y)(0); // pos = index of the maximum value of y
116  double fg = -x(pos); // first-guess value of spectral shift
117 
118  // (3) PERFORM A SIMPLE FIT ASSUMING A PERFECTLY LINEAR MODEL
119  // MODEL IS A GAUSSIAN WITH SIGMA=0.2 CM^-1
120  // FIT PARAMETERS ARE AMPLITUDE AND CENTER OF THE GAUSSIAN
121  Array<double, 2> K(y.rows(), 2);
122  double sig2 = aband_solar_line_width.value;
123  Array<double, 1> ygauss( exp(-pow2(x + fg) / (2.0 * sig2)) );
124 
125  K(ra, 0) = ygauss;
126  K(ra, 1) = -ygauss / sig2 * (x + fg);
127 
128  // Note that KtK is a 2x2 symmetric matrix and can be inverted analytically if desired.
129  // form matrix (Kt K)^{-1} Kt
130  Array<double, 2> Kt( K.transpose(secondDim, firstDim) );
131 
132  // Kt * K, matrix multiplication using blitz tensor notaion
133  Array<double, 2> KtK( sum(Kt(i1, i3) * K(i3, i2), i3) );
134  Array<double, 2> KtK_m1 = generalized_inverse(KtK);
135  Array<double, 1> yminusg(y - ygauss);
136  Array<double, 1> Kty(sum(Kt(i1, i2) * yminusg(i2), i2));
137  Array<double, 1> delta(sum(KtK_m1(i1, i2) * Kty(i2), i2));
138 
139  Array<double, 1> fit(delta.rows());
140  fit = 1, fg;
141  fit = fit + delta;
142 
143  double aband_shift = fit(1);
144 
145  if(0) {
146  std::cerr << "#sig2 = " << std::endl
147  << sig2 << std::endl
148  << "# x = " << std::endl
149  << x << std::endl
150  << "# y = " << std::endl
151  << y << std::endl
152  << "# fit = " <<std::endl
153  << fit << std::endl
154  << "# offset_scaling = "<<std::endl
155  << offset_scaling.value << std::endl
156  << "# aband_shift = " << std::endl
157  << aband_shift << std::endl;
158  // Output to plot stuff like so:
159  // pyplot.plot(x, y) # measured data
160  // yfit = fit[0]* numpy.exp(-(x+fit[1])**2/ (2.0*sig2))
161  // pyplot.plot(x,yfit) # fit modeled data
162  }
163 
164  // Create dispersion by scaling non aband values according to this method:
165  // Where below wn_spc is the spacing, ie second coefficient
166  // wco2 offset : delta_2 = 0.48 * ( delta_1 + wn_spc) - wn_spc)
167  // sco2_offset : delta_3 = 0.38 * ( delta_1 + wn_spc) - wn_spc)
168  Array<double, 2> disp_adj(disp_initial.copy());
169 
170  // Store computed shift for each band
171  shift_.resize(disp_adj.rows());
172 
173  for(int band_idx = 0; band_idx < disp_adj.rows(); band_idx++) {
174  shift_(band_idx) = offset_scaling.value(band_idx) * aband_shift;
175  disp_adj(band_idx, 0) = disp_adj(band_idx, 0) + shift_(band_idx);
176  }
177 
178  return disp_adj;
179 }
180 
181 void DispersionFit::print(std::ostream& Os) const
182 {
183  Os << "DispersionFit\n";
184 }
blitz::Array< double, 2 > fit(const blitz::Array< double, 2 > disp_initial, const DoubleWithUnit &aband_solar_line_location, const DoubleWithUnit &aband_solar_line_width, const DoubleWithUnit &aband_search_width, const DoubleWithUnit &aband_ils_offset, const ArrayWithUnit< double, 1 > &offset_scaling) const
Given a single frame of data, estimate the spectral shift in band 1P.
This is the base of the exception hierarchy for Full Physics code.
Definition: fp_exception.h:16
This is used to read a Level 1B file.
Definition: level_1b.h:15
blitz::Array< T, D > value
#define REGISTER_LUA_CLASS(X)
Definition: register_lua.h:116
Apply value function to a blitz array.
const Unit K("K", 1.0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0)
blitz::Array< double, 2 > generalized_inverse(const blitz::Array< double, 2 > &A, double Rcond=std::numeric_limits< double >::epsilon())
This returns the generalized inverse of the given matrix.
A one-dimensional polynomial class.
FullPhysics::AutoDerivative< double > pow2(const FullPhysics::AutoDerivative< double > &x)
We frequently have a double with units associated with it.
blitz::Array< double, 1 > shift() const
Return computed shift in each band.
Libraries such as boost::units allow unit handling where we know the units at compile time...
Definition: unit.h:25
Contains classes to abstract away details in various Spurr Radiative Transfer software.
Definition: doxygen_python.h:1
#define REGISTER_LUA_END()
Definition: register_lua.h:134
void print(std::ostream &Os) const
DispersionFit(const boost::shared_ptr< Level1bSampleCoefficient > &Level1b)
Initialize class with data needed to perform fit.
const DoubleWithUnit speed_of_light(299792458, units::m/units::s)
Speed of light, in m/s.

Copyright © 2017, California Institute of Technology.
ALL RIGHTS RESERVED.
U.S. Government Sponsorship acknowledged.
Generated Fri Aug 24 2018 15:44:08