Citlali
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1#pragma once
2
3#include <time.h>
4#include <vector>
5#include <numeric>
6#include <complex>
7//#include <png.h>
8
9#include <fftw3.h>
10
11#include <Eigen/Core>
12#include <unsupported/Eigen/FFT>
13#include <unsupported/Eigen/Splines>
14
15#include <tula/grppi.h>
16#include <tula/algorithm/ei_stats.h>
17
19
20//#include "matplotlibcpp.h"
21
22namespace engine_utils {
23
24/*int next_power_of_two(int n) {
25 return pow(2,std::ceil(std::log2(n)));
26}*/
27
28/*void output_memory_usage() {
29 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
30 std::string line;
31 std::ifstream procStatus("/proc/self/status");
32
33 while (std::getline(procStatus, line)) {
34 // Check for memory usage entries
35 if (line.substr(0, 6) == "VmSize") { // Virtual memory size
36 logger->info("Total memory used: {}", line.substr(7));
37 }
38 if (line.substr(0, 6) == "VmRSS") { // Resident set size
39 logger->info("Resident set size (actual physical memory): {}", line.substr(7));
40 }
41 }
42}*/
43
44static const int parseLine(char* line){
45 // this assumes that a digit will be found and the line ends in " Kb".
46 int i = strlen(line);
47 const char* p = line;
48 while (*p <'0' || *p > '9') {
49 p++;
50 }
51 line[i-3] = '\0';
52 i = atoi(p);
53 return i;
54}
55
56static const int get_phys_memory() { // note: this value is in KB!
57 FILE* file = fopen("/proc/self/status", "r");
58 int result = -1;
59 char line[128];
60
61 while (fgets(line, 128, file) != NULL){
62 if (strncmp(line, "VmRSS:", 6) == 0){
63 result = parseLine(line);
64 break;
65 }
66 }
67 fclose(file);
68 return result;
69}
70
71// planck function (W x sr-1 x m-2 x Hz−1)
72static const double planck_nu(const double freq_Hz, const double T_K) {
73 return 2.*h_J_s*pow(freq_Hz,3)/pow(c_m_s,2)/(exp((h_J_s*freq_Hz)/(kB_J_K*T_K)) - 1.);
74}
75
76// convert flux in mJy/beam to uK
77static const double mJy_beam_to_uK(const double flux_mjy_beam, const double freq_Hz, const double fwhm) {
78 // planck function at T_CMB
79 auto planck = planck_nu(freq_Hz, T_cmb_K);
80 // exponent term (h x nu)/(k_B x T_cmb)
81 auto h_nu_k_T = (h_J_s*freq_Hz)/(kB_J_K*T_cmb_K);
82 // beam area in steradians
83 //auto beam_area_rad = 2.*pi*pow(fwhm*FWHM_TO_STD*ASEC_TO_RAD,2);
84 // conversion from K to mJy/beam
85 auto K_to_mJy_beam = planck*exp(h_nu_k_T)/(exp(h_nu_k_T)-1)*h_nu_k_T/pow(T_cmb_K,2)*1e26*1e3;//*beam_area_rad;
86 // flux in mJy/beam converted to uK
87 return 1e6*flux_mjy_beam/K_to_mJy_beam;
88}
89
90// get current date/time, format is YYYY-MM-DD.HH:mm:ss
91static const std::string current_date_time() {
92 time_t now = time(0);
93 struct tm tstruct;
94 char buf[80];
95 tstruct = *localtime(&now);
96 strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
97
98 return buf;
99}
100
101// convert unixt time to utc, format is YYYY-MM-DD.HH:mm:ss
102static const std::string unix_to_utc(double &t) {
103 time_t now = t;
104 struct tm tstruct;
105 char buf[80];
106 tstruct = *localtime(&now);
107 strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
108
109 return buf;
110}
111
112template <typename DerivedA, typename DerivedB>
113void utc_to_unix(Eigen::DenseBase<DerivedA> &tel_utc, Eigen::DenseBase<DerivedB> &ut_date) {
114 // size of time vector
115 Eigen::Index n_pts = tel_utc.size();
116
117 time_t utc_time;
118
119 // who would have guessed?
120 auto days_in_year = 365.0;
121
122 int year = std::floor(ut_date(0));
123 int days = int(((ut_date(0)-year)*days_in_year)+1);
124
125 auto ut_time = 180/15/pi * tel_utc.derived().array();
126
127 Eigen::VectorXd tel_unix(n_pts);
128
129 // loop through points
130 for (Eigen::Index i=0; i<n_pts; ++i) {
131 auto h =(int)ut_time(i);
132 auto m = (int)((ut_time(i) - h)*60);
133 auto s = (((ut_time(i) - h)*60 - m)*60);
134
135 struct tm tm_time;
136 tm_time.tm_isdst = -1;
137 tm_time.tm_mon = 0;
138 tm_time.tm_mday = days;
139 tm_time.tm_year = year - 1900;
140
141 tm_time.tm_sec = s;
142 tm_time.tm_min = m;
143 tm_time.tm_hour = h;
144
145 // get UTC time
146 utc_time = timegm(&tm_time);
147
148 // convert UTC time to Unix timestamp
149 time_t unix_time = (time_t) utc_time;
150 // add Unix time to vector
151 tel_unix(i) = unix_time;
152 }
153 // overrwite UTC time with Unix time
154 tel_utc = tel_unix;
155}
156
157static long long modified_julian_date_to_unix(double jd) {
158 // unix epoch in julian date
159 const double UNIX_EPOCH_JD = 2440587.5;
160
161 // difference in days and convert from modified julian to julian date
162 double diff = jd - UNIX_EPOCH_JD + 2400000.5;
163
164 // convert days to seconds
165 long long unix_time = static_cast<long long>(diff * 86400.0);
166
167 return unix_time;
168}
169
170static double unix_to_modified_julian_date(double unix_time) {
171 const double seconds_per_day = 86400.0;
172 const double mjd_offset = 40587.0;
173 return unix_time / seconds_per_day + mjd_offset;
174}
175
176template <typename Derived>
177auto make_meshgrid(const Eigen::DenseBase<Derived> &x, const Eigen::DenseBase<Derived> &y) {
178 // column major
179 // [x0, y0,] [x1, y0] [x2, y0] ... [xn, y0] [x0, y1] ... [xn, yn]
180 const long nx = x.size(), ny = y.size();
181 Derived xy(nx * ny, 2);
182 // map xx [ny, nx] to the first column of xy
183 Eigen::Map<Eigen::MatrixXd> xx(xy.data(), ny, nx);
184 // map yy [ny, nx] to the second column of xy
185 Eigen::Map<Eigen::MatrixXd> yy(xy.data() + xy.rows(), ny, nx);
186 // populate xx such that each row is x
187 for (Eigen::Index i=0; i<ny; ++i) {
188 xx.row(i) = x.transpose();
189 }
190 // populate yy such that each col is y
191 for (Eigen::Index j=0; j<nx; ++j) {
192 yy.col(j) = y;
193 }
194 return xy;
195}
196
197// direction of fft
202
203// parallelized 2d fft using eigen
204template <FFTDirection direction, typename Derived>
205auto fft(Eigen::DenseBase<Derived> &in, std::string parallel_policy) {
206 // get dimensions of data
207 Eigen::Index n_rows = in.rows();
208 Eigen::Index n_cols = in.cols();
209
210 // output matrix
211 Eigen::MatrixXcd out(n_rows, n_cols);
212
213 // placeholder vectors for grppi maps
214 std::vector<Eigen::Index> row_vec_in(n_rows);
215 std::iota(row_vec_in.begin(), row_vec_in.end(),0);
216 std::vector<Eigen::Index> row_vec_out(n_rows);
217
218 std::vector<Eigen::Index> col_vec_in(n_cols);
219 std::iota(col_vec_in.begin(), col_vec_in.end(),0);
220 std::vector<Eigen::Index> col_vec_out(n_cols);
221
222 // do the fft over the cols
223 grppi::map(tula::grppi_utils::dyn_ex(parallel_policy),col_vec_in,col_vec_out,[&](auto i) {
224 Eigen::FFT<double> fft;
225 fft.SetFlag(Eigen::FFT<double>::HalfSpectrum);
226 fft.SetFlag(Eigen::FFT<double>::Unscaled);
227
228 Eigen::VectorXcd temp_col(n_rows);
229 // forward fft
230 if constexpr(direction == forward) {
231 fft.fwd(temp_col, in.col(i));
232 }
233 // inverse fft
234 else if constexpr(direction == inverse){
235 fft.inv(temp_col, in.col(i));
236 }
237 out.col(i) = temp_col;
238
239 return 0;});
240
241 // do the fft over the rows
242 grppi::map(tula::grppi_utils::dyn_ex(parallel_policy),row_vec_in,row_vec_out,[&](auto i) {
243 Eigen::FFT<double> fft;
244 fft.SetFlag(Eigen::FFT<double>::HalfSpectrum);
245 fft.SetFlag(Eigen::FFT<double>::Unscaled);
246
247 Eigen::VectorXcd temp_row(n_cols);
248 // forward fft
249 if constexpr(direction == forward) {
250 fft.fwd(temp_row, out.row(i));
251 }
252 // inversee fft
253 else if constexpr(direction == inverse){
254 fft.inv(temp_row, out.row(i));
255 }
256 out.row(i) = temp_row;
257
258 return 0;});
259
260 if constexpr(direction == forward) {
261 // set fft normalization
262 out = out/n_rows/n_cols;
263 }
264
265 return out;
266}
267
268// parallelized 2d fft using eigen
269template <FFTDirection direction, typename Derived, typename fftw_plan_t>
270auto fft2(Eigen::DenseBase<Derived> &in, fftw_plan_t &plan, fftw_complex* a, fftw_complex*b){
271 // get dimensions of data
272 Eigen::Index n_rows = in.rows();
273 Eigen::Index n_cols = in.cols();
274
275 // copy data from input (row major?)
276 for (Eigen::Index i=0; i< n_rows; ++i) {
277 for (Eigen::Index j=0; j<n_cols; ++j) {
278 int ii = n_cols*i + j;
279 a[ii][0] = in(i,j).real();
280 a[ii][1] = in(i,j).imag();
281 }
282 }
283
284 fftw_execute(plan);
285
286 // output matrix
287 Eigen::MatrixXcd out(n_rows, n_cols);
288
289 // copy data to output (row major?)
290 for (Eigen::Index i=0; i<n_rows; ++i) {
291 for (Eigen::Index j=0; j<n_cols; ++j) {
292 int ii = n_cols*i + j;
293 out.real()(i,j) = b[ii][0];
294 out.imag()(i,j) = b[ii][1];
295 }
296 }
297
298 if constexpr(direction == forward) {
299 // set fft normalization
300 out = out/n_rows/n_cols;
301 }
302 return out;
303}
304
305// iterate over tuple (https://stackoverflow.com/questions/26902633/how-to-iterate-over-a-stdtuple-in-c-11)
306template<class F, class...Ts, std::size_t...Is>
307void for_each_in_tuple(const std::tuple<Ts...> & tuple, F func, std::index_sequence<Is...>) {
308 using expander = int[];
309 (void)expander { 0, ((void)func(std::get<Is>(tuple)), 0)... };
310}
311
312template<class F, class...Ts>
313void for_each_in_tuple(const std::tuple<Ts...> & tuple, F func) {
314 for_each_in_tuple(tuple, func, std::make_index_sequence<sizeof...(Ts)>());
315}
316
317template<typename Derived>
318std::vector<std::tuple<double, int>> sorter(Eigen::DenseBase<Derived> &vec) {
319 std::vector<std::tuple<double, int>> vis;
320 Eigen::VectorXi indices = Eigen::VectorXi::LinSpaced(vec.size(),0,vec.size()-1);
321
322 for(Eigen::Index i=0; i<vec.size(); ++i) {
323 std::tuple<double, double> vec_and_val(vec(i), indices(i));
324 vis.push_back(vec_and_val);
325 }
326
327 std::sort(vis.begin(), vis.end(),
328 [&](const std::tuple<double, int> &a, const std::tuple<double, int> &b) -> bool {
329 return std::get<0>(a) < std::get<0>(b);
330 });
331
332 return vis;
333}
334
335template <typename DerivedA>
336auto calc_std_dev(Eigen::DenseBase<DerivedA> &data) {
337
338 auto n_good = data.derived().size();
339
340 // number of samples for divisor
341 double n_samples;
342 if (n_good == 1) {
343 n_samples = n_good;
344 }
345 else {
346 n_samples = n_good - 1;
347 }
348
349 // calc standard deviation
350 double std_dev = std::sqrt((((data.derived().array()) -
351 (data.derived().array().mean())).square().sum()) / n_samples);
352
353 return std_dev;
354}
355
356template <typename DerivedA, typename DerivedB>
357auto calc_std_dev(Eigen::DenseBase<DerivedA> &data, Eigen::DenseBase<DerivedB> &flag) {
358
359 auto f = (1 - flag.derived().template cast<double>().array());
360
361 // number of unflagged samples
362 auto n_good = (f.array() == 1).count();
363
364 // number of samples for divisor
365 double n_samples;
366
367 // no good samples
368 if (n_good == 0) {
369 return 0.0;
370 }
371 // one good sample
372 else if (n_good == 1) {
373 n_samples = n_good;
374 }
375 // > 1 good sample
376 else {
377 n_samples = n_good - 1;
378 }
379
380 auto mean = (data.derived().array()*f.array()).mean();
381
382 // calc standard deviation
383 double std_dev = std::sqrt((((data.derived().array()*f.array()) - mean).square().sum()) / n_samples);
384
385 return std_dev;
386}
387
388template <typename DerivedA>
389auto calc_rms(Eigen::DenseBase<DerivedA> &data) {
390
391 // number of unflagged samples
392 auto n_good = data.size();
393
394 // number of samples for divisor
395 double n_samples;
396
397 if (n_good == 0) {
398 return 0.0;
399 }
400
401 else {
402 n_samples = n_good;
403 }
404
405 // calc rms
406 return std::sqrt(data.derived().array().square().mean());
407}
408
409template <typename DerivedA, typename DerivedB>
410auto calc_rms(Eigen::DenseBase<DerivedA> &data, Eigen::DenseBase<DerivedB> &flag) {
411
412 // flip flags
413 auto f = (flag.derived().template cast<double>().array() - 1).abs();
414
415 // number of unflagged samples
416 auto n_good = (f.array() == 1).count();
417
418 // number of samples for divisor
419 double n_samples;
420
421 if (n_good == 0) {
422 return 0.0;
423 }
424
425 else {
426 n_samples = n_good;
427 }
428
429 // calc rms
430 return std::sqrt((((data.derived().array()*f.array())).square().sum()) / n_samples);
431}
432
433template <typename Derived>
434double calc_mad(Eigen::DenseBase<Derived> &data) {
435 Eigen::Index n_pts = data.size();
436 double med = tula::alg::median(data);
437
438 Derived abs_delt = abs(data.derived().array() - med);
439 return tula::alg::median(abs_delt);
440}
441
442static auto hanning_window(Eigen::Index n_rows, Eigen::Index n_cols) {
443 double a = 2.*pi/n_rows;
444 double b = 2.*pi/n_cols;
445
446 Eigen::ArrayXd index = Eigen::ArrayXd::LinSpaced(n_rows,0,n_rows-1);
447 Eigen::RowVectorXd row = -0.5 * cos(index*a) + 0.5;
448
449 index = Eigen::ArrayXd::LinSpaced(n_cols,0,n_cols-1);
450 Eigen::VectorXd col = -0.5 * cos(index*b) + 0.5;
451
452 Eigen::MatrixXd window(n_rows, n_cols);
453
454 for (Eigen::Index i=0; i<n_cols; ++i) {
455 for (Eigen::Index j=0; j<n_rows; ++j) {
456 window(j,i) = row(j)*col(i);
457 }
458 }
459
460 return window;
461}
462
463static double pivot_select(std::vector<double> input, int index) {
464 unsigned int pivot_index = rand() % input.size();
465 double pivot_value = input[pivot_index];
466 std::vector<double> left;
467 std::vector<double> right;
468 for (unsigned int i = 0; i < input.size(); ++i) {
469 if (i != pivot_index) {
470 if (input[i] > pivot_value) {
471 right.push_back(input[i]);
472 }
473 else {
474 left.push_back(input[i]);
475 }
476 }
477 }
478 if ((int)left.size() == index) {
479 return pivot_value;
480 }
481 else if ((int)left.size() < index) {
482 return pivot_select(right, index - left.size() - 1);
483 }
484 else {
485 return pivot_select(left, index);
486 }
487}
488
489template <typename Derived>
490double find_weight_threshold(Eigen::DenseBase<Derived> &weight, double cov) {
491
492 // find number of non-zero elements in weights
493 Eigen::Index n_non_zero = (weight.derived().array() > 0).count();
494
495 // if all values are zero, set coverage limit to zero
496 if (n_non_zero==0) {
497 return 0;
498 }
499
500 // vector to hold non-zero elements
501 //Eigen::VectorXd non_zero_weights;
502 //non_zero_weights.setZero();
503
504 std::vector<double> non_zero_weights_vec;
505
506 // check if weights are all constant
507 int diff = 0;
508 bool constant = false;
509
510 Eigen::Index k = 0;
511 // populate vector with non-zero elements
512 for (Eigen::Index i=0; i<weight.rows(); ++i) {
513 for (Eigen::Index j=0; j<weight.cols(); ++j) {
514 if (weight(i,j) > 0) {
515 //non_zero_weights(k) = weight(i,j);
516 non_zero_weights_vec.push_back(weight(i,j));
517 if (k==0) {
518 diff = weight(i,j);
519 }
520 else if (weight(i,j)!=diff) {
521 constant = true;
522 }
523 k++;
524 }
525 }
526 }
527
528 // value of weight at coverage limit value
529 double weight_val;
530
531 // if all weight values are constant, pivot search doesn't work
532 if (constant) {
533 Eigen::VectorXd non_zero_weights = Eigen::Map<Eigen::VectorXd>(non_zero_weights_vec.data(),
534 non_zero_weights_vec.size());
535 // sort in ascending order
536 std::sort(non_zero_weights.data(), non_zero_weights.data() + non_zero_weights.size(),
537 [](double lhs, double rhs){return rhs > lhs;});
538
539 // find index of upper 25 % of weights
540 Eigen::Index cov_limit_index = 0.75*non_zero_weights.size();
541
542 // get weight value at cov_limit_index + size/2
543 Eigen::Index weight_index = std::floor((cov_limit_index + non_zero_weights.size())/2.);
544 weight_val = non_zero_weights(weight_index);
545 }
546
547 // sort using pivot selection
548 else {
549 // find index of upper 25 % of weights
550 Eigen::Index cov_limit_index = 0.75*non_zero_weights_vec.size();
551 // get weight value at cov_limit_index + size/2
552 //weight_val = pivot_select(non_zero_weights_vec, cov_limit_index);
553 // get weight index
554 Eigen::Index weight_index = floor((cov_limit_index + non_zero_weights_vec.size())/2.);
555 weight_val = pivot_select(non_zero_weights_vec, weight_index);
556 }
557
558 // return weight value x coverage cut
559 return weight_val*cov;
560}
561
562template <typename Derived>
563auto set_cov_cov_ranges(const Eigen::DenseBase<Derived> &weight, const double weight_threshold) {
564 // matrix to hold coverage ranges
565 Eigen::MatrixXd cov_ranges(2,2);
566
567 cov_ranges.row(0).setZero();
568 cov_ranges(1,0) = weight.rows() - 1;
569 cov_ranges(1,1) = weight.cols() - 1;
570
571 // find lower row bound
572 bool flag = false;
573 for (Eigen::Index i=0; i<weight.rows(); ++i) {
574 for (Eigen::Index j=0; j<weight.cols(); ++j) {
575 if (weight(i,j) >= weight_threshold) {
576 cov_ranges(0,0) = i;
577 flag = true;
578 break;
579 }
580 }
581 if (flag == true) {
582 break;
583 }
584 }
585
586 // find upper row bound
587 flag = false;
588 for (Eigen::Index i=weight.rows()-1; i>-1; i--) {
589 for (Eigen::Index j = 0; j < weight.cols(); ++j) {
590 if (weight(i,j) >= weight_threshold) {
591 cov_ranges(1,0) = i;
592 flag = true;
593 break;
594 }
595 }
596 if (flag == true) {
597 break;
598 }
599 }
600
601 // find lower column bound
602 flag = false;
603 for (Eigen::Index i=0; i<weight.cols(); ++i) {
604 for (Eigen::Index j = cov_ranges(0,0); j < cov_ranges(1,0) + 1; ++j) {
605 if (weight(j,i) >= weight_threshold) {
606 cov_ranges(0,1) = i;
607 flag = true;
608 break;
609 }
610 }
611 if (flag == true) {
612 break;
613 }
614 }
615
616 // find upper column bound
617 flag = false;
618 for (Eigen::Index i=weight.cols()-1; i>-1; i--) {
619 for (Eigen::Index j = cov_ranges(0,0); j < cov_ranges(1,0) + 1; ++j) {
620 if (weight(j,i) >= weight_threshold) {
621 cov_ranges(1,1) = i;
622 flag = true;
623 break;
624 }
625 }
626 if (flag == true) {
627 break;
628 }
629 }
630
631 return cov_ranges;
632}
633
634// direction of fft
639
640template<SmoothType smooth_type, typename DerivedA, typename DerivedB>
641void smooth(Eigen::DenseBase<DerivedA> &in, Eigen::DenseBase<DerivedB> &out, int w) {
642 // ensure w is odd
643 if (w % 2 == 0) {
644 w++;
645 }
646
647 Eigen::Index n_pts = in.size();
648 if constexpr (smooth_type==boxcar) {
649 out.head((w - 1) / 2) = in.head((w - 1) / 2);
650 out.tail(n_pts - (w + 1) / 2. + 1) = in.tail(n_pts - (w + 1) / 2. + 1);
651
652 double winv = 1. / w;
653 int wm1d2 = (w - 1) / 2.;
654 int wp1d2 = (w + 1) / 2.;
655
656 for (int i = wm1d2; i <= n_pts - wp1d2; ++i) {
657 out(i) = winv * in.segment(i - wm1d2, w).sum();
658 }
659 }
660
661 else if constexpr (smooth_type==edge_truncate) {
662 double w_inv = 1./w;
663 int w_mid = (w - 1)/2;
664
665 double sum;
666 for (Eigen::Index i=0; i<n_pts; ++i) {
667 sum=0;
668 for (int j=0; j<w; ++j) {
669 int add_index = i + j - w_mid;
670
671 if (add_index < 0) {
672 add_index=0;
673 }
674 else if (add_index > n_pts-1) {
675 add_index = n_pts - 1;
676 }
677 sum += in(add_index);
678 }
679 out(i) = w_inv*sum;
680 }
681 }
682}
683
684template <typename DerivedA, typename DerivedB>
685auto calc_2D_psd(Eigen::DenseBase<DerivedA> &data, Eigen::DenseBase<DerivedB> &y, Eigen::DenseBase<DerivedB> &x,
686 int smooth_window, std::string parallel_policy) {
687
688 Eigen::Index n_rows = data.rows();
689 Eigen::Index n_cols = data.cols();
690
691 Eigen::VectorXd psd, psd_freq;
692
693 double diff_rows = y(1) - y(0);
694 double diff_cols = x(1) - x(0);
695 double rsize = diff_rows * n_rows;
696 double csize = diff_cols * n_cols;
697 double diffq_rows = 1. / rsize;
698 double diffq_cols = 1. / csize;
699
700 // setup input signal data
701 Eigen::MatrixXcd in(n_rows, n_cols);
702 in.real() = data;
703 in.imag().setZero();
704
705 // apply hanning window
706 in.real() = in.real().array()*engine_utils::hanning_window(n_rows, n_cols).array();
707
708 // fftw setup
709 fftw_complex *a;
710 fftw_complex *b;
711 fftw_plan pf;
712
713 a = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
714 b = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
715
716 pf = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_FORWARD, FFTW_ESTIMATE);
717
718 // do fft
719 //in = engine_utils::fft<engine_utils::forward>(in, parallel_policy);
720 in = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
721
722 // free fftw vectors
723 fftw_free(a);
724 fftw_free(b);
725
726 // destroy fftw plan
727 fftw_destroy_plan(pf);
728
729 // get power
730 Eigen::MatrixXd out = diffq_rows*diffq_cols*(in.real().array().pow(2) + in.imag().array().pow(2));
731
732 // make vectors for frequencies
733 Eigen::VectorXd q_rows(n_rows), q_cols(n_cols);
734
735 // shift q_rows
736 Eigen::Index index;
737 Eigen::Index shift = n_rows/2 - 1;
738 for (Eigen::Index i=0; i<n_rows; ++i) {
739 index = i-shift;
740 if (index < 0) {
741 index += n_rows;
742 }
743 q_rows(index) = diffq_rows*(i-(n_rows/2-1));
744 }
745
746 // shift q_cols
747 shift = n_cols/2 - 1;
748 for (Eigen::Index i=0; i<n_cols; ++i) {
749 index = i-shift;
750 if (index < 0) {
751 index += n_cols;
752 }
753 q_cols(index) = diffq_cols*(i-(n_cols/2-1));
754 }
755
756 // remove first row and column of power, qr, qc
757 Eigen::MatrixXd pmfq = out.block(1,1,n_rows-1,n_cols-1);
758
759 // shift rows over by 1
760 for (Eigen::Index i=0; i<n_rows-1; ++i) {
761 q_rows(i) = q_rows(i+1);
762 }
763
764 // shift cols over by 1
765 for (Eigen::Index j=0; j<n_cols-1; ++j) {
766 q_cols(j) = q_cols(j+1);
767 }
768
769 // matrices of frequencies and distances
770 Eigen::MatrixXd qmap(n_rows-1,n_cols-1);
771 Eigen::MatrixXd qsymm(n_rows-1,n_cols-1);
772
773 for (Eigen::Index i=1; i<n_cols; ++i) {
774 for(Eigen::Index j=1; j<n_rows; ++j) {
775 qmap(j-1,i-1) = sqrt(pow(q_rows(j),2) + pow(q_cols(i),2));
776 qsymm(j-1,i-1) = q_rows(j)*q_cols(i);
777 }
778 }
779
780 // find max of n_rows and n_cols and get diff_q
781 Eigen::Index nn;
782 double diff_q;
783 if (n_rows > n_cols) {
784 nn = n_rows/2 + 1;
785 diff_q = diffq_rows;
786 }
787 else {
788 nn = n_cols/2 + 1;
789 diff_q = diffq_cols;
790 }
791
792 // psd frequency vector
793 psd_freq = Eigen::VectorXd::LinSpaced(nn,diff_q*0.5,diff_q*(nn-1 + 0.5));
794
795 // get psd values
796 psd.setZero(nn);
797
798 std::vector<Eigen::Index> psd_vec_in(nn);
799 std::iota(psd_vec_in.begin(), psd_vec_in.end(),0);
800 std::vector<Eigen::Index> psd_vec_out(nn);
801
802 // do the fft over the cols (parallelized for large maps)
803 grppi::map(tula::grppi_utils::dyn_ex(parallel_policy),psd_vec_in,psd_vec_out,[&](auto i) {
804 //for (Eigen::Index i=0; i<nn; ++i) {
805 int count_s = 0;
806 int count_a = 0;
807 double psdarr_s = 0.;
808 double psdarr_a = 0.;
809 for (int j=0; j<n_cols-1; ++j) {
810 for (int k=0; k<n_rows-1; ++k) {
811 if ((int) (qmap(k,j) / diff_q) == i) {
812 if (qsymm(k,j) >= 0.){
813 count_s++;
814 psdarr_s += pmfq(k,j);
815 }
816 else {
817 count_a++;
818 psdarr_a += pmfq(k,j);
819 }
820 }
821 }
822 }
823 if (count_s != 0) {
824 psdarr_s /= count_s;
825 }
826 if (count_a != 0) {
827 psdarr_a /= count_a;
828 }
829 psd(i) = std::min(psdarr_s,psdarr_a);
830 return 0;
831 });
832
833 // smooth the psd
834 Eigen::VectorXd smoothed_psd(nn);
835 engine_utils::smooth<edge_truncate>(psd, smoothed_psd, smooth_window);
836 psd = std::move(smoothed_psd);
837
838 return std::tuple<Eigen::VectorXd, Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd>(psd, psd_freq, pmfq, qmap);
839}
840
841template <typename Derived>
842auto calc_hist(Eigen::DenseBase<Derived> &data, int n_bins) {
843 double min_data = data.minCoeff();
844 double max_data = data.maxCoeff();
845
846 // force the histogram to be symmetric about 0
847 double rg = (abs(min_data) > abs(max_data)) ? abs(min_data) : abs(max_data);
848
849 // set up hist bins
850 Eigen::VectorXd hist_bins = Eigen::VectorXd::LinSpaced(n_bins,-rg,rg);
851 // set size of hist values
852 Eigen::VectorXd hist;
853 hist.setZero(n_bins);
854
855 // loop through bins and count up values
856 for (Eigen::Index j=0; j<n_bins-1; ++j) {
857 hist(j) = ((data.derived().array() >= hist_bins(j)) && (data.derived().array() < hist_bins(j+1))).count();
858 }
859
860 return std::tuple<Eigen::VectorXd, Eigen::VectorXd>(hist, hist_bins);
861}
862
863// shift a vector
864template <typename Derived>
865auto shift_1D(Eigen::DenseBase<Derived> &in, std::vector<Eigen::Index> shift_indices) {
866
867 Eigen::Index n_pts = in.size();
868
869 Derived out(n_pts);
870
871 for (Eigen::Index i=0; i<n_pts; ++i) {
872 Eigen::Index ti = (i+shift_indices[0])%n_pts;
873 Eigen::Index shift_index = (ti < 0) ? n_pts+ti : ti;
874 out(shift_index) = in(i);
875 }
876
877 return out;
878}
879
880// shift a 2D matrix
881template <typename Derived>
882auto shift_2D(Eigen::DenseBase<Derived> &in, std::vector<Eigen::Index> shift_indices) {
883 Eigen::Index n_rows = in.rows();
884 Eigen::Index n_cols = in.cols();
885
886 Derived out(n_rows,n_cols);
887
888 for (Eigen::Index i=0; i<n_cols; ++i) {
889 Eigen::Index ti = (i+shift_indices[1]) % n_cols;
890 Eigen::Index shift_col = (ti < 0) ? n_cols+ti : ti;
891 for (Eigen::Index j=0; j<n_rows; ++j) {
892 Eigen::Index tj = (j+shift_indices[0]) % n_rows;
893 Eigen::Index shift_row = (tj < 0) ? n_rows+tj : tj;
894 out(shift_row,shift_col) = in(j,i);
895 }
896 }
897 return out;
898}
899
900/*
901void abort_(const char * s, ...) {
902 va_list args;
903 va_start(args, s);
904 vfprintf(stderr, s, args);
905 fprintf(stderr, "\n");
906 va_end(args);
907 abort();
908}
909
910void write_png_file(char* file_name, int width, int height) {
911
912 int x, y;
913
914 png_byte color_type;
915 png_byte bit_depth;
916
917 png_structp png_ptr;
918 png_infop info_ptr;
919 int number_of_passes;
920 png_bytep * row_pointers;
921
922 // create file
923 FILE *fp = fopen(file_name, "wb");
924 if (!fp)
925 abort_("[write_png_file] File %s could not be opened for writing", file_name);
926
927 // initialize stuff
928 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
929
930 if (!png_ptr)
931 abort_("[write_png_file] png_create_write_struct failed");
932
933 info_ptr = png_create_info_struct(png_ptr);
934 if (!info_ptr)
935 abort_("[write_png_file] png_create_info_struct failed");
936
937 if (setjmp(png_jmpbuf(png_ptr)))
938 abort_("[write_png_file] Error during init_io");
939
940 png_init_io(png_ptr, fp);
941
942 // write header
943 if (setjmp(png_jmpbuf(png_ptr)))
944 abort_("[write_png_file] Error during writing header");
945
946 png_set_IHDR(png_ptr, info_ptr, width, height,
947 bit_depth, color_type, PNG_INTERLACE_NONE,
948 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
949
950 png_write_info(png_ptr, info_ptr);
951
952 // write bytes
953 if (setjmp(png_jmpbuf(png_ptr)))
954 abort_("[write_png_file] Error during writing bytes");
955
956 png_write_image(png_ptr, row_pointers);
957
958 // end write
959 if (setjmp(png_jmpbuf(png_ptr)))
960 abort_("[write_png_file] Error during end of write");
961
962 png_write_end(png_ptr, NULL);
963
964 // cleanup heap allocation
965 for (y=0; y<height; y++)
966 free(row_pointers[y]);
967 free(row_pointers);
968
969 fclose(fp);
970}*/
971
973public:
974 double x_min;
975 double x_max;
976
977 SplineFunction(Eigen::VectorXd const &x_vec,
978 Eigen::VectorXd const &y_vec)
979 : x_min(x_vec.minCoeff()),
980 x_max(x_vec.maxCoeff()),
981 // spline fitting here. X values are scaled down to [0, 1] for this.
982 spline_(Eigen::SplineFitting<Eigen::Spline<double, 1>>::Interpolate(
983 y_vec.transpose(),
984 // no more than cubic spline, but accept short vectors.
985 std::min<int>(x_vec.rows() - 1, 3),
986 scaled_values(x_vec))) {}
987
988 SplineFunction() = default;
989
990 void interpolate(Eigen::VectorXd const &x_vec,
991 Eigen::VectorXd const &y_vec) {
992 x_min = x_vec.minCoeff();
993 x_max = x_vec.maxCoeff();
994 // spline fitting here. X values are scaled down to [0, 1] for this.
995 spline_ = Eigen::SplineFitting<Eigen::Spline<double, 1>>::Interpolate(
996 y_vec.transpose(),
997 // no more than cubic spline, but accept short vectors.
998 std::min<int>(x_vec.rows() - 1, 3),
999 scaled_values(x_vec));
1000 }
1001
1002 double operator()(double x) const {
1003 // x values need to be scaled down in extraction as well.
1004 return spline_(scaled_value(x))(0);
1005 }
1006
1007private:
1008 // helpers to scale x values down to [0, 1]
1009 double scaled_value(double x) const {
1010 return (x - x_min) / (x_max - x_min);
1011 }
1012
1013 Eigen::RowVectorXd scaled_values(Eigen::VectorXd const &x_vec) const {
1014 return x_vec.unaryExpr([this](double x) { return scaled_value(x); }).transpose();
1015 }
1016
1017 // spline of one-dimensional points.
1018 Eigen::Spline<double, 1> spline_;
1019};
1020
1021template <typename Derived>
1022void fix_periodic_boundary(Eigen::DenseBase<Derived>&data, double low,
1023 double high, double upper_limit) {
1024
1025 Eigen::Index n_pts = data.size();
1026 double max = data.maxCoeff();
1027 double min = data.minCoeff();
1028 if (max > high && min < low) {
1029 for (Eigen::Index i=0; i<n_pts; ++i) {
1030 if (data(i) < low) {
1031 data(i) += upper_limit;
1032 }
1033 }
1034 }
1035}
1036
1037/*template <class map_class_t, class map_fitter_t>
1038void calc_gaussian_transfer_func(map_class_t &mb, map_fitter_t &map_fitter, int map_index) {
1039 // x and y spacing should be equal
1040 diff_rows = abs(mb.rows_tan_vec(1) - mb.rows_tan_vec(0));
1041 diff_cols = abs(mb.cols_tan_vec(1) - mb.cols_tan_vec(0));
1042
1043 // collect what we need
1044 Eigen::MatrixXd temp_kernel = mb.kernel[map_index];
1045
1046 double init_row = -99;
1047 double init_col = -99;
1048
1049 // carry out fit to kernel to get centroid
1050 auto [map_params, map_perror, good_fit] =
1051 map_fitter.fit_to_gaussian<engine_utils::mapFitter::pointing>(mb.kernel[map_index], mb.weight[map_index],
1052 init_fwhm, init_row, init_col);
1053
1054 // if fit failed, give up
1055 if (!good_fit) {
1056 logger->error("fit to kernel map failed. try setting a small fitting_region_arcsec value.");
1057 std::exit(EXIT_FAILURE);
1058 }
1059
1060 // rescale parameters to on-sky units
1061 map_params(1) = mb.pixel_size_rad*(map_params(1) - (n_cols)/2);
1062 map_params(2) = mb.pixel_size_rad*(map_params(2) - (n_rows)/2);
1063
1064 Eigen::Index shift_row = -std::round(map_params(2)/diff_rows);
1065 Eigen::Index shift_col = -std::round(map_params(1)/diff_cols);
1066
1067 std::vector<Eigen::Index> shift_indices = {shift_row,shift_col};
1068 temp_kernel = engine_utils::shift_2D(temp_kernel, shift_indices);
1069
1070 // calculate distance
1071 Eigen::MatrixXd dist(n_rows,n_cols);
1072 for (Eigen::Index i=0; i<n_cols; ++i) {
1073 for (Eigen::Index j=0; j<n_rows; ++j) {
1074 dist(j,i) = sqrt(pow(mb.rows_tan_vec(j),2)+pow(mb.cols_tan_vec(i),2));
1075 }
1076 }
1077
1078 // pixel closet to tangent point
1079 Eigen::Index row_index, col_index;
1080 auto min_dist = dist.minCoeff(&row_index,&col_index);
1081
1082 // create new bins based on diff_rows
1083 int n_bins = mb.rows_tan_vec(n_rows-1)/diff_rows;
1084 Eigen::VectorXd bin_low = Eigen::VectorXd::LinSpaced(n_bins,0,n_bins-1)*diff_rows;
1085
1086 Eigen::VectorXd kernel_interp(n_bins-1);
1087 kernel_interp.setZero();
1088 Eigen::VectorXd dist_interp(n_bins-1);
1089 dist_interp.setZero();
1090
1091 // radial averages
1092 for (Eigen::Index i=0; i<n_bins-1; ++i) {
1093 int c = 0;
1094 for (Eigen::Index j=0; j<n_cols; ++j) {
1095 for (Eigen::Index k=0; k<n_rows; ++k) {
1096 if (dist(k,j) >= bin_low(i) && dist(k,j) < bin_low(i+1)){
1097 c++;
1098 kernel_interp(i) += temp_kernel(k,j);
1099 dist_interp(i) += dist(k,j);
1100 }
1101 }
1102 }
1103 kernel_interp(i) /= c;
1104 dist_interp(i) /= c;
1105 }
1106
1107 // now spline interpolate to generate new template array
1108 filter_template.resize(n_rows,n_cols);
1109
1110 // create spline function
1111 engine_utils::SplineFunction s(dist_interp, kernel_interp);
1112
1113 // carry out the interpolation
1114 for (Eigen::Index i=0; i<n_cols; ++i) {
1115 for (Eigen::Index j=0; j<n_rows; ++j) {
1116 Eigen::Index tj = (j-row_index)%n_rows;
1117 Eigen::Index ti = (i-col_index)%n_cols;
1118 Eigen::Index shiftj = (tj < 0) ? n_rows+tj : tj;
1119 Eigen::Index shifti = (ti < 0) ? n_cols+ti : ti;
1120
1121 // if within limits
1122 if (dist(j,i) <= s.x_max && dist(j,i) >= s.x_min) {
1123 filter_template(shiftj,shifti) = s(dist(j,i));
1124 }
1125 // if above x limit
1126 else if (dist(j,i) > s.x_max) {
1127 filter_template(shiftj,shifti) = kernel_interp(kernel_interp.size()-1);
1128 }
1129 // if below x limit
1130 else if (dist(j,i) < s.x_min) {
1131 filter_template(shiftj,shifti) = kernel_interp(0);
1132 }
1133 }
1134 }
1135}*/
1136
1138inline auto now() { return std::chrono::high_resolution_clock::now(); }
1139
1141inline auto elapsed_since(
1142 const std::chrono::time_point<std::chrono::high_resolution_clock> &t0) {
1143 return now() - t0;
1144}
1145
1148 scoped_timeit(std::string_view msg_, double *elapsed_msec_ = nullptr)
1149 : msg(msg_), elapsed_msec(elapsed_msec_) {
1150 logger->info("**timeit** {}", msg);
1151 }
1153 auto t = elapsed_since(t0);
1154 constexpr auto s_to_ms = 1e3;
1155 if (this->elapsed_msec != nullptr) {
1156 *(this->elapsed_msec) =
1157 std::chrono::duration_cast<std::chrono::duration<double>>(t)
1158 .count() *
1159 s_to_ms;
1160 }
1161 logger->info("**timeit** {} finished in {}", msg, t);
1162 }
1163 scoped_timeit(const scoped_timeit &) = delete;
1165 auto operator=(const scoped_timeit &) -> scoped_timeit & = delete;
1166 auto operator=(scoped_timeit &&) -> scoped_timeit & = delete;
1167
1168 std::chrono::time_point<std::chrono::high_resolution_clock> t0{now()};
1169 std::string_view msg;
1170 double *elapsed_msec{nullptr};
1171 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
1172
1173};
1174
1175// Function to compute numerical derivatives using central difference
1176template <typename Derived>
1177Eigen::VectorXd compute_numerical_derivative(const Eigen::DenseBase<Derived>& times, const Eigen::DenseBase<Derived>& values) {
1178 if (times.size() != values.size()) {
1179 throw std::invalid_argument("Time vector and value vector must have the same number of elements.");
1180 }
1181
1182 Eigen::VectorXd derivatives(values.size());
1183 double dt;
1184
1185 // Calculate derivatives
1186 for (int i = 0; i < values.size(); ++i) {
1187 if (i == 0) { // forward difference for the first point
1188 dt = times(i + 1) - times(i);
1189 derivatives(i) = (values(i + 1) - values(i)) / dt;
1190 } else if (i == values.size() - 1) { // backward difference for the last point
1191 dt = times(i) - times(i - 1);
1192 derivatives(i) = (values(i) - values(i - 1)) / dt;
1193 } else { // central difference for other points
1194 dt = times(i + 1) - times(i - 1);
1195 derivatives(i) = (values(i + 1) - values(i - 1)) / (2 * dt);
1196 }
1197 }
1198
1199 return derivatives;
1200}
1201
1202static const bool is_point_in_box(double a, double b, double x, double y, double theta) {
1203
1204 // rotate point (a, b) by -theta
1205 double xp = a * std::cos(theta) + b * std::sin(theta);
1206 double yp = -a * std::sin(theta) + b * std::cos(theta);
1207
1208 // Check horizontal bounds
1209 if (-x / 2 <= xp && xp <= x / 2) {
1210 // Check vertical bounds
1211 if (-y / 2 <= yp && yp <= y / 2) {
1212 return true;
1213 }
1214 }
1215 return false;
1216}
1217
1218} //namespace engine_utils
Definition utils.h:972
double x_min
Definition utils.h:974
double x_max
Definition utils.h:975
Eigen::RowVectorXd scaled_values(Eigen::VectorXd const &x_vec) const
Definition utils.h:1013
double operator()(double x) const
Definition utils.h:1002
double scaled_value(double x) const
Definition utils.h:1009
Eigen::Spline< double, 1 > spline_
Definition utils.h:1018
void interpolate(Eigen::VectorXd const &x_vec, Eigen::VectorXd const &y_vec)
Definition utils.h:990
SplineFunction(Eigen::VectorXd const &x_vec, Eigen::VectorXd const &y_vec)
Definition utils.h:977
#define c_m_s
Definition constants.h:12
#define kB_J_K
Definition constants.h:15
constexpr auto pi
Definition constants.h:6
#define T_cmb_K
Definition constants.h:18
#define h_J_s
Definition constants.h:9
Definition fitting.h:11
std::vector< std::tuple< double, int > > sorter(Eigen::DenseBase< Derived > &vec)
Definition utils.h:318
auto shift_2D(Eigen::DenseBase< Derived > &in, std::vector< Eigen::Index > shift_indices)
Definition utils.h:882
static double unix_to_modified_julian_date(double unix_time)
Definition utils.h:170
void fix_periodic_boundary(Eigen::DenseBase< Derived > &data, double low, double high, double upper_limit)
Definition utils.h:1022
static const double mJy_beam_to_uK(const double flux_mjy_beam, const double freq_Hz, const double fwhm)
Definition utils.h:77
static const bool is_point_in_box(double a, double b, double x, double y, double theta)
Definition utils.h:1202
auto now()
Return the current time.
Definition utils.h:1138
auto calc_rms(Eigen::DenseBase< DerivedA > &data)
Definition utils.h:389
double calc_mad(Eigen::DenseBase< Derived > &data)
Definition utils.h:434
auto calc_hist(Eigen::DenseBase< Derived > &data, int n_bins)
Definition utils.h:842
void utc_to_unix(Eigen::DenseBase< DerivedA > &tel_utc, Eigen::DenseBase< DerivedB > &ut_date)
Definition utils.h:113
double find_weight_threshold(Eigen::DenseBase< Derived > &weight, double cov)
Definition utils.h:490
auto shift_1D(Eigen::DenseBase< Derived > &in, std::vector< Eigen::Index > shift_indices)
Definition utils.h:865
static const int get_phys_memory()
Definition utils.h:56
auto elapsed_since(const std::chrono::time_point< std::chrono::high_resolution_clock > &t0)
Return the time elapsed since given time.
Definition utils.h:1141
SmoothType
Definition utils.h:635
@ edge_truncate
Definition utils.h:637
@ boxcar
Definition utils.h:636
auto set_cov_cov_ranges(const Eigen::DenseBase< Derived > &weight, const double weight_threshold)
Definition utils.h:563
void for_each_in_tuple(const std::tuple< Ts... > &tuple, F func, std::index_sequence< Is... >)
Definition utils.h:307
static long long modified_julian_date_to_unix(double jd)
Definition utils.h:157
Eigen::VectorXd compute_numerical_derivative(const Eigen::DenseBase< Derived > &times, const Eigen::DenseBase< Derived > &values)
Definition utils.h:1177
auto fft2(Eigen::DenseBase< Derived > &in, fftw_plan_t &plan, fftw_complex *a, fftw_complex *b)
Definition utils.h:270
static const std::string current_date_time()
Definition utils.h:91
static double pivot_select(std::vector< double > input, int index)
Definition utils.h:463
auto fft(Eigen::DenseBase< Derived > &in, std::string parallel_policy)
Definition utils.h:205
static auto hanning_window(Eigen::Index n_rows, Eigen::Index n_cols)
Definition utils.h:442
static const int parseLine(char *line)
Definition utils.h:44
static const std::string unix_to_utc(double &t)
Definition utils.h:102
auto calc_2D_psd(Eigen::DenseBase< DerivedA > &data, Eigen::DenseBase< DerivedB > &y, Eigen::DenseBase< DerivedB > &x, int smooth_window, std::string parallel_policy)
Definition utils.h:685
auto make_meshgrid(const Eigen::DenseBase< Derived > &x, const Eigen::DenseBase< Derived > &y)
Definition utils.h:177
FFTDirection
Definition utils.h:198
@ forward
Definition utils.h:199
@ inverse
Definition utils.h:200
static const double planck_nu(const double freq_Hz, const double T_K)
Definition utils.h:72
void smooth(Eigen::DenseBase< DerivedA > &in, Eigen::DenseBase< DerivedB > &out, int w)
Definition utils.h:641
auto calc_std_dev(Eigen::DenseBase< DerivedA > &data)
Definition utils.h:336
Definition timestream.h:59
An RAII class to report the lifetime of itself.
Definition utils.h:1147
double * elapsed_msec
Definition utils.h:1170
std::chrono::time_point< std::chrono::high_resolution_clock > t0
Definition utils.h:1168
~scoped_timeit()
Definition utils.h:1152
auto operator=(scoped_timeit &&) -> scoped_timeit &=delete
scoped_timeit(const scoped_timeit &)=delete
scoped_timeit(scoped_timeit &&)=delete
std::shared_ptr< spdlog::logger > logger
Definition utils.h:1171
std::string_view msg
Definition utils.h:1169
auto operator=(const scoped_timeit &) -> scoped_timeit &=delete
scoped_timeit(std::string_view msg_, double *elapsed_msec_=nullptr)
Definition utils.h:1148