Citlali
Loading...
Searching...
No Matches
calibrate.h
Go to the documentation of this file.
1#pragma once
2
3#include <map>
4#include <string>
5#include <Eigen/Core>
6
9
10namespace timestream {
11
13public:
14 // get logger
15 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
16
17 // extinction model
18 std::string extinction_model;
19
20 // coefficients for transmission ratios fit to order 6 polynomial
21 std::map<std::string,Eigen::VectorXd> tx_ratio_coeff;
22
23 std::map<std::string, double> tx_225_zenith = {
24 {"am_q0", 1.0},
25 {"am_q25", 0.9500275},
26 {"am_q50", 0.9142065},
27 {"am_q75", 0.8515054},
28 {"am_q95", 0.7337698},
29 };
30
31 void setup(double tau_225_zenith) {
32 // cos of zenith angle
33 auto cz = cos(pi/2 - 80.0*DEG_TO_RAD);
34 // 1/cos(zenith angle)
35 auto secz = 1. / cz;
36 // airmass
37 auto A = secz * (1. - 0.0012 * (pow(secz, 2) - 1.));
38
39 // tau at 225 GHz at 80deg from atm model and airmass
40 Eigen::VectorXd tau_225_calc(tx_225_zenith.size());
41
42 // calc tau at 225 GHz at 80deg from each model
43 int i = 0;
44 for (const auto &[key,val]: tx_225_zenith) {
45 tau_225_calc(i) = -log(val)/A;
46 i++;
47 }
48
49 // set initial model to am_q0
50 extinction_model = "am_q0";
51
52 // find model with closest tau to telescope tau and use that model
53 // for extinction correction
54 i = 0;
55 for (const auto &[key,val]: tx_225_zenith) {
56 if (tau_225_calc(i) <= tau_225_zenith) {
57 extinction_model = key;
58 }
59 i++;
60 }
61
62 // allocate transmission coefficients (order 6 polynomial)
63 tx_ratio_coeff["a1100"].resize(7);
64 tx_ratio_coeff["a1400"].resize(7);
65 tx_ratio_coeff["a2000"].resize(7);
66
67 // am_q0
68 if (extinction_model=="am_q0") {
69 tx_ratio_coeff["a1100"] << 0, 0, 0, 0, 0, 0, 0;
70 tx_ratio_coeff["a1400"] << 0, 0, 0, 0, 0, 0, 0;
71 tx_ratio_coeff["a2000"] << 0, 0, 0, 0, 0, 0, 0;
72 }
73
74 // am_q25
75 else if (extinction_model=="am_q25") {
76 tx_ratio_coeff["a1100"] << -0.12008024, 0.72422015, -1.81734478, 2.45313012, -1.92159695, 0.86918801, 0.78604295;
77 tx_ratio_coeff["a1400"] << 0.02619509, -0.15757661, 0.39400473, -0.52912696, 0.411213, -0.18360141, 1.04398466;
78 tx_ratio_coeff["a2000"] << 0.16726241, -1.00436302, 2.50507317, -3.35219659, 2.59080373, -1.14622096, 1.26931683;
79 }
80
81 // am_q50
82 else if (extinction_model=="am_q50") {
83 tx_ratio_coeff["a1100"] << -0.18770822, 1.13390437, -2.85173457, 3.8617083, -3.03996805, 1.38624303,
84 0.65300169;
85 tx_ratio_coeff["a1400"] << 0.04292884, -0.25817762, 0.64533115, -0.86622214, 0.67267823, -0.29996916,
86 1.07167603;
87 tx_ratio_coeff["a2000"] << 0.35178447, -2.10859714, 5.24620825, -6.9952531, 5.37645792, -2.35675076,
88 1.54286813;
89 }
90
91 // am_q75
92 else if (extinction_model=="am_q75") {
93 tx_ratio_coeff["a1100"] << -0.28189529, 1.70842347, -4.31606883, 5.88248549, -4.67702093, 2.16747228,
94 0.4393435;
95 tx_ratio_coeff["a1400"] << 0.07581154, -0.45574885, 1.13852458, -1.52697451, 1.18425865, -0.5269455,
96 1.12531753;
97 tx_ratio_coeff["a2000"] << 0.79869908, -4.77265095, 11.82393401, -15.67007557, 11.93052031,
98 -5.14788907, 2.14595898;
99 }
100
101 // am_q95
102 else if (extinction_model=="am_q95") {
103 tx_ratio_coeff["a1100"] << -1.21882233, 6.67068453, -14.96466875, 17.78045563, -12.10288687,
104 4.76050807, -0.06765066;
105 tx_ratio_coeff["a1400"] << 0.76090502, -4.05867663, 8.78487281, -9.90872343, 6.2198602, -2.13790165,
106 1.3668983;
107 tx_ratio_coeff["a2000"] << 16.0063036, -84.30325144, 179.28096414, -197.05751682, 118.73627425,
108 -37.99279818, 6.55457576;
109 }
110 }
111
112 // polynomial fit to transmission ratio as a function of elevation (radians)
113 template <typename DerivedA, typename DerivedB>
114 auto tau_polynomial(Eigen::DenseBase<DerivedA> &coeff, Eigen::DenseBase<DerivedB> &elev) {
115
116 return coeff(0)*pow(elev.derived().array(),6) + coeff(1)*pow(elev.derived().array(),5) +
117 coeff(2)*pow(elev.derived().array(),4) + coeff(3)*pow(elev.derived().array(),3) +
118 coeff(4)*pow(elev.derived().array(),2) + coeff(5)*pow(elev.derived().array(),1) +
119 coeff(6);
120 }
121
122 // calculate tau for each toltec band for given elevations
123 template <typename Derived>
124 auto calc_tau(Eigen::DenseBase<Derived> &, double);
125
126 // run flux calibration on the timestreams
127 template <TCDataKind tcdata_kind, class calib_t>
129
130 // run extinction correction on the timestreams
131 template <TCDataKind tcdata_kind, class calib_t, typename tau_t>
133};
134
135template <typename Derived>
136auto Calibration::calc_tau(Eigen::DenseBase<Derived> &elev, double tau_225_GHz) {
137
138 // tau at toltec freqs
139 std::map<int,Eigen::VectorXd> tau_freq;
140
141 if (extinction_model=="am_q0") {
142 tau_freq[0] = Eigen::VectorXd::Zero(elev.size());
143 tau_freq[1] = Eigen::VectorXd::Zero(elev.size());
144 tau_freq[2] = Eigen::VectorXd::Zero(elev.size());
145
146 return tau_freq;
147 }
148
149 // zenith angle
150 auto cz = cos(pi/2 - elev.derived().array());
151 auto secz = 1. / cz;
152 auto A = secz * (1. - 0.0012 * (pow(secz.array(), 2) - 1.));
153
154 // observed 225 GHz tau
155 auto obs_tau_i = A*tau_225_GHz;
156
157 // 225 GHz transmission
158 auto tx = (-obs_tau_i).exp();
159
160 // a1100
161 auto tx_a1100 = tau_polynomial(tx_ratio_coeff["a1100"],elev).array()*tx.array();
162 tau_freq[0] = -(tx_a1100.array().log())/A.array();
163
164 // a1400
165 auto tx_a1400 = tau_polynomial(tx_ratio_coeff["a1400"],elev).array()*tx.array();
166 tau_freq[1] = -(tx_a1400.array().log())/A.array();
167
168 // a2000
169 auto tx_a2000 = tau_polynomial(tx_ratio_coeff["a2000"],elev).array()*tx.array();
170 tau_freq[2] = -(tx_a2000.array().log())/A.array();
171
172 return tau_freq;
173}
174
175template <TCDataKind tcdata_kind, class calib_t>
177
178 // loop through detectors
179 for (Eigen::Index i=0; i<in.scans.data.cols(); ++i) {
180 // current array index in apt table
181 Eigen::Index array_index = calib.apt["array"](i);
182
183 // flux conversion factor for non-mJy/beam units
184 in.fcf.data(i) = calib.flux_conversion_factor(array_index);
185
186 // data x flxscale x factor
187 in.scans.data.col(i) = in.scans.data.col(i).array()*in.fcf.data(i)*calib.apt["flxscale"](i);
188 }
189}
190
191template <TCDataKind tcdata_kind, class calib_t, typename tau_t>
193
194 // loop through detectors
195 for (Eigen::Index i=0; i<in.scans.data.cols(); ++i) {
196 // current array index in apt table
197 Eigen::Index array_index = calib.apt["array"](i);
198
199 // factor = 1 / exp(-tau_freq)
200 auto factor = 1./(-tau_freq[array_index]).array().exp();
201
202 // apply extinction correction to fcf
203 in.fcf.data(i) = (in.fcf.data(i)*factor).mean();
204
205 // data x factor
206 in.scans.data.col(i) = in.scans.data.col(i).array()*factor.array();
207 }
208}
209} // namespace timestream
Definition calibrate.h:12
std::map< std::string, double > tx_225_zenith
Definition calibrate.h:23
void extinction_correction(TCData< tcdata_kind, Eigen::MatrixXd > &, calib_t &, tau_t &)
Definition calibrate.h:192
auto calc_tau(Eigen::DenseBase< Derived > &, double)
Definition calibrate.h:136
void setup(double tau_225_zenith)
Definition calibrate.h:31
std::shared_ptr< spdlog::logger > logger
Definition calibrate.h:15
std::map< std::string, Eigen::VectorXd > tx_ratio_coeff
Definition calibrate.h:21
auto tau_polynomial(Eigen::DenseBase< DerivedA > &coeff, Eigen::DenseBase< DerivedB > &elev)
Definition calibrate.h:114
void calibrate_tod(TCData< tcdata_kind, Eigen::MatrixXd > &, calib_t &)
Definition calibrate.h:176
std::string extinction_model
Definition calibrate.h:18
#define DEG_TO_RAD
Definition constants.h:27
constexpr auto pi
Definition constants.h:6
Definition clean.h:10
TC data class.
Definition timestream.h:55