Citlali
Loading...
Searching...
No Matches
todproc.h
Go to the documentation of this file.
1#pragma once
2
3#include <unsupported/Eigen/CXX11/Tensor>
4
5#include <tula/eigen.h>
6
8#include <tula/algorithm/mlinterp/mlinterp.hpp>
9
13
14namespace fs = std::filesystem;
15
17 template <typename OStream>
18 friend OStream &operator<<(OStream &os, const DummyEngine &e) {
19 return os << fmt::format("DummyEngine()");
20 }
21};
22
28template <class EngineType>
29struct TimeOrderedDataProc : ConfigMapper<TimeOrderedDataProc<EngineType>> {
31 using config_t = typename Base::config_t;
32 using Engine = EngineType;
33 using scanindicies_t = Eigen::MatrixXI;
34 using map_extent_t = std::vector<int>;
35 using map_coord_t = std::vector<Eigen::VectorXd>;
36 using map_count_t = std::size_t;
37 using array_indices_t = std::vector<std::tuple<Eigen::Index, Eigen::Index>>;
38 using det_indices_t = std::vector<std::tuple<Eigen::Index, Eigen::Index>>;
39
40 // get logger
41 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
42
43 TimeOrderedDataProc(config_t config) : Base{std::move(config)} {}
44
45 // check if config file has nodes
46 static auto check_config(const config_t &config)
47 -> std::optional<std::string> {
48 // get logger
49 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
50
51 std::vector<std::string> missing_keys;
52 logger->info("check TOD proc config\n{}", config);
53 // check for runtime config node
54 if (!config.has("runtime")) {
55 missing_keys.push_back("runtime");
56 }
57 // check for timestream config node
58 if (!config.has("timestream")) {
59 missing_keys.push_back("timestream");
60 }
61 // check for mapmaking config node
62 if (!config.has("mapmaking")) {
63 missing_keys.push_back("mapmaking");
64 }
65 // check for beammap config node
66 if (!config.has("beammap")) {
67 missing_keys.push_back("beammap");
68 }
69 // check for coadd config node
70 if (!config.has("coadd")) {
71 missing_keys.push_back("coadd");
72 }
73 // check for noise map config node
74 if (!config.has("noise_maps")) {
75 missing_keys.push_back("noise_maps");
76 }
77 // check for post processing config node
78 if (!config.has("post_processing")) {
79 missing_keys.push_back("post_processing");
80 }
81 if (missing_keys.empty()) {
82 return std::nullopt;
83 }
84 return fmt::format("invalid or missing keys={}", missing_keys);
85 }
86
87 // create output FITS files (does not populate)
89 // get apt from raw data files (beammapping)
90 void get_apt_from_files(const RawObs &rawobs);
91 // get tone frequencies from raw files
92 void get_tone_freqs_from_files(const RawObs &rawobs);
93 // get adc snap data from raw files
94 void get_adc_snap_from_files(const RawObs &rawobs);
95 // create output directories
96 void create_output_dir();
97 // count up detectors from input files and check for mismatch with apt
98 void check_inputs(const RawObs &rawobs);
99 // align networks and hwpr vectors in time
100 void align_timestreams(const RawObs &rawobs);
101 // updated alignment of networks and hwpr vectors in time that accounts for gaps
102 void align_timestreams_gaps(const RawObs &rawobs);
103 // interpolate pointing vectors
104 void interp_pointing();
105 // calculate number of maps
106 void calc_map_num();
107 // calculate size of omb maps
108 void calc_omb_size(std::vector<map_extent_t> &, std::vector<map_coord_t> &);
109 // allocate observation maps
111 // calculate size of cmb maps
112 void calc_cmb_size(std::vector<map_coord_t> &);
113 // allocate coadded maps
114 void allocate_cmb();
115 // allocate noise maps
116 template<class map_buffer_t>
117 void allocate_nmb(map_buffer_t &);
118 // coadd omb into cmb
119 void coadd();
120 // make index files
121 void make_index_file(std::string);
122
123 // TODO fix the const correctness
124 Engine &engine() { return m_engine; }
125
126 const Engine &engine() const { return m_engine; }
127
128 template <typename OStream>
129 friend OStream &operator<<(OStream &os,
130 const TimeOrderedDataProc &todproc) {
131 return os << fmt::format("TimeOrderedDataProc(engine={})",
132 todproc.engine());
133 }
134
135private:
137};
138
139// make apt table from raw files instead of an ecsv table
140template <class EngineType>
142 using namespace netCDF;
143 using namespace netCDF::exceptions;
144
145 // nw names
146 std::vector<Eigen::Index> interfaces;
147
148 // total number of detectors
149 Eigen::Index n_dets = 0;
150 // detector, nw and array names for each network
151 std::vector<Eigen::Index> dets, nws, arrays;
152 // loop through input files
153 for (const RawObs::DataItem &data_item : rawobs.kidsdata()) {
154 try {
155 // load data file
156 NcFile fo(data_item.filepath(), NcFile::read);
157 auto vars = fo.getVars();
158
159 // get the interface
160 interfaces.push_back(std::stoi(data_item.interface().substr(6)));
161
162 // add the current file's number of dets to the total
163 n_dets += vars.find("Data.Toltec.Is")->second.getDim(1).getSize();
164
165 // get the number of dets in file
166 dets.push_back(vars.find("Data.Toltec.Is")->second.getDim(1).getSize());
167 // get the nw from interface
168 nws.push_back(interfaces.back());
169 // get the array from the interface
170 arrays.push_back(engine().toltec_io.nw_to_array_map[interfaces.back()]);
171
172 fo.close();
173
174 } catch (NcException &e) {
175 logger->error("{}", e.what());
176 throw DataIOError{fmt::format(
177 "failed to load data from netCDF file {}", data_item.filepath())};
178 }
179 }
180
181 // explicitly clear the apt
182 engine().calib.apt.clear();
183
184 // resize the apt vectors
185 for (auto const& key : engine().calib.apt_header_keys) {
186 if (key=="x_t" || key=="y_t") {
187 engine().calib.apt[key].setZero(n_dets);
188 }
189 else {
190 engine().calib.apt[key].setOnes(n_dets);
191 }
192 }
193
194 // set all flags to good
195 engine().calib.apt["flag"].setZero(n_dets);
196
197 // add the nws and arrays to the apt table
198 Eigen::Index j = 0;
199 for (Eigen::Index i=0; i<nws.size(); ++i) {
200 engine().calib.apt["nw"].segment(j,dets[i]).setConstant(nws[i]);
201 engine().calib.apt["array"].segment(j,dets[i]).setConstant(arrays[i]);
202
203 j = j + dets[i];
204 }
205
206 // set uids
207 engine().calib.apt["uid"] = Eigen::VectorXd::LinSpaced(n_dets,0,n_dets-1);
208
209 // setup nws, arrays, etc.
210 engine().calib.setup();
211
212 // filepath
213 engine().calib.apt_filepath = "internally generated for beammap";
214}
215
216template <class EngineType>
218 using namespace netCDF;
219 using namespace netCDF::exceptions;
220
221 // tone frquencies for each network
222 std::map<Eigen::Index,Eigen::MatrixXd> tone_freqs;
223
224 // nw names
225 std::vector<Eigen::Index> interfaces;
226
227 // total number of detectors
228 Eigen::Index n_dets = 0;
229 // detector, nw and array names for each network
230 std::vector<Eigen::Index> dets, nws, arrays;
231 // loop through input files
232 for (const RawObs::DataItem &data_item : rawobs.kidsdata()) {
233 try {
234 // load data file
235 NcFile fo(data_item.filepath(), NcFile::read);
236 auto vars = fo.getVars();
237
238 // get the interface
239 interfaces.push_back(std::stoi(data_item.interface().substr(6)));
240
241 // add the current file's number of dets to the total
242 n_dets += vars.find("Data.Toltec.Is")->second.getDim(1).getSize();
243
244 // dimension of tone freqs is (n_sweeps, n_tones)
245 Eigen::Index n_sweeps = vars.find("Header.Toltec.ToneFreq")->second.getDim(0).getSize();
246
247 // get local oscillator frequency
248 double lo_freq;
249 vars.find("Header.Toltec.LoCenterFreq")->second.getVar(&lo_freq);
250
251 // get tone_freqs for interface
252 tone_freqs[interfaces.back()].resize(vars.find("Header.Toltec.ToneFreq")->second.getDim(1).getSize(),n_sweeps);
253 vars.find("Header.Toltec.ToneFreq")->second.getVar(tone_freqs[interfaces.back()].data());
254
255 // add local oscillator freq
256 tone_freqs[interfaces.back()] = tone_freqs[interfaces.back()].array() + lo_freq;
257
258 fo.close();
259
260 } catch (NcException &e) {
261 logger->error("{}", e.what());
262 throw DataIOError{fmt::format(
263 "failed to load data from netCDF file {}", data_item.filepath())};
264 }
265 }
266
267 engine().calib.apt["tone_freq"].resize(engine().calib.n_dets);
268
269 // add the nws and arrays to the apt table
270 Eigen::Index j = 0;
271 for (Eigen::Index i=0; i<engine().calib.nws.size(); ++i) {
272 engine().calib.apt["tone_freq"].segment(j,tone_freqs[interfaces[i]].size()) = tone_freqs[interfaces[i]];
273
274 j = j + tone_freqs[interfaces[i]].size();
275 }
276
277 if (!engine().telescope.sim_obs) {
278 /* find duplicates */
279
280 // frequency separation
281 Eigen::VectorXd dfreq(engine().calib.n_dets);
282 dfreq(0) = engine().calib.apt["tone_freq"](1) - engine().calib.apt["tone_freq"](0);
283
284 // loop through tone freqs and find distance
285 for (Eigen::Index i=1; i<engine().calib.apt["tone_freq"].size()-1; ++i) {
286 dfreq(i) = std::min(abs(engine().calib.apt["tone_freq"](i) - engine().calib.apt["tone_freq"](i-1)),
287 abs(engine().calib.apt["tone_freq"](i+1) - engine().calib.apt["tone_freq"](i)));
288 }
289 // get last distance
290 dfreq(dfreq.size()-1) = abs(engine().calib.apt["tone_freq"](dfreq.size()-1)-engine().calib.apt["tone_freq"](dfreq.size()-2));
291
292 // number of nearby tones found
293 int n_nearby_tones = 0;
294
295 // store duplicates
296 engine().calib.apt["duplicate_tone"].setZero(engine().calib.n_dets);
297
298 // loop through flag columns
299 for (Eigen::Index i=0; i<engine().calib.n_dets; ++i) {
300 // if closer than freq separation limit and unflagged, flag it
301 if (dfreq(i) < engine().rtcproc.delta_f_min_Hz) {
302 engine().calib.apt["duplicate_tone"](i) = 1;
303 n_nearby_tones++;
304 }
305 }
306 logger->info("{} nearby tones found. these will be flagged.",n_nearby_tones);
307 }
308}
309
310template <class EngineType>
312 using namespace netCDF;
313 using namespace netCDF::exceptions;
314
315 // explicitly clear adc vector
316 engine().diagnostics.adc_snap_data.clear();
317
318 // loop through input files
319 for (const RawObs::DataItem &data_item : rawobs.kidsdata()) {
320 try {
321 // load data file
322 NcFile fo(data_item.filepath(), NcFile::read);
323 auto vars = fo.getVars();
324
325 // dimension 0 of adc data
326 Eigen::Index adcSnapDim = vars.find("Header.Toltec.AdcSnapData")->second.getDim(0).getSize();
327 // dimension 1 of adc data
328 Eigen::Index adcSnapDataDim = vars.find("Header.Toltec.AdcSnapData")->second.getDim(1).getSize();
329
330 // matrix to hold adc data for current file
331 Eigen::Matrix<short,Eigen::Dynamic, Eigen::Dynamic> adcsnap(adcSnapDataDim,adcSnapDim);
332 // load adc data
333 vars.find("Header.Toltec.AdcSnapData")->second.getVar(adcsnap.data());
334 // append to vector of adc data
335 engine().diagnostics.adc_snap_data.push_back(adcsnap);
336
337 fo.close();
338
339 } catch (NcException &e) {
340 logger->warn("{} adc data not found",data_item.filepath());
341 }
342 }
343}
344
345// create output directories
346template <class EngineType>
348 // redu subdir
349 engine().redu_dir_name = "";
350
351 // create reduction subdir
352 if (engine().use_subdir) {
353 // redu number
354 engine().redu_dir_num = 0;
355
356 std::stringstream ss_redu_dir_num;
357 // add leading zero to redu_dir_num (i.e., '00', '01',...)
358 ss_redu_dir_num << std::setfill('0') << std::setw(2) << engine().redu_dir_num;
359
360 // create redu dir name ('redu00', 'redu01',...)
361 std::string redu_dir_name = "redu" + ss_redu_dir_num.str();
362
363 // iteratively check if current subdir with current redu number exists
364 while (fs::exists(fs::status(engine().output_dir + "/" + redu_dir_name))) {
365 // increment redu number if subdir exists
366 engine().redu_dir_num++;
367 std::stringstream ss_redu_dir_num_i;
368 ss_redu_dir_num_i << std::setfill('0') << std::setw(2) << engine().redu_dir_num;
369 redu_dir_name = "redu" + ss_redu_dir_num_i.str();
370 }
371
372 // final redu dir name is output directory from config + /reduNN
373 engine().redu_dir_name = engine().output_dir + "/" + redu_dir_name;
374
375 // create redu dir directory
376 fs::create_directories(engine().redu_dir_name);
377 }
378 else {
379 engine().redu_dir_name = engine().output_dir + "/";
380 }
381
382 // coadded subdir
383 if (engine().run_coadd) {
384 engine().coadd_dir_name = engine().redu_dir_name + "/coadded/";
385 // coadded raw subdir
386 if (!fs::exists(fs::status(engine().coadd_dir_name + "raw/"))) {
387 fs::create_directories(engine().coadd_dir_name + "raw/");
388 }
389 else {
390 logger->warn("directory {} already exists", engine().coadd_dir_name + "raw/");
391 }
392 // if map filtering is requested
393 if (engine().run_map_filter) {
394 // coadded filtered subdir
395 if (!fs::exists(fs::status(engine().coadd_dir_name + "filtered/"))) {
396 fs::create_directories(engine().coadd_dir_name + "filtered/");
397 }
398 else {
399 logger->warn("directory {} already exists", engine().coadd_dir_name + "filtered/");
400 }
401 }
402 }
403}
404
405template <class EngineType>
407 using namespace netCDF;
408 using namespace netCDF::exceptions;
409
410 Eigen::Index n_dets = 0;
411
412 // loop through input files
413 for (const RawObs::DataItem &data_item : rawobs.kidsdata()) {
414 try {
415 // load data file
416 NcFile fo(data_item.filepath(), NcFile::read);
417 auto vars = fo.getVars();
418 // get number of dets from data and add to global value
419 n_dets += vars.find("Data.Toltec.Is")->second.getDim(1).getSize();
420
421 fo.close();
422
423 } catch (NcException &e) {
424 logger->error("{}", e.what());
425 throw DataIOError{fmt::format(
426 "failed to load data from netCDF file {}", data_item.filepath())};
427 }
428 }
429
430 // check if number of detectors in apt file is equal to those in files
431 if (n_dets != engine().calib.n_dets) {
432 logger->error("number of detectors in data files and apt file do not match");
433 std::exit(EXIT_FAILURE);
434 }
435}
436
437// align tod with telescope
438template <class EngineType>
440 using namespace netCDF;
441 using namespace netCDF::exceptions;
442
443 // clear start and end indices for each observation
444 engine().start_indices.clear();
445 engine().end_indices.clear();
446
447 // clear gaps
448 engine().gaps.clear();
449
450 // vector of network times
451 std::vector<Eigen::VectorXd> nw_ts;
452 // start and end times
453 std::vector<double> nw_t0, nw_tn;
454
455 // maximum start time
456 double max_t0 = -99;
457
458 // minimum end time
459 double min_tn = std::numeric_limits<double>::max();
460 // indices of max start time and min end time
461 Eigen::Index max_t0_i, min_tn_i;
462
463 // set network
464 Eigen::Index nw = 0;
465 // sample rate
466 double fsmp = -1;
467
468 // loop through input files
469 for (const RawObs::DataItem &data_item : rawobs.kidsdata()) {
470 try {
471 // load data file
472 NcFile fo(data_item.filepath(), NcFile::read);
473 auto vars = fo.getVars();
474
475 // get roach index for offsets
476 int roach_index;
477 vars.find("Header.Toltec.RoachIndex")->second.getVar(&roach_index);
478
479 // get sample rate
480 double fsmp_roach;
481 vars.find("Header.Toltec.SampleFreq")->second.getVar(&fsmp_roach);
482
483 // check if sample rate is the same and exit if not
484 if (fsmp!=-1 && fsmp_roach!=fsmp) {
485 logger->error("mismatched sample rate in toltec{}",roach_index);
486 std::exit(EXIT_FAILURE);
487 }
488 else {
489 fsmp = fsmp_roach;
490 }
491
492 // get dimensions for time matrix
493 Eigen::Index n_pts = vars.find("Data.Toltec.Ts")->second.getDim(0).getSize();
494 Eigen::Index n_time = vars.find("Data.Toltec.Ts")->second.getDim(1).getSize();
495
496 // get time matrix
497 Eigen::MatrixXi ts(n_time,n_pts);
498 vars.find("Data.Toltec.Ts")->second.getVar(ts.data());
499
500 // transpose due to row-major order
501 ts.transposeInPlace();
502
503 // find gaps
504 int gaps = ((ts.block(1,3,n_pts,1).array() - ts.block(0,3,n_pts-1,1).array()).array() > 1).count();
505
506 // add gaps to engine map
507 if (gaps>0) {
508 engine().gaps["Toltec" + std::to_string(roach_index)] = gaps;
509 }
510
511 // get fpga frequency
512 double fpga_freq;
513 vars.find("Header.Toltec.FpgaFreq")->second.getVar(&fpga_freq);
514
515 // ClockTime (sec)
516 auto sec0 = ts.cast <double> ().col(0);
517 // ClockTimeNanoSec (nsec)
518 auto nsec0 = ts.cast <double> ().col(5);
519 // PpsCount (pps ticks)
520 auto pps = ts.cast <double> ().col(1);
521 // ClockCount (clock ticks)
522 auto msec = ts.cast <double> ().col(2)/fpga_freq;
523 // PacketCount (packet ticks)
524 auto count = ts.cast <double> ().col(3);
525 // PpsTime (clock ticks)
526 auto pps_msec = ts.cast <double> ().col(4)/fpga_freq;
527 // get start time
528 auto t0 = sec0 + nsec0*1e-9;
529
530 // shift start time (offset determined empirically)
531 int start_t = int(t0[0] - 0.5);
532 //int start_t = int(t0[0]);
533
534 // convert start time to double
535 double start_t_dbl = start_t;
536 // clock count - clock ticks
537 Eigen::VectorXd dt = msec - pps_msec;
538 // remove overflow due to int32
539 dt = (dt.array() < 0).select(msec.array() - pps_msec.array() + (pow(2.0,32)-1)/fpga_freq,msec - pps_msec);
540 // get network time and add offsets
541 nw_ts.push_back(start_t_dbl + pps.array() + dt.array() +
542 engine().interface_sync_offset["toltec"+std::to_string(roach_index)]);
543
544 // push back start time
545 nw_t0.push_back(nw_ts.back()[0]);
546
547 // push back end time
548 nw_tn.push_back(nw_ts.back()[n_pts - 1]);
549
550 // get global max start time and index
551 if (nw_t0.back() > max_t0) {
552 max_t0 = nw_t0.back();
553 max_t0_i = nw;
554 }
555
556 // get global min end time and index
557 if (nw_tn.back() < min_tn) {
558 min_tn = nw_tn.back();
559 min_tn_i = nw;
560 }
561
562 // increment nw
563 nw++;
564
565 fo.close();
566
567 } catch (NcException &e) {
568 logger->error("{}", e.what());
569 throw DataIOError{fmt::format(
570 "failed to load data from netCDF file {}", data_item.filepath())};
571 }
572 }
573
574 // get hwpr timing
575 if (engine().calib.run_hwpr) {
576 // if hwpr init time is larger than max start time, replace global max start time
577 Eigen::Index hwpr_ts_n_pts = engine().calib.hwpr_recvt.size();
578 if (engine().calib.hwpr_recvt(0) > max_t0) {
579 max_t0 = engine().calib.hwpr_recvt(0);
580 }
581
582 // if hwpr init time is smaller than min end time, replace global min end time
583 if (engine().calib.hwpr_recvt(hwpr_ts_n_pts - 1) < min_tn) {
584 min_tn = engine().calib.hwpr_recvt(hwpr_ts_n_pts - 1);
585 }
586 }
587
588 // size of smallest data time vector
589 Eigen::Index min_size = nw_ts[0].size();
590
591 // loop through time vectors and get the smallest
592 for (Eigen::Index i=0; i<nw_ts.size(); ++i) {
593 // find start index that is larger than max start
594 Eigen::Index si, ei;
595 // find index closest to max start time
596 auto s = (abs(nw_ts[i].array() - max_t0)).minCoeff(&si);
597
598 // if closest index is smaller than max start time
599 // incrememnt index until it is larger or equal
600 while (nw_ts[i][si] < max_t0) {
601 si++;
602 }
603 // pushback start index on start index vector
604 engine().start_indices.push_back(si);
605
606 // find end index that is smaller than min end
607 auto e = (abs(nw_ts[i].array() - min_tn)).minCoeff(&ei);
608 // if closest index is larger than min end time
609 // incrememnt index until it is smaller or equal
610 while (nw_ts[i][ei] > min_tn) {
611 ei--;
612 }
613 // pushback end index on end index vector
614 engine().end_indices.push_back(ei);
615 }
616
617 // get min size
618 for (Eigen::Index i=0; i<nw_ts.size(); ++i) {
619 // start indices
620 auto si = engine().start_indices[i];
621 // end indices
622 auto ei = engine().end_indices[i];
623
624 // if smallest length, update min_size
625 if ((ei - si + 1) < min_size) {
626 min_size = ei - si + 1;
627 }
628 }
629
630 // if hwpr requested
631 if (engine().calib.run_hwpr) {
632 Eigen::Index si, ei;
633 // find start index that is larger than max start for hwpr
634 auto s = (abs(engine().calib.hwpr_recvt.array() - max_t0)).minCoeff(&si);
635
636 // if closest index is smaller than max start time
637 // incrememnt index until it is larger or equal
638 while (engine().calib.hwpr_recvt(si) < max_t0) {
639 si++;
640 }
641 // pushback start index on hwpr start index vector
642 engine().hwpr_start_indices = si;
643
644 // find end index that is smaller than min end for hwpr
645 auto e = (abs(engine().calib.hwpr_recvt.array() - min_tn)).minCoeff(&ei);
646 // if closest index is larger than min end time
647 // incrememnt index until it is smaller or equal
648 while (engine().calib.hwpr_recvt(ei) > min_tn) {
649 ei--;
650 }
651 // pushback end index on hwpr end index vector
652 engine().hwpr_end_indices = ei;
653
654 // update min_size for all time vectors if hwpr data is shorter (data and hwpr)
655 if ((ei - si + 1) < min_size) {
656 min_size = ei - si + 1;
657 }
658 }
659
660 // size of telescope data
661 Eigen::Matrix<Eigen::Index,1,1> nd;
662 nd << engine().telescope.tel_data["TelTime"].size();
663
664 // shortest data time vector
665 Eigen::VectorXd xi = nw_ts[max_t0_i].head(min_size);
666
667 // interpolate telescope data
668 for (const auto &tel_it : engine().telescope.tel_data) {
669 if (tel_it.first !="TelTime") {
670 // telescope vector to interpolate
671 Eigen::VectorXd yd = engine().telescope.tel_data[tel_it.first];
672 // vector to store interpolated outputs in
673 Eigen::VectorXd yi(min_size);
674
675 mlinterp::interp(nd.data(), min_size, // nd, ni
676 yd.data(), yi.data(), // yd, yi
677 engine().telescope.tel_data["TelTime"].data(), xi.data()); // xd, xi
678
679 // move back into tel_data vector
680 engine().telescope.tel_data[tel_it.first] = std::move(yi);
681 }
682 }
683
684 // replace telescope time vectors
685 engine().telescope.tel_data["TelTime"] = xi;
686 engine().telescope.tel_data["TelUTC"] = xi;
687
688 // interpolate hwpr data
689 if (engine().calib.run_hwpr) {
690 Eigen::VectorXd yd = engine().calib.hwpr_angle;
691 // vector to store interpolated outputs in
692 Eigen::VectorXd yi(min_size);
693 mlinterp::interp(nd.data(), min_size, // nd, ni
694 yd.data(), yi.data(), // yd, yi
695 engine().calib.hwpr_recvt.data(), xi.data()); // xd, xi
696
697 // move back into hwpr angle
698 engine().calib.hwpr_angle = std::move(yi);
699 }
700}
701
702// upgraded alignment of tod with telescope
703template <class EngineType>
705 using namespace netCDF;
706 using namespace netCDF::exceptions;
707
708 // clear start and end indices for each observation
709 engine().start_indices.clear();
710 engine().end_indices.clear();
711
712 std::vector<Eigen::VectorXd> nw_times(rawobs.kidsdata().size());
713
714 // clear gaps
715 engine().gaps.clear();
716
717 // loop through networks and build time vectors
718 int i = 0;
719 double f_smp_roach;
720 for (const RawObs::DataItem &data_item : rawobs.kidsdata()) {
721 try {
722 const RawObs::DataItem &data_item = rawobs.kidsdata()[i];
723 // load data file
724 NcFile fo(data_item.filepath(), NcFile::read);
725 auto vars = fo.getVars();
726
727 // get roach sample rate
728 vars.find("Header.Toltec.SampleFreq")->second.getVar(&f_smp_roach);
729
730 // get roach index for offsets
731 int roach_index;
732 vars.find("Header.Toltec.RoachIndex")->second.getVar(&roach_index);
733
734 // get dimensions for time matrix
735 Eigen::Index n_pts = vars.find("Data.Toltec.Ts")->second.getDim(0).getSize();
736 Eigen::Index n_times = vars.find("Data.Toltec.Ts")->second.getDim(1).getSize();
737
738 // get time matrix
739 //Eigen::MatrixXi ts(n_times, n_pts);
740 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> ts(n_pts, n_times);
741 vars.find("Data.Toltec.Ts")->second.getVar(ts.data());
742
743 // get fpga frequency
744 double fpga_freq;
745 vars.find("Header.Toltec.FpgaFreq")->second.getVar(&fpga_freq);
746
747 // cast to double
748 Eigen::MatrixXd ts_double = ts.cast<double>();
749
750 Eigen::MatrixXi ts_t = ts.transpose();
751
752 // find gaps
753 int gaps = ((ts.block(1,3,n_pts,1).array() - ts.block(0,3,n_pts-1,1).array()).array() > 1).count();
754
755 // add gaps to engine map
756 if (gaps>0) {
757 engine().gaps["Toltec" + std::to_string(roach_index)] = gaps;
758 }
759
760 auto sec = ts_double.col(0); // ClockTime (sec)
761 auto nsec = ts_double.col(5); // ClockTimeNanoSec (nsec)
762 auto pps = ts_double.col(1); // PpsCount (pps ticks)
763 auto msec = ts_double.col(2) / fpga_freq; // ClockCount (clock ticks) to seconds
764 //Eigen::VectorXd count = ts_double.col(3); // PacketCount (packet ticks)
765 auto pps_msec = ts_double.col(4) / fpga_freq; // PpsTime (clock ticks) to seconds
766
767 // determine start time with empirical offset
768 double start_time_dbl = sec[0] + nsec[0] * 1e-9;
769 int start_time = int(start_time_dbl - 0.5);
770 start_time_dbl = start_time;
771
772 // calculate clock count difference (dt)
773 Eigen::VectorXd dt = msec - pps_msec;
774
775 // handle overflow due to int32, using Eigen array logic
776 dt = (dt.array() < 0).select(msec.array() - pps_msec.array() + (pow(2.0, 32) - 1) / fpga_freq, msec - pps_msec);
777
778 // build the time vector for the current network
779 Eigen::VectorXd nw_time = start_time_dbl + pps.array() + dt.array()
780 + engine().interface_sync_offset["toltec"+std::to_string(roach_index)];
781
782 // store all time vectors
783 nw_times[i] = std::move(nw_time);
784 i++;
785
786 fo.close();
787
788 } catch (NcException &e) {
789 throw std::runtime_error(fmt::format("unable to open file : {}", "data_item.filepath()", e.what()));
790 }
791 }
792
793 // get hwpr times if not ignored
794 if (engine().calib.run_hwpr) {
795 logger->debug("calculating hwpr time");
796 // hwpr gets added alongside networks
797 nw_times.push_back(engine().calib.hwpr_recvt);
798 }
799
800 // latest starting time of all networks
801 double max_init_time = std::numeric_limits<double>::lowest();
802 // earliest final time of all networks
803 double min_final_time = std::numeric_limits<double>::max();
804
805 // indices for max_init_time and min_final_time
806 Eigen::Index max_init_idx = 0;
807 Eigen::Index min_final_idx = 0;
808
809 // get global max init and min final times and indices
810 for (Eigen::Index i = 0; i < nw_times.size(); ++i) {
811 double initial_time = nw_times[i](0);
812 double final_time = nw_times[i](nw_times[i].size() - 1);
813
814 // get latest starting network
815 if (initial_time > max_init_time) {
816 max_init_time = initial_time;
817 max_init_idx = i;
818 }
819 // get earliest ending network
820 if (final_time < min_final_time) {
821 min_final_time = final_time;
822 min_final_idx = i;
823 }
824 }
825
826 double dt = 1.0 / f_smp_roach;
827 Eigen::Index n_samples = static_cast<int>((min_final_time - max_init_time) / dt) + 1;
828 Eigen::VectorXd t_common = Eigen::VectorXd::LinSpaced(n_samples, max_init_time, max_init_time + dt * (n_samples - 1));
829 double tol = dt / 2.0;
830
831 std::vector<Eigen::VectorXi> masks;
832 masks.reserve(nw_times.size());
833
834 for (const auto &t : nw_times) {
835 Eigen::VectorXi mask = Eigen::VectorXi::Zero(n_samples);
836
837 for (int i = 0; i < t.size(); ++i) {
838 double time = t(i);
839 int idx = static_cast<int>(std::round((time - max_init_time) / dt));
840 if (idx >= 0 && idx < n_samples && std::abs(time - t_common(idx)) <= tol) {
841 mask(idx) = 1;
842 }
843 }
844
845 logger->warn("{}/{} samples were not aligned to the common time grid", mask.size() - mask.sum(), mask.size());
846
847 masks.push_back(std::move(mask));
848 }
849
850 // size of telescope data
851 Eigen::Matrix<Eigen::Index,1,1> nd;
852 nd << engine().telescope.tel_data["TelTime"].size();
853
854 // interpolate telescope data onto data timestream
855 for (const auto &tel_it : engine().telescope.tel_data) {
856 // don't interpolate telescope time itself
857 if (tel_it.first !="TelTime" && tel_it.first !="TelUTC") {
858 // telescope vector to interpolate
859 Eigen::VectorXd yd = engine().telescope.tel_data.at(tel_it.first);
860 // vector to store interpolated outputs in
861 Eigen::VectorXd yi(n_samples);
862
863 mlinterp::interp(nd.data(), n_samples, // nd, ni
864 yd.data(), yi.data(), // yd, yi
865 engine().telescope.tel_data.at("TelTime").data(), t_common.data()); // xd, xi
866
867 // move back into data vector
868 engine().telescope.tel_data[tel_it.first] = std::move(yi);
869 }
870 }
871
872 // replace telescope time vectors
873 engine().telescope.tel_data.at("TelTime") = t_common;
874 engine().telescope.tel_data.at("TelUTC") = t_common;
875
876 // interpolate hwpr
877 if (engine().calib.run_hwpr) {
878 logger->debug("interpolating hwpr angle");
879 int n_times = nw_times.size();
880 nd << nw_times[n_times - 1].size();
881
882 // vector to store interpolated outputs in
883 Eigen::VectorXd yi(n_samples);
884
885 mlinterp::interp(nd.data(), n_samples, // nd, ni
886 engine().calib.hwpr_angle.data(), yi.data(), // yd, yi
887 nw_times[n_times - 1].data(), t_common.data()); // xd, xi
888
889 engine().calib.hwpr_angle = std::move(yi);
890 }
891
892 engine().t_common = t_common;
893 engine().masks = masks;
894 engine().nw_times = nw_times;
895}
896
897template <class EngineType>
899 // how many offsets in config file
900 Eigen::Index n_offsets = engine().pointing_offsets_arcsec["az"].size();
901
902 // keys for pointing offsets
903 std::vector<std::string> altaz_keys = {"alt", "az"};
904
905 for (const auto &key: altaz_keys) {
906 // if only one value given
907 if (n_offsets==1) {
908 double offset = engine().pointing_offsets_arcsec[key](0);
909 engine().pointing_offsets_arcsec[key].resize(engine().telescope.tel_data["TelTime"].size());
910 engine().pointing_offsets_arcsec[key].setConstant(offset);
911 }
912 else if (n_offsets==2) {
913 // size of telescope data
914 Eigen::Index ni = engine().telescope.tel_data["TelTime"].size();
915
916 // size of telescope data
917 Eigen::Matrix<Eigen::Index,1,1> nd;
918 nd << n_offsets;
919
920 // vector to store interpolation
921 Eigen::VectorXd yi(ni);
922
923 // start and end times of observation
924 Eigen::VectorXd xd(n_offsets);
925 // use start and end of current obs if julian dates not specified
926 if ((engine().pointing_offsets_modified_julian_date<=0).any()) {
927 xd << engine().telescope.tel_data["TelTime"](0), engine().telescope.tel_data["TelTime"](ni-1);
928 }
929 // else use specified modified julian dates, convert to julian dates, and calc unix time
930 else {
931 xd << engine_utils::modified_julian_date_to_unix(engine().pointing_offsets_modified_julian_date(0)),
932 engine_utils::modified_julian_date_to_unix(engine().pointing_offsets_modified_julian_date(1));
933
934 // make sure offsets are before and after the observation
935 if (xd(0) > engine().telescope.tel_data["TelTime"](0) || xd(1) < engine().telescope.tel_data["TelTime"](ni-1)) {
936 logger->error("MJD range is invalid");
937 std::exit(EXIT_FAILURE);
938 }
939 }
940
941 // interpolate offset onto time vector
942 mlinterp::interp(nd.data(), ni, // nd, ni
943 engine().pointing_offsets_arcsec[key].data(), yi.data(), // yd, yi
944 xd.data(), engine().telescope.tel_data["TelTime"].data()); // xd, xi
945
946 // overwrite pointing offsets
947 engine().pointing_offsets_arcsec[key] = yi;
948 }
949 else {
950 logger->error("only one or two values for altaz offsets are supported");
951 std::exit(EXIT_FAILURE);
952 }
953 }
954}
955
956// get map number
957template <class EngineType>
959 // auto map grouping
960 if (engine().map_grouping=="auto") {
961 // array map grouping for science and pointing
962 if ((engine().redu_type == "science") || (engine().redu_type == "pointing")) {
963 engine().map_grouping = "array";
964 }
965
966 // detector map grouping for beammaps
967 else if ((engine().redu_type == "beammap")) {
968 engine().map_grouping = "detector";
969 }
970 }
971
972 // overwrite map number for detectors
973 if (engine().map_grouping == "detector") {
974 engine().n_maps = engine().calib.n_dets;
975 }
976
977 // overwrite map number for networks
978 else if (engine().map_grouping == "nw") {
979 engine().n_maps = engine().calib.n_nws;
980 }
981
982 // overwrite map number for arrays
983 else if (engine().map_grouping == "array") {
984 engine().n_maps = engine().calib.n_arrays;
985 }
986
987 // overwrite map number for fg grouping
988 else if (engine().map_grouping == "fg") {
989 // there are potentially 4 fg's per array, so total number of maps is max 4 x n_arrays
990 engine().n_maps = engine().calib.fg.size()*engine().calib.n_arrays;
991 }
992
993 if (engine().rtcproc.run_polarization) {
994 // multiply by number of polarizations (stokes I + Q + U = 3)
995 engine().n_maps = engine().n_maps*engine().rtcproc.polarization.stokes_params.size();
996 }
997
998 // mapping from index in map vector to detector array index
999 // if stokes I array grouping with all arrays, this will be [0,1,2]
1000 // if missing array 0, this will be [1,2]
1001 engine().maps_to_arrays.resize(engine().n_maps);
1002
1003 // mapping from index in map vector to stokes parameter index (I=0, Q=1, U=2)
1004 // if array grouping with all arrays this will be [0,0,0,1,1,2,2,2]
1005 // and maps_to_arrays will be [0,1,2,0,1,2,0,1,2]
1006 engine().maps_to_stokes.resize(engine().n_maps);
1007
1008 // mapping from array index to index in map vectors (reverse of maps_to_arrays)
1009 // if stokes I array grouping with all arrays, this will also be [0,1,2]
1010 // if missing array 0, this will be [0,1]
1011 engine().arrays_to_maps.resize(engine().n_maps);
1012
1013 // array to hold mapping from group to detector array index
1014 Eigen::VectorXI array_indices;
1015
1016 // detector gropuing
1017 if (engine().map_grouping == "detector") {
1018 // only do stokes I as Q and U don't make sense for detector grouping
1019 // this is just a copy of the array indices from the apt
1020 array_indices = engine().calib.apt["array"].template cast<Eigen::Index> ();
1021 }
1022
1023 // array grouping
1024 else if (engine().map_grouping == "array") {
1025 // if all arrays are included this will be [0,1,2]
1026 array_indices = engine().calib.arrays;
1027 }
1028
1029 // network grouping
1030 else if (engine().map_grouping == "nw") {
1031 // if all nws/arrays are included this will be:
1032 // [0,0,0,0,0,0,0,0,1,1,1,1,2,2]
1033 // nws are ordered automatically when files are read in
1034 array_indices.resize(engine().calib.nws.size());
1035
1036 // find all map from nw to arrays
1037 for (Eigen::Index i=0; i<engine().calib.nws.size(); ++i) {
1038 // get array for current nw
1039 array_indices(i) = engine().toltec_io.nw_to_array_map[engine().calib.nws(i)];
1040 }
1041 }
1042
1043 // frequency grouping
1044 else if (engine().map_grouping == "fg") {
1045 // size of array indices is number of fg's x number of arrays
1046 // if all fgs are included, this will be:
1047 // [0,0,0,0,0,1,1,1,1,1,2,2,2,2,2]
1048 // the order of the fgs will vary depending on the apt, but this is irrelevant
1049 array_indices.resize(engine().calib.fg.size()*engine().calib.n_arrays);
1050
1051 // map from fg to array index
1052 Eigen::Index j = 0;
1053 // loop through arrays
1054 for (Eigen::Index i=0; i<engine().calib.n_arrays; ++i) {
1055 // append current array index to all elements within a segment of fg size
1056 array_indices.segment(j,engine().calib.fg.size()).setConstant(engine().calib.arrays(i));
1057 // increment by fg size
1058 j = j + engine().calib.fg.size();
1059 }
1060 }
1061
1062 // copy array_indices into maps_to_arrays and maps_to_stokes for each stokes param
1063 Eigen::Index j = 0;
1064 // loop through stokes params
1065 for (const auto &[stokes_index,stokes_param]: engine().rtcproc.polarization.stokes_params) {
1066 // for each stokes param append all array indices in order
1067 engine().maps_to_arrays.segment(j,array_indices.size()) = array_indices;
1068 // for each stokes param append current stokes index
1069 engine().maps_to_stokes.segment(j,array_indices.size()).setConstant(stokes_index);
1070 // increment by array index size
1071 j = j + array_indices.size();
1072 }
1073
1074 // calculate detector array index to map index
1075 Eigen::Index index = 0;
1076 // start at map index 0
1077 engine().arrays_to_maps(0) = index;
1078 for (Eigen::Index i=1; i<engine().n_maps; ++i) {
1079 // we move to the next map index when the array increments
1080 if (engine().maps_to_arrays(i) > engine().maps_to_arrays(i-1)) {
1081 index++;
1082 }
1083 // reset to first map index when we return the an earlier array
1084 else if (engine().maps_to_arrays(i) < engine().maps_to_arrays(i-1)) {
1085 index = 0;
1086 }
1087 engine().arrays_to_maps(i) = index;
1088 }
1089}
1090
1091// calculate map dimensions
1092template <class EngineType>
1093void TimeOrderedDataProc<EngineType>::calc_omb_size(std::vector<map_extent_t> &map_extents, std::vector<map_coord_t> &map_coords) {
1094
1095 // reference to map buffer
1096 auto& omb = engine().omb;
1097
1098 // only run if manual map sizes have not been input
1099 if ((engine().omb.wcs.naxis[0] <= 0) || (engine().omb.wcs.naxis[1] <= 0)) {
1100 // matrix to store size limits
1101 Eigen::MatrixXd det_lat_limits(engine().calib.n_dets, 2);
1102 Eigen::MatrixXd det_lon_limits(engine().calib.n_dets, 2);
1103 det_lat_limits.setZero();
1104 det_lon_limits.setZero();
1105
1106 // placeholder vectors for grppi maps
1107 std::vector<int> det_in_vec, det_out_vec;
1108
1109 // placeholder vectors for grppi loop
1110 det_in_vec.resize(engine().calib.n_dets);
1111 std::iota(det_in_vec.begin(), det_in_vec.end(), 0);
1112 det_out_vec.resize(engine().calib.n_dets);
1113
1114 // get telescope meta data for current scan
1115 std::map<std::string, Eigen::VectorXd> tel_data;
1116
1117 // pointing offsets
1118 std::map<std::string, Eigen::VectorXd> pointing_offsets_arcsec;
1119
1120 // loop through scans
1121 for (Eigen::Index i=0; i<engine().telescope.scan_indices.cols(); ++i) {
1122 // lower scan index
1123 auto si = engine().telescope.scan_indices(0,i);
1124 // upper scan index
1125 auto sl = engine().telescope.scan_indices(1,i) - engine().telescope.scan_indices(0,i) + 1;
1126
1127 for (auto const& x: engine().telescope.tel_data) {
1128 tel_data[x.first] = engine().telescope.tel_data[x.first].segment(si,sl);
1129 }
1130
1131 // get pointing offsets for current scan
1132 pointing_offsets_arcsec["az"] = engine().pointing_offsets_arcsec["az"].segment(si,sl);
1133 pointing_offsets_arcsec["alt"] = engine().pointing_offsets_arcsec["alt"].segment(si,sl);
1134
1135 // don't need to find the offsets if in detector mode
1136 if (engine().map_grouping!="detector") {
1137 // loop through detectors
1138 grppi::map(tula::grppi_utils::dyn_ex(engine().parallel_policy), det_in_vec, det_out_vec, [&](auto j) {
1139
1140 // get pointing
1141 auto [lat, lon] = engine_utils::calc_det_pointing(tel_data, engine().calib.apt["x_t"](j), engine().calib.apt["y_t"](j),
1142 engine().telescope.pixel_axes, pointing_offsets_arcsec, engine().map_grouping);
1143 // check for min and max
1144 if (engine().calib.apt["flag"](j)==0) {
1145 if (lat.minCoeff() < det_lat_limits(j,0)) {
1146 det_lat_limits(j,0) = lat.minCoeff();
1147 }
1148 if (lat.maxCoeff() > det_lat_limits(j,1)) {
1149 det_lat_limits(j,1) = lat.maxCoeff();
1150 }
1151 if (lon.minCoeff() < det_lon_limits(j,0)) {
1152 det_lon_limits(j,0) = lon.minCoeff();
1153 }
1154 if (lon.maxCoeff() > det_lon_limits(j,1)) {
1155 det_lon_limits(j,1) = lon.maxCoeff();
1156 }
1157 }
1158 return 0;
1159 });
1160 }
1161 else {
1162 // calculate detector pointing for first detector only since offsets are zero
1163 auto [lat, lon] = engine_utils::calc_det_pointing(tel_data, 0., 0., engine().telescope.pixel_axes,
1164 pointing_offsets_arcsec, engine().map_grouping);
1165 if (lat.minCoeff() < det_lat_limits(0,0)) {
1166 det_lat_limits.col(0).setConstant(lat.minCoeff());
1167 }
1168 if (lat.maxCoeff() > det_lat_limits(0,1)) {
1169 det_lat_limits.col(1).setConstant(lat.maxCoeff());
1170 }
1171 if (lon.minCoeff() < det_lon_limits(0,0)) {
1172 det_lon_limits.col(0).setConstant(lon.minCoeff());
1173 }
1174 if (lon.maxCoeff() > det_lon_limits(0,1)) {
1175 det_lon_limits.col(1).setConstant(lon.maxCoeff());
1176 }
1177 }
1178 }
1179
1180 // get the global min and max
1181 double min_lat = det_lat_limits.col(0).minCoeff();
1182 double max_lat = det_lat_limits.col(1).maxCoeff();
1183 double min_lon = det_lon_limits.col(0).minCoeff();
1184 double max_lon = det_lon_limits.col(1).maxCoeff();
1185
1186 // calculate dimensions
1187 auto calc_map_dims = [&](double min_dim, double max_dim) {
1188 int min_pix = static_cast<int>(ceil(abs(min_dim / omb.pixel_size_rad)));
1189 int max_pix = static_cast<int>(ceil(abs(max_dim / omb.pixel_size_rad)));
1190 return 2 * std::max(min_pix, max_pix) + 1;
1191 };
1192
1193 // get n_rows and n_cols
1194 omb.n_rows = calc_map_dims(min_lat, max_lat);
1195 omb.n_cols = calc_map_dims(min_lon, max_lon);
1196 }
1197
1198 else {
1199 // Ensure odd dimensions
1200 omb.n_rows = (omb.wcs.naxis[1] % 2 == 0) ? omb.wcs.naxis[1] + 1 : omb.wcs.naxis[1];
1201 omb.n_cols = (omb.wcs.naxis[0] % 2 == 0) ? omb.wcs.naxis[0] + 1 : omb.wcs.naxis[0];
1202 }
1203
1204 Eigen::VectorXd rows_tan_vec = Eigen::VectorXd::LinSpaced(omb.n_rows, 0, omb.n_rows - 1).array() * omb.pixel_size_rad -
1205 (omb.n_rows / 2.0) * omb.pixel_size_rad;
1206 Eigen::VectorXd cols_tan_vec = Eigen::VectorXd::LinSpaced(omb.n_cols, 0, omb.n_cols - 1).array() * omb.pixel_size_rad -
1207 (omb.n_cols / 2.0) * omb.pixel_size_rad;
1208
1209
1210 // push back map sizes and coordinates
1211 map_extents.push_back({static_cast<int>(omb.n_rows), static_cast<int>(omb.n_cols)});
1212 map_coords.push_back({std::move(rows_tan_vec), std::move(cols_tan_vec)});
1213}
1214
1215// determine the map dimensions of the coadded map buffer
1216template <class EngineType>
1217void TimeOrderedDataProc<EngineType>::calc_cmb_size(std::vector<map_coord_t> &map_coords) {
1218 auto& cmb = engine().cmb;
1219
1220 // Initialize min/max values
1221 double min_row = std::numeric_limits<double>::max();
1222 double max_row = std::numeric_limits<double>::lowest();
1223 double min_col = min_row;
1224 double max_col = max_row;
1225
1226 // Find global min/max for rows and columns
1227 for (const auto& coord : map_coords) {
1228 min_row = std::min(min_row, coord.front().minCoeff());
1229 max_row = std::max(max_row, coord.front().maxCoeff());
1230 min_col = std::min(min_col, coord.back().minCoeff());
1231 max_col = std::max(max_col, coord.back().maxCoeff());
1232 }
1233
1234 // calculate dimensions
1235 auto calc_map_dims = [&](auto min_dim, auto max_dim) {
1236 int min_pix = static_cast<int>(ceil(abs(min_dim / engine().cmb.pixel_size_rad)));
1237 int max_pix = static_cast<int>(ceil(abs(max_dim / engine().cmb.pixel_size_rad)));
1238
1239 int n_dim = 2 * std::max(min_pix, max_pix) + 1;
1240 Eigen::VectorXd dim_vec = Eigen::VectorXd::LinSpaced(n_dim, 0, n_dim - 1)
1241 .array() * engine().cmb.pixel_size_rad - (n_dim / 2.0) * engine().cmb.pixel_size_rad;
1242
1243 return std::make_tuple(n_dim, std::move(dim_vec));
1244 };
1245
1246 // get dimensions and tangent coordinate vectorx
1247 auto [n_rows, rows_tan_vec] = calc_map_dims(min_row, max_row);
1248 auto [n_cols, cols_tan_vec] = calc_map_dims(min_col, max_col);
1249
1250 // Set dimensions and wcs parameters
1251 cmb.n_rows = n_rows;
1252 cmb.n_cols = n_cols;
1253 cmb.wcs.naxis[0] = n_cols;
1254 cmb.wcs.naxis[1] = n_rows;
1255 cmb.wcs.crpix[0] = (n_cols - 1) / 2.0;
1256 cmb.wcs.crpix[1] = (n_rows - 1) / 2.0;
1257 // set tangent plane coordinate vectors
1258 cmb.rows_tan_vec = std::move(rows_tan_vec);
1259 cmb.cols_tan_vec = std::move(cols_tan_vec);
1260}
1261
1262// allocate observation map buffer
1263template <class EngineType>
1265 auto& omb = engine().omb;
1266
1267 std::vector<Eigen::MatrixXd>().swap(omb.signal);
1268 std::vector<Eigen::MatrixXd>().swap(omb.weight);
1269 std::vector<Eigen::MatrixXd>().swap(omb.kernel);
1270 std::vector<Eigen::MatrixXd>().swap(omb.coverage);
1271 std::vector<Eigen::MatrixXd>().swap(omb.pointing);
1272
1273 // set omb dimensions and wcs parameters
1274 omb.n_rows = map_extent[0];
1275 omb.n_cols = map_extent[1];
1276 omb.wcs.naxis[0] = omb.n_cols;
1277 omb.wcs.naxis[1] = omb.n_rows;
1278 omb.wcs.crpix[0] = (omb.n_cols - 1) / 2.0;
1279 omb.wcs.crpix[1] = (omb.n_rows - 1) / 2.0;
1280 // set tangent plane coordinate vectors
1281 omb.rows_tan_vec = map_coord[0];
1282 omb.cols_tan_vec = map_coord[1];
1283
1284 Eigen::MatrixXd zero_matrix = Eigen::MatrixXd::Zero(omb.n_rows, omb.n_cols);
1285
1286 for (Eigen::Index i=0; i<engine().n_maps; ++i) {
1287 omb.signal.push_back(zero_matrix);
1288 omb.weight.push_back(zero_matrix);
1289
1290 if (engine().rtcproc.run_kernel) {
1291 omb.kernel.push_back(zero_matrix);
1292 }
1293
1294 if (engine().map_grouping != "detector") {
1295 omb.coverage.push_back(zero_matrix);
1296 }
1297 }
1298
1299 if (engine().rtcproc.run_polarization) {
1300 // allocate pointing matrix
1301 for (Eigen::Index i=0; i<engine().n_maps/engine().rtcproc.polarization.stokes_params.size(); ++i) {
1302 omb.pointing.emplace_back(omb.n_rows*omb.n_cols, 9);
1303 engine().omb.pointing.back().setZero();
1304 }
1305 }
1306}
1307
1308// allocate the coadded map buffer
1309template <class EngineType>
1311 auto& cmb = engine().cmb;
1312
1313 // clear map vectors
1314 std::vector<Eigen::MatrixXd>().swap(cmb.signal);
1315 std::vector<Eigen::MatrixXd>().swap(cmb.weight);
1316 std::vector<Eigen::MatrixXd>().swap(cmb.kernel);
1317 std::vector<Eigen::MatrixXd>().swap(cmb.coverage);
1318 std::vector<Eigen::MatrixXd>().swap(cmb.pointing);
1319
1320 Eigen::MatrixXd zero_matrix = Eigen::MatrixXd::Zero(cmb.n_rows, cmb.n_cols);
1321
1322 // loop through maps and allocate space
1323 for (Eigen::Index i=0; i<engine().n_maps; ++i) {
1324 cmb.signal.push_back(zero_matrix);
1325 cmb.weight.push_back(zero_matrix);
1326
1327 if (engine().rtcproc.run_kernel) {
1328 // allocate kernel
1329 cmb.kernel.push_back(zero_matrix);
1330 }
1331 if (engine().map_grouping!="detector") {
1332 // allocate coverage
1333 cmb.coverage.push_back(zero_matrix);
1334 }
1335 }
1336
1337 if (engine().rtcproc.run_polarization) {// && engine().run_noise) {
1338 // allocate pointing matrix
1339 for (Eigen::Index i=0; i<engine().n_maps/engine().rtcproc.polarization.stokes_params.size(); ++i) {
1340 cmb.pointing.emplace_back(cmb.n_rows*cmb.n_cols, 9);
1341 cmb.pointing.back().setZero();
1342 }
1343 }
1344}
1345
1346template <class EngineType>
1347template <class map_buffer_t>
1349 // clear noise map buffer
1350 std::vector<Eigen::Tensor<double,3>>().swap(nmb.noise);
1351
1352 // resize noise maps (n_maps, [n_rows, n_cols, n_noise])
1353 for (Eigen::Index i=0; i<engine().n_maps; ++i) {
1354 nmb.noise.emplace_back(nmb.n_rows, nmb.n_cols, nmb.n_noise);
1355 nmb.noise.at(i).setZero();
1356 }
1357}
1358
1359// coadd maps
1360template <class EngineType>
1362 // calculate the offset between cmb and omb
1363 int delta_row = 0.5*(engine().cmb.n_rows - engine().omb.n_rows);
1364 int delta_col= 0.5*(engine().cmb.n_cols - engine().omb.n_cols);
1365
1366 // loop through the maps
1367 for (Eigen::Index i=0; i<engine().n_maps; ++i) {
1368 // define common block references
1369 auto cmb_weight_block = engine().cmb.weight.at(i).block(delta_row, delta_col, engine().omb.n_rows, engine().omb.n_cols);
1370 auto cmb_signal_block = engine().cmb.signal.at(i).block(delta_row, delta_col, engine().omb.n_rows, engine().omb.n_cols);
1371
1372 // update cmb.weight with omb.weight
1373 cmb_weight_block += engine().omb.weight.at(i);
1374
1375 // update cmb.signal with omb.signal * omb.weight
1376 cmb_signal_block += (engine().omb.signal.at(i).array() * engine().omb.weight.at(i).array()).matrix();
1377
1378 // update cmb.kernel with omb.kernel * omb.weight
1379 if (engine().rtcproc.run_kernel) {
1380 auto cmb_kernel_block = engine().cmb.kernel.at(i).block(delta_row, delta_col, engine().omb.n_rows, engine().omb.n_cols);
1381 cmb_kernel_block += (engine().omb.kernel.at(i).array() * engine().omb.weight.at(i).array()).matrix();
1382 }
1383
1384 // update coverage
1385 if (!engine().cmb.coverage.empty()) {
1386 auto cmb_coverage_block = engine().cmb.coverage.at(i).block(delta_row, delta_col, engine().omb.n_rows, engine().omb.n_cols);
1387 cmb_coverage_block += engine().omb.coverage.at(i);
1388 }
1389 }
1390}
1391
1392template <class EngineType>
1394 // clear fits_io vectors
1395 std::vector<fitsIO<file_type_enum::write_fits, CCfits::ExtHDU*>>().swap(engine().coadd_fits_io_vec);
1396 std::vector<fitsIO<file_type_enum::write_fits, CCfits::ExtHDU*>>().swap(engine().coadd_noise_fits_io_vec);
1397 std::vector<fitsIO<file_type_enum::write_fits, CCfits::ExtHDU*>>().swap(engine().filtered_coadd_fits_io_vec);
1398 std::vector<fitsIO<file_type_enum::write_fits, CCfits::ExtHDU*>>().swap(engine().filtered_coadd_noise_fits_io_vec);
1399
1400 // loop through arrays
1401 for (Eigen::Index i=0; i<engine().calib.n_arrays; ++i) {
1402 // array index
1403 auto array = engine().calib.arrays[i];
1404 // array name
1405 std::string array_name = engine().toltec_io.array_name_map[array];
1406 // map filename
1407 auto filename = engine().toltec_io.template create_filename<engine_utils::toltecIO::toltec, engine_utils::toltecIO::map,
1408 engine_utils::toltecIO::raw>(engine().coadd_dir_name + "raw/",
1409 "", array_name, "",
1410 engine().telescope.sim_obs);
1411 // create fits_io class for current array file
1413 // append to fits_io vector
1414 engine().coadd_fits_io_vec.push_back(std::move(fits_io));
1415
1416 // if noise maps requested
1417 if (engine().run_noise) {
1418 // noise map filename
1419 auto filename = engine().toltec_io.template create_filename<engine_utils::toltecIO::toltec, engine_utils::toltecIO::noise,
1420 engine_utils::toltecIO::raw>(engine().coadd_dir_name + "raw/",
1421 "", array_name, "",
1422 engine().telescope.sim_obs);
1423 // create fits_io class for current array file
1425 // append to fits_io vector
1426 engine().coadd_noise_fits_io_vec.push_back(std::move(fits_io));
1427 }
1428 }
1429
1430 // if map filtering are requested
1431 if (engine().run_map_filter) {
1432 // loop through arrays
1433 for (Eigen::Index i=0; i<engine().calib.n_arrays; ++i) {
1434 // array index
1435 auto array = engine().calib.arrays[i];
1436 // array name
1437 std::string array_name = engine().toltec_io.array_name_map[array];
1438 // filtered map filename
1439 auto filename = engine().toltec_io.template create_filename<engine_utils::toltecIO::toltec, engine_utils::toltecIO::map,
1440 engine_utils::toltecIO::filtered>(engine().coadd_dir_name +
1441 "filtered/","", array_name,
1442 "", engine().telescope.sim_obs);
1443 // create fits_io class for current array file
1445 // append to fits_io vector
1446 engine().filtered_coadd_fits_io_vec.push_back(std::move(fits_io));
1447
1448 // if noise maps requested
1449 if (engine().run_noise) {
1450 // filtered noise map filename
1451 auto filename = engine().toltec_io.template create_filename<engine_utils::toltecIO::toltec, engine_utils::toltecIO::noise,
1452 engine_utils::toltecIO::filtered>(engine().coadd_dir_name +
1453 "filtered/","", array_name,
1454 "", engine().telescope.sim_obs);
1455 // create fits_io class for current array file
1457 // append to fits_io vector
1458 engine().filtered_coadd_noise_fits_io_vec.push_back(std::move(fits_io));
1459 }
1460 }
1461 }
1462}
1463
1464template <class EngineType>
1466 // get sortedfiles and directories in filepath
1467 std::set<fs::path> sorted_by_name;
1468 for (auto &entry : fs::directory_iterator(filepath))
1469 sorted_by_name.insert(entry);
1470
1471 // yaml node to store names
1472 YAML::Node node;
1473 // data products
1474 node["description"].push_back("citlali data products");
1475 // datetime when file is created
1476 node["date"].push_back(engine_utils::current_date_time());
1477 // citlali version
1478 node["citlali_version"].push_back(CITLALI_GIT_VERSION);
1479 // kids version
1480 node["kids_version"].push_back(KIDSCPP_GIT_VERSION);
1481 // tula version
1482 node["tula_version"].push_back(TULA_GIT_VERSION);
1483
1484 // call make_index_file recursively if current object is directory
1485 for (const auto & entry : sorted_by_name) {
1486 std::string path_string{entry.generic_string()};
1487 if (fs::is_directory(entry)) {
1488 make_index_file(path_string);
1489 }
1490 node["files/dirs"].push_back(path_string.substr(path_string.find_last_of("/") + 1));
1491 }
1492 // output yaml index file
1493 std::ofstream fout(filepath + "/index.yaml");
1494 fout << node;
1495}
Definition engine.h:155
@ map
Definition toltec_io.h:22
@ noise
Definition toltec_io.h:23
@ toltec
Definition toltec_io.h:14
@ filtered
Definition toltec_io.h:34
@ raw
Definition toltec_io.h:33
Definition fits_io.h:10
config::ConfigValidatorMixin< Derived, config::YamlConfig > ConfigMapper
Definition main_old.cpp:124
auto calc_det_pointing(tel_data_t &tel_data, double az_off, double el_off, const std::string pixel_axes, pointing_offset_t &pointing_offsets, const std::string map_grouping)
Definition pointing.h:11
static long long modified_julian_date_to_unix(double jd)
Definition utils.h:157
static const std::string current_date_time()
Definition utils.h:91
Definition calib.h:12
Definition timestream.h:59
Definition netcdf_io.h:5
Definition todproc.h:16
friend OStream & operator<<(OStream &os, const DummyEngine &e)
Definition todproc.h:18
The DataItem struct This represent a single data item that belongs to a particular observation.
Definition io.h:58
const std::string & filepath() const
Definition io.h:81
The raw obs struct This represents a single observation that contains a set of data items and calibra...
Definition io.h:50
auto kidsdata() const -> decltype(auto)
Definition io.h:283
The time ordered data processing struct This wraps around the lali config.
Definition todproc.h:29
void calc_map_num()
Definition todproc.h:958
Eigen::MatrixXI scanindicies_t
Definition todproc.h:33
const Engine & engine() const
Definition todproc.h:126
void get_apt_from_files(const RawObs &rawobs)
Definition todproc.h:141
void make_index_file(std::string)
Definition todproc.h:1465
void get_tone_freqs_from_files(const RawObs &rawobs)
Definition todproc.h:217
void calc_omb_size(std::vector< map_extent_t > &, std::vector< map_coord_t > &)
Definition todproc.h:1093
ConfigMapper< TimeOrderedDataProc< EngineType > > Base
Definition todproc.h:30
void allocate_omb(map_extent_t &, map_coord_t &)
Definition todproc.h:1264
void check_inputs(const RawObs &rawobs)
Definition todproc.h:406
void create_coadded_map_files()
Definition todproc.h:1393
void coadd()
Definition todproc.h:1361
void create_output_dir()
Definition todproc.h:347
std::size_t map_count_t
Definition todproc.h:36
void interp_pointing()
Definition todproc.h:898
void calc_cmb_size(std::vector< map_coord_t > &)
Definition todproc.h:1217
std::vector< std::tuple< Eigen::Index, Eigen::Index > > array_indices_t
Definition todproc.h:37
std::vector< Eigen::VectorXd > map_coord_t
Definition todproc.h:35
Engine m_engine
Definition todproc.h:136
void align_timestreams_gaps(const RawObs &rawobs)
Definition todproc.h:704
TimeOrderedDataProc(config_t config)
Definition todproc.h:43
std::shared_ptr< spdlog::logger > logger
Definition todproc.h:41
static auto check_config(const config_t &config) -> std::optional< std::string >
Definition todproc.h:46
Engine & engine()
Definition todproc.h:124
typename Base::config_t config_t
Definition todproc.h:31
std::vector< std::tuple< Eigen::Index, Eigen::Index > > det_indices_t
Definition todproc.h:38
void allocate_cmb()
Definition todproc.h:1310
void get_adc_snap_from_files(const RawObs &rawobs)
Definition todproc.h:311
std::vector< int > map_extent_t
Definition todproc.h:34
friend OStream & operator<<(OStream &os, const TimeOrderedDataProc &todproc)
Definition todproc.h:129
void allocate_nmb(map_buffer_t &)
Definition todproc.h:1348
void align_timestreams(const RawObs &rawobs)
Definition todproc.h:439