Citlali
Loading...
Searching...
No Matches
wiener_filter.h
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4
5#include <boost/math/special_functions/bessel.hpp>
6
7#include <Eigen/Core>
8#include <unsupported/Eigen/CXX11/Tensor>
9#include <unsupported/Eigen/Splines>
10
11#include <tula/algorithm/mlinterp/mlinterp.hpp>
12#include <tula/logging.h>
13
16
20
21namespace mapmaking {
22
24public:
25 // get logger
26 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
27
28 // filter template
30 // normalize filtered map errors
32 // uniform weighting
34 // lowpass only
36
37 // number of loops in denom calc
39 // maximum number of loops for denom calc
40 int max_loops = 500;
41 // lower limit to zero out denom values
42 double denom_limit = 1.e-4;
43 // psd limit
44 double psd_lim = 1.e-4;
45
46 // guess fwhm for kernel map filtering
47 double init_fwhm;
48
49 // fwhms for gaussian template
50 std::map<std::string, double> template_fwhm_rad;
51
52 // size of maps
54
55 // size of pixel in each dimension
57
58 // parallelization for ffts
59 std::string parallel_policy;
60
61 // matrices for main calculations from each function
62 Eigen::MatrixXd rr, vvq, denom, nume;
63 // temporarily holds the filtered map
64 Eigen::MatrixXd filtered_map;
65 // filter template
66 Eigen::MatrixXd filter_template;
67
68 // declare fitter class
70
71 // get config file
72 template <typename config_t>
73 void get_config(config_t &, std::vector<std::vector<std::string>> &, std::vector<std::vector<std::string>> &);
74
75 // make a symmetric Gaussian to use as a template
76 template<class MB>
77 void make_gaussian_template(MB &mb, const double);
78
79 // make an Airy pattern to use as a template
80 template<class MB>
81 void make_airy_template(MB &mb, const double);
82
83 // use a symmetric version of the kernel as a template
84 template<class MB, class CD>
85 void make_kernel_template(MB &mb, const int, CD &);
86
87 // main function to determine what template to make
88 template<class MB, class CD>
89 void make_template(MB &, CD &c, const double, const int);
90
91 // calculate standard deviations of each pixel
92 template<class MB>
93 void calc_rr(MB &, const int);
94
95 // calculate normalized noise psd
96 template <class MB>
97 void calc_vvq(MB &, const int);
98
99 // calculate the numerator
100 void calc_numerator();
101
102 // calculate the denominator
103 void calc_denominator();
104
105 // run the filter on the signal, weight, and kernel maps
106 template<class MB>
107 void run_filter(MB &, const int);
108
109 // simple convolution with template
110 void run_convolve();
111
112 // test destriper
113 void destripe(double);
114
115 // filter a map
116 template<class MB>
117 void filter_maps(MB &, const int);
118
119 // filter the noise maps
120 template<class MB>
121 void filter_noise(MB &mb, const int, const int);
122};
123
124// get config file
125template <typename config_t>
126void WienerFilter::get_config(config_t &config, std::vector<std::vector<std::string>> &missing_keys,
127 std::vector<std::vector<std::string>> &invalid_keys) {
128
129 // for array names
130 engine_utils::toltecIO toltec_io;
131
132 // get filter type
133 get_config_value(config, filter_type, missing_keys, invalid_keys,
134 std::tuple{"post_processing","map_filtering","type"},{"wiener_filter","convolve","destripe"});
135 // get template type
136 get_config_value(config, template_type, missing_keys, invalid_keys,
137 std::tuple{"wiener_filter","template_type"},{"kernel","gaussian","airy","highpass"});
138 // run lowpass only?
139 get_config_value(config, run_lowpass, missing_keys, invalid_keys,
140 std::tuple{"wiener_filter","lowpass_only"});
141 // re-normalize weight maps?
142 get_config_value(config, normalize_error, missing_keys, invalid_keys,
143 std::tuple{"post_processing","map_filtering","normalize_errors"});
144
145 // gaussian or airy template fwhms
146 if (template_type=="gaussian" || template_type=="airy") {
147 // loop through array names and get fwhms
148 for (auto const& [arr_index, arr_name] : toltec_io.array_name_map) {
149 get_config_value(config, template_fwhm_rad[arr_name], missing_keys, invalid_keys,
150 std::tuple{"wiener_filter","template_fwhm_arcsec",arr_name});
151 }
152 // convert to radians
153 for (auto const& pair : template_fwhm_rad) {
154 template_fwhm_rad[pair.first] = template_fwhm_rad[pair.first]*ASEC_TO_RAD;
155 }
156 }
157}
158
159template<class MB>
160void WienerFilter::make_gaussian_template(MB &mb, const double template_fwhm_rad) {
161 // distance from tangent point
162 Eigen::MatrixXd dist(n_rows,n_cols);
163
164 // calculate distance
165 for (Eigen::Index i=0; i<n_rows; ++i) {
166 for (Eigen::Index j=0; j<n_cols; ++j) {
167 dist(i,j) = sqrt(pow(mb.rows_tan_vec(i)+0.5*mb.pixel_size_rad,2) + pow(mb.cols_tan_vec(j)+0.5*mb.pixel_size_rad,2));
168 }
169 }
170
171 // to hold minimum distance
172 Eigen::Index row_index, col_index;
173
174 // minimum distance
175 double min_dist = dist.minCoeff(&row_index,&col_index);
176 // standard deviation
177 double sigma = template_fwhm_rad*FWHM_TO_STD;
178
179 // shift indices
180 std::vector<Eigen::Index> shift_indices = {-row_index, -col_index};
181
182 // calculate template
183 filter_template = exp(-0.5 * pow(dist.array() / sigma, 2.));
184 // shift template
186}
187
188template<class MB>
189void WienerFilter::make_airy_template(MB &mb, const double template_fwhm_rad) {
190 // distance from tangent point
191 Eigen::MatrixXd dist(n_rows,n_cols);
192
193 // calculate distance
194 for (Eigen::Index i=0; i<n_rows; ++i) {
195 for (Eigen::Index j=0; j<n_cols; ++j) {
196 dist(i,j) = sqrt(pow(mb.rows_tan_vec(i)+0.5*mb.pixel_size_rad,2) + pow(mb.cols_tan_vec(j)+0.5*mb.pixel_size_rad,2));
197 }
198 }
199
200 // to hold minimum distance
201 Eigen::Index row_index, col_index;
202
203 // minimum distance
204 double min_dist = dist.minCoeff(&row_index,&col_index);
205
206 // shift indices
207 std::vector<Eigen::Index> shift_indices = {-row_index, -col_index};
208
209 // calculate template
210 double factor = pi*(1.028/template_fwhm_rad);
211
212 // resize template
214
215 // populate template
216 for (Eigen::Index i=0; i<n_rows; ++i) {
217 for (Eigen::Index j=0; j<n_cols; ++j) {
218 if (dist(i,j)!=0) {
219 filter_template(i,j) = pow(2*boost::math::cyl_bessel_j(1,factor*dist(i,j))/(factor*dist(i,j)),2);
220 }
221 else {
222 filter_template(i,j) = 1;
223 }
224 }
225 }
226
227 // shift template
229}
230
231template<class MB, class CD>
232void WienerFilter::make_kernel_template(MB &mb, const int map_index, CD &calib_data) {
233 // collect what we need
234 Eigen::MatrixXd temp_kernel = mb.kernel[map_index];
235
236 double init_row = -99;
237 double init_col = -99;
238
239 // carry out fit to kernel to get centroid
240 auto [map_params, map_perror, good_fit] =
241 map_fitter.fit_to_gaussian<engine_utils::mapFitter::pointing>(mb.kernel[map_index], mb.weight[map_index],
242 init_fwhm, init_row, init_col);
243
244 // if fit failed, give up
245 if (!good_fit) {
246 logger->error("fit to kernel map failed. try setting a small fitting_region_arcsec value.");
247 std::exit(EXIT_FAILURE);
248 }
249
250 // rescale parameters to on-sky units
251 map_params(1) = mb.pixel_size_rad*(map_params(1) - (n_cols)/2);
252 map_params(2) = mb.pixel_size_rad*(map_params(2) - (n_rows)/2);
253
254 Eigen::Index shift_row = -std::round(map_params(2)/diff_rows);
255 Eigen::Index shift_col = -std::round(map_params(1)/diff_cols);
256
257 std::vector<Eigen::Index> shift_indices = {shift_row,shift_col};
258 temp_kernel = engine_utils::shift_2D(temp_kernel, shift_indices);
259
260 // calculate distance
261 Eigen::MatrixXd dist(n_rows,n_cols);
262 // calculate distance
263 for (Eigen::Index i=0; i<n_rows; ++i) {
264 for (Eigen::Index j=0; j<n_cols; ++j) {
265 dist(i,j) = sqrt(pow(mb.rows_tan_vec(i)+0.5*mb.pixel_size_rad,2) + pow(mb.cols_tan_vec(j)+0.5*mb.pixel_size_rad,2));
266 }
267 }
268
269 // pixel closet to tangent point
270 Eigen::Index row_index, col_index;
271 auto min_dist = dist.minCoeff(&row_index,&col_index);
272
273 // create new bins based on diff_rows
274 int n_bins = mb.rows_tan_vec(n_rows-1)/diff_rows;
275 Eigen::VectorXd bin_low = Eigen::VectorXd::LinSpaced(n_bins,0,n_bins-1)*diff_rows;
276
277 Eigen::VectorXd kernel_interp(n_bins-1);
278 kernel_interp.setZero();
279 Eigen::VectorXd dist_interp(n_bins-1);
280 dist_interp.setZero();
281
282 // radial averages
283 for (Eigen::Index i=0; i<n_bins-1; ++i) {
284 int c = 0;
285 for (Eigen::Index j=0; j<n_cols; ++j) {
286 for (Eigen::Index k=0; k<n_rows; ++k) {
287 if (dist(k,j) >= bin_low(i) && dist(k,j) < bin_low(i+1)){
288 c++;
289 kernel_interp(i) += temp_kernel(k,j);
290 dist_interp(i) += dist(k,j);
291 }
292 }
293 }
294 kernel_interp(i) /= c;
295 dist_interp(i) /= c;
296 }
297
298 // now spline interpolate to generate new template array
300
301 // create spline function
302 engine_utils::SplineFunction s(dist_interp, kernel_interp);
303
304 // carry out the interpolation
305 for (Eigen::Index i=0; i<n_cols; ++i) {
306 Eigen::Index ti = (i-col_index)%n_cols;
307 Eigen::Index shifti = (ti < 0) ? n_cols+ti : ti;
308 for (Eigen::Index j=0; j<n_rows; ++j) {
309 Eigen::Index tj = (j-row_index)%n_rows;
310 Eigen::Index shiftj = (tj < 0) ? n_rows+tj : tj;
311
312 // if within limits
313 if (dist(j,i) <= s.x_max && dist(j,i) >= s.x_min) {
314 filter_template(shiftj,shifti) = s(dist(j,i));
315 }
316 // if above x limit
317 else if (dist(j,i) > s.x_max) {
318 filter_template(shiftj,shifti) = kernel_interp(kernel_interp.size()-1);
319 }
320 // if below x limit
321 else if (dist(j,i) < s.x_min) {
322 filter_template(shiftj,shifti) = kernel_interp(0);
323 }
324 }
325 }
326}
327
328// calculate standard deviations of each pixel
329template<class MB>
330void WienerFilter::calc_rr(MB &mb, const int map_index) {
331 if (uniform_weight) {
332 rr = Eigen::MatrixXd::Ones(n_rows,n_cols);
333 }
334 else {
335 rr = sqrt(mb.weight[map_index].array());
336 }
337}
338
339template <class MB>
340void WienerFilter::calc_vvq(MB &mb, const int map_index) {
341 // resize psd_q
342 Eigen::MatrixXd psd_q(n_rows,n_cols);
343
344 // set constant if lowpass only
345 if (run_lowpass) {
346 psd_q.setOnes();
347 }
348 else {
349 // psd and psd freq vectors
350 Eigen::VectorXd psd = mb.noise_psds[map_index];
351 Eigen::VectorXd psd_freq = mb.noise_psd_freqs[map_index];
352
353 // size of psd and psd freq vectors
354 Eigen::Index n_psd = psd.size();
355
356 // modify the psd array to take out lowpassing and highpassing
357 Eigen::Index max_psd_index;
358 double max_psd = psd.maxCoeff(&max_psd_index);
359 double psd_freq_break = 0.;
360 double psd_break = 0.;
361
362 for (Eigen::Index i=0; i<n_psd; ++i) {
363 if (psd(i)/max_psd < psd_lim) {
364 psd_freq_break = psd_freq(i);
365 break;
366 }
367 }
368
369 // number of frequency samples below lowpass break
370 int count = (psd_freq.array() <= 0.8*psd_freq_break).count();
371
372 // flatten the response above the lowpass break
373 if (count > 0) {
374 for (Eigen::Index i=0; i<n_psd; ++i) {
375 if (psd_freq_break > 0) {
376 if (psd_freq(i) <= 0.8*psd_freq_break) {
377 psd_break = psd(i);
378 }
379
380 if (psd_freq(i) > 0.8*psd_freq_break) {
381 psd(i) = psd_break;
382 }
383 }
384 }
385 }
386
387 // flatten highpass response if present
388 if (max_psd_index > 0) {
389 psd.head(max_psd_index).setConstant(max_psd);
390 }
391
392 // get spacing
393 double diff_qr = 1. / (n_rows * diff_rows);
394 double diff_qc = 1. / (n_cols * diff_cols);
395
396 Eigen::MatrixXd q_map(n_rows,n_cols);
397
398 // q_row
399 Eigen::VectorXd q_row = Eigen::VectorXd::LinSpaced(n_rows, -n_rows / 2, n_rows / 2) * diff_qr;
400 // q_col
401 Eigen::VectorXd q_col = Eigen::VectorXd::LinSpaced(n_cols, -n_cols / 2, n_cols / 2) * diff_qc;
402
403 // shift q_row
404 std::vector<Eigen::Index> shift_1 = {-n_rows/2};
405 engine_utils::shift_1D(q_row, shift_1);
406 // shift q_col
407 std::vector<Eigen::Index> shift_2 = {n_cols/2};
408 engine_utils::shift_1D(q_col, shift_2);
409
410 for (Eigen::Index i=0; i<n_rows; ++i) {
411 for (Eigen::Index j=0; j<n_cols; ++j) {
412 q_map(i,j) = sqrt(pow(q_row(i),2)+pow(q_col(j),2));
413 }
414 }
415
416 // set psd q to zero
417 psd_q.setZero();
418
419 Eigen::Matrix<Eigen::Index, 1, 1> n_psd_matrix;
420 n_psd_matrix << n_psd;
421
422 // interpolate onto psd_q
423 Eigen::Index interp_pts = 1;
424 for (Eigen::Index i=0; i<n_cols; ++i) {
425 for (Eigen::Index j=0; j<n_rows; ++j) {
426 if ((q_map(j,i) <= psd_freq(psd_freq.size() - 1)) && (q_map(j,i) >= psd_freq(0))) {
427 mlinterp::interp<mlinterp::rnatord>(n_psd_matrix.data(), interp_pts,
428 psd.data(), psd_q.data() + n_rows * i + j,
429 psd_freq.data(), q_map.data() + n_rows * i + j);
430 }
431 else if (q_map(j,i) > psd_freq(n_psd - 1)) {
432 psd_q(j,i) = psd(n_psd- 1);
433 }
434 else if (q_map(j,i) < psd_freq(0)) {
435 psd_q(j,i) = psd(0);
436 }
437 }
438 }
439
440 // find the minimum value of psd
441 auto psd_min = psd.minCoeff();
442
443 for (Eigen::Index i=0; i<n_rows; ++i) {
444 for (Eigen::Index j=0; j<n_cols; ++j) {
445 if (psd_q(i,j) < psd_min) {
446 psd_q(i,j) = psd_min;
447 }
448 }
449 }
450 //psd_q = psd_q.array().max(psd_min).matrix();
451 }
452
453 // normalize the power spectrum psd_q and place into vvq
454 vvq = psd_q/psd_q.sum();
455}
456
458 // set up fftw
459 fftw_complex *a;
460 fftw_complex *b;
461 fftw_plan pf, pr;
462
463 // allocate space for 2d ffts
464 a = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
465 b = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
466
467 // fftw plans
468 pf = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_FORWARD, FFTW_ESTIMATE);
469 pr = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_BACKWARD, FFTW_ESTIMATE);
470
471 // set up inputs and outputs
472 Eigen::MatrixXcd in(n_rows,n_cols), out(n_rows,n_cols);
473 // d x RR
474 in.real() = rr.array() * filtered_map.array();
475 in.imag().setZero();
476
477 // fft(d x RR)
478 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
479
480 // fft(d x RR) x 1/VV
481 in.real() = out.real().array() / vvq.array();
482 in.imag() = out.imag().array() / vvq.array();
483
484 // ifft(fft(d x RR) x 1/VV)
485 out = engine_utils::fft2<engine_utils::inverse>(in, pr, a, b);
486
487 // Q = ifft(fft(d x RR) x 1/VV) x RR
488 in.real() = out.real().array() * rr.array();
489 in.imag().setZero();
490
491 // fft(Q)
492 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
493
494 // copy of fft(Q)
495 Eigen::MatrixXcd Q = out;
496
497 // f(x)
498 in.real() = filter_template;
499 in.imag().setZero();
500
501 // fft(f(x)) (re-use out)
502 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
503
504 // fft(f(x)) x fft(Q) (convolution)
505 in.real() = out.real().array() * Q.real().array() + out.imag().array() * Q.imag().array();
506 in.imag() = -out.imag().array() * Q.real().array() + out.real().array() * Q.imag().array();
507
508 // ifft(fft(f(x)) x fft(Q))
509 out = engine_utils::fft2<engine_utils::inverse>(in, pr, a, b);
510
511 // populate numerator with real(ifft(fft(f(x)) x fft(Q)))
512 nume = out.real();
513
514 // free fftw vectors
515 fftw_free(a);
516 fftw_free(b);
517
518 // destroy fftw plans
519 fftw_destroy_plan(pf);
520 fftw_destroy_plan(pr);
521}
522
524 // set up fftw
525 fftw_complex *a;
526 fftw_complex *b;
527 fftw_plan pf, pr;
528
529 // allocate space for 2d ffts
530 a = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
531 b = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
532
533 // fftw plans
534 pf = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_FORWARD, FFTW_ESTIMATE);
535 pr = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_BACKWARD, FFTW_ESTIMATE);
536
537 // resize denominator
538 denom.setZero(n_rows,n_cols);
539
540 // inputs and outputs to ffts
541 Eigen::MatrixXcd in(n_rows,n_cols), out(n_rows,n_cols);
542
543 // using uniform weights only
544 if (uniform_weight) {
545 in.real() = filter_template;
546 in.imag().setZero();
547
548 // fft(f(x))
549 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
550
551 // set denominator = abs(fft(f(x))/VV
552 denom.setConstant(((out.real().array() * out.real().array() + out.imag().array() * out.imag().array()) / vvq.array()).sum());
553 }
554 else {
555 // initialize denominator
556 denom.setZero();
557
558 // 1/VV
559 in.real() = pow(vvq.array(),-1);
560 in.imag().setZero();
561
562 // Z = ifft(1/VV)
563 out = engine_utils::fft2<engine_utils::inverse>(in, pr, a, b);
564
565 // flattened real(Z) array
566 Eigen::VectorXd Z(n_rows * n_cols);
567
568 // real(Z). do loop to make sure colmajor is preserved
569 for (Eigen::Index i=0; i<n_cols; ++i) {
570 for (Eigen::Index j=0; j<n_rows;++j) {
571 int ii = n_rows*i+j;
572 Z(ii) = (out.real()(j,i));
573 }
574 }
575
576 // sort absolute values of Z in ascending order
577 Eigen::VectorXd Z_abs = Z.array().abs();
578 auto Z_indices_sorted = engine_utils::sorter(Z_abs);
579
580 // number of iterations for convergence
581 n_loops = n_rows * n_cols / 100;
582
583 // flag for convergence
584 bool done = false;
585
586 tula::logging::progressbar pb(
587 [&](const auto &msg) { logger->info("{}", msg); }, 90,
588 "calculating denom");
589
590 // loop through cols and rows
591 for (Eigen::Index k=0; k<n_cols; ++k) {
592 for (Eigen::Index l=0; l<n_rows; ++l) {
593 if (!done) {
594 // inputs and outputs
595 Eigen::MatrixXcd in(n_rows,n_cols), out(n_rows,n_cols);
596
597 // current element in flattened 1d vector
598 int kk = n_rows * k + l;
599 // get index in reverse order to get largest abs(ifft(1/VV))
600 auto shift_index = std::get<1>(Z_indices_sorted[n_rows * n_cols - kk - 1]);
601
602 // indices to shift by
603 std::vector<Eigen::Index> shift_indices = {static_cast<Eigen::Index>(-shift_index % n_rows),
604 static_cast<Eigen::Index>(-shift_index / n_rows)};
605
606 // f(x) x f(x-x_d)
607 Eigen::MatrixXd in_prod = filter_template.array() * engine_utils::shift_2D(filter_template, shift_indices).array();
608
609 // populate matrices for fft
610 in.real() = in_prod;
611 in.imag().setZero();
612
613 // fft(f(x) x f(x-x_d))
614 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
615
616 // copy of fft(f(x) x f(x-x_d))
617 Eigen::MatrixXcd ffdq = out;
618
619 // R(x) x R(x-x_d)
620 in_prod = rr.array() * engine_utils::shift_2D(rr, shift_indices).array();
621
622 // populate matrices for fft
623 in.real() = in_prod;
624 in.imag().setZero();
625
626 // fft(R(x) x R(x-x_d))
627 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
628
629 // fft(f(x) x f(x-x_d)) x fft(R(x) x R(x-x_d))
630 in.real() = ffdq.real().array() * out.real().array() + ffdq.imag().array() * out.imag().array();
631 in.imag() = -ffdq.imag().array() * out.real().array() + ffdq.real().array() * out.imag().array();
632
633 // G = ifft(fft(f(x) x f(x-x_d)) x fft(R(x) x R(x-x_d)))
634 out = engine_utils::fft2<engine_utils::inverse>(in, pr, a, b);
635
636 // Z(x_d) x G/n_pixels
637 Eigen::MatrixXd delta_denom = Z(shift_index) * out.real()/n_rows/n_cols;
638
639 // D = D + Z(x_d) x G/n_pixels
640 denom = denom.array() + delta_denom.array();
641
642 // update status
643 if ((kk % 100) == 1) {
644 double max_ratio = -1;
645 // maximum of denominator
646 double max_denom = 0.01 * denom.maxCoeff();
647
648 // find largest value of abs(delta_denom / denom)
649 for (Eigen::Index i=0; i<n_rows; ++i) {
650 for (Eigen::Index j=0; j<n_cols; ++j) {
651 // exclude small denom values
652 if (denom(i,j) > max_denom) {
653 // abs(delta_denom / denom)
654 auto ratio = max_ratio = abs(delta_denom(i,j) / denom(i,j));
655 // if absolute change in denom is > current ratio
656 if (ratio > max_ratio) {
657 // update max ratio
658 max_ratio = ratio;
659 }
660 }
661 }
662 }
663 logger->info("{} iteration(s) complete. denom ratio = {}", kk, static_cast<float>(max_ratio));
664
665 // check if we've reached max loop or if change in denom is too small
666 if (((kk >= max_loops) && (max_ratio < 0.0002)) || max_ratio < 1e-10) {
667 done = true;
668 }
669 }
670 }
671 }
672 }
673
674 // zero out extremely small denom values
675 for (Eigen::Index i=0; i<n_rows; ++i) {
676 for (Eigen::Index j=0; j<n_cols; ++j) {
677 if (denom(i,j) < denom_limit) {
678 denom(i,j) = 0;
679 }
680 }
681 }
682 }
683
684 // free fftw vectors
685 fftw_free(a);
686 fftw_free(b);
687
688 // destroy fftw plans
689 fftw_destroy_plan(pf);
690 fftw_destroy_plan(pr);
691}
692
693template<class MB, class CD>
694void WienerFilter::make_template(MB &mb, CD &calib_data, const double template_fwhm_rad, const int map_index) {
695 // make sure filtered maps have even dimensions
696 n_rows = mb.n_rows;
697 n_cols = mb.n_cols;
698
699 // x and y spacing should be equal
700 diff_rows = abs(mb.rows_tan_vec(1) - mb.rows_tan_vec(0));
701 diff_cols = abs(mb.cols_tan_vec(1) - mb.cols_tan_vec(0));
702
703 // highpass template
704 if (template_type=="highpass") {
705 logger->info("creating highpass template");
707 filter_template(0,0) = 1;
708 }
709
710 // gaussian template
711 else if (template_type=="gaussian") {
712 logger->info("creating gaussian template");
714 }
715
716 // airy template
717 else if (template_type=="airy") {
718 logger->info("creating airy template");
720 }
721
722 // symmetric version of kernel template
723 else {
724 logger->info("creating template from kernel map");
725 make_kernel_template(mb, map_index, calib_data);
726 }
727}
728
729template<class MB>
730void WienerFilter::run_filter(MB &mb, const int map_index) {
731 // calculate pixel standard deviations
732 logger->debug("calculating rr");
733 calc_rr(mb, map_index);
734 logger->debug("rr {}", rr);
735
736 // calculate normalized psd
737 logger->debug("calculating vvq");
738 calc_vvq(mb, map_index);
739 logger->debug("vvq {}", vvq);
740
741 // calculate denominator
742 logger->debug("calculating denominator");
744 logger->debug("denominator {}", denom);
745
746 // calculate numerator
747 logger->debug("calculating numerator");
749 logger->debug("numerator {}", nume);
750}
751
753 // set up fftw
754 fftw_complex *a;
755 fftw_complex *b;
756 fftw_plan pf, pr;
757
758 // allocate space for 2d ffts
759 a = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
760 b = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*n_rows*n_cols);
761
762 // fftw plans
763 pf = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_FORWARD, FFTW_ESTIMATE);
764 pr = fftw_plan_dft_2d(n_rows, n_cols, a, b, FFTW_BACKWARD, FFTW_ESTIMATE);
765
766 // inputs and outputs to ffts
767 Eigen::MatrixXcd in(n_rows,n_cols), out(n_rows,n_cols);
768
770
771 in.real() = filter_template;
772 in.imag().setZero();
773
774 // fft(f(x))
775 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
776 out = out*n_rows*n_cols;
777
778 Eigen::MatrixXcd fft_filter = out;
779
780 in.real() = filtered_map;
781 in.imag().setZero();
782
783 out = engine_utils::fft2<engine_utils::forward>(in, pf, a, b);
784 out = out*n_rows*n_cols;
785
786 // convolution
787 in.real() = out.real().array() * fft_filter.real().array() - out.imag().array() * fft_filter.imag().array();
788 in.imag() = out.imag().array() * fft_filter.real().array() + out.real().array() * fft_filter.imag().array();
789
790 out = engine_utils::fft2<engine_utils::inverse>(in, pr, a, b);
791 out = out/n_rows/n_cols;
792
793 nume = out.real();
794 denom.setOnes(n_rows,n_cols);
795
796 // free fftw vectors
797 fftw_free(a);
798 fftw_free(b);
799
800 // destroy fftw plans
801 fftw_destroy_plan(pf);
802 fftw_destroy_plan(pr);
803}
804
805void WienerFilter::destripe(double threshold_factor) {
806
807 // allocate FFTW plans and buffers
808 fftw_complex *in, *out;
809 fftw_plan p_forward, p_backward;
810
811 in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n_rows * n_cols);
812 out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n_rows * n_cols);
813
814 // create plans
815 p_forward = fftw_plan_dft_2d(n_rows, n_cols, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
816 p_backward = fftw_plan_dft_2d(n_rows, n_cols, out, in, FFTW_BACKWARD, FFTW_ESTIMATE);
817
818 // copy the image to the in buffer and set imaginary parts to zero
819 for (int i = 0; i < n_rows; ++i) {
820 for (int j = 0; j < n_cols; ++j) {
821 in[i * n_cols + j][0] = filtered_map(i, j);
822 in[i * n_cols + j][1] = 0.0;
823 }
824 }
825
826 // perform the forward FFT
827 fftw_execute(p_forward);
828
829 // compute the magnitude and find the maximum
830 double max_magnitude = 0.0;
831 for (int i = 0; i < n_rows * n_cols; ++i) {
832 double magnitude = std::sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
833 if (magnitude > max_magnitude) {
834 max_magnitude = magnitude;
835 }
836 }
837
838 // threshold and zero out coefficients below threshold
839 double threshold = threshold_factor * max_magnitude;
840 int n_pixels = 0;
841 for (int i = 0; i < n_rows * n_cols; ++i) {
842 double magnitude = std::sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
843 if (magnitude < threshold) {
844 out[i][0] = 0.0;
845 out[i][1] = 0.0;
846 n_pixels++;
847 }
848 }
849
850 logger->info("number of pixels below threshold {}", n_pixels);
851
852 // perform the inverse FFT
853 fftw_execute(p_backward);
854
855 // copy the normalized real part back to the Eigen matrix
856 for (int i = 0; i < n_rows; ++i) {
857 for (int j = 0; j < n_cols; ++j) {
858 filtered_map(i, j) = in[i * n_cols + j][0] / (n_rows * n_cols);
859 }
860 }
861
862 // cleanup
863 fftw_destroy_plan(p_forward);
864 fftw_destroy_plan(p_backward);
865 fftw_free(in);
866 fftw_free(out);
867}
868
869template<class MB>
870void WienerFilter::filter_maps(MB &mb, const int map_index) {
871 /*if (filter_type=="destripe") {
872 filtered_map = mb.signal[map_index];
873 destripe(0.5);
874 mb.signal[map_index] = filtered_map;
875 }*/
876
877 // filter kernel
878 logger->info("filtering kernel");
879 filtered_map = mb.kernel[map_index];
880 // run all filter steps
881 if (filter_type=="wiener_filter") {
882 uniform_weight = true;
883 run_filter(mb, map_index);
884 }
885 else if (filter_type=="convolve") {
886 run_convolve();
887 }
888
889 // divide by filtered weight
890 for (Eigen::Index i=0; i<n_rows; ++i) {
891 for (Eigen::Index j=0; j<n_cols; ++j) {
892 if (denom(i,j) != 0.0) {
893 mb.kernel[map_index](i,j) = nume(i,j)/denom(i,j);
894 }
895 else {
896 mb.kernel[map_index](i,j) = 0.0;
897 }
898 }
899 }
900
901 logger->info("kernel filtering done");
902
903 logger->info("filtering signal");
904 // filter signal
905 filtered_map = mb.signal[map_index];
906 if (filter_type=="wiener_filter") {
907 uniform_weight = false;
908 run_filter(mb, map_index);
909 }
910 else if (filter_type=="convolve") {
911 run_convolve();
912 }
913
914 // divide by filtered weight
915 for (Eigen::Index i=0; i<n_rows; ++i) {
916 for (Eigen::Index j=0; j<n_cols; ++j) {
917 if (denom(i,j) != 0.0) {
918 mb.signal[map_index](i,j) = nume(i,j)/denom(i,j);
919 }
920 else {
921 mb.signal[map_index](i,j) = 0.0;
922 }
923 }
924 }
925 if (filter_type=="wiener_filter") {
926 // weight map is the denominator
927 mb.weight[map_index] = denom;
928 }
929 else if (filter_type=="convolve") {
930 filtered_map = mb.weight[map_index];
931 run_convolve();
932 mb.weight[map_index] = nume;
933 }
934
935 logger->info("signal/weight map filtering done");
936}
937
938template<class MB>
939void WienerFilter::filter_noise(MB &mb, const int map_index, const int noise_num) {
940 // get noise map
941 filtered_map = Eigen::Map<Eigen::MatrixXd>(mb.noise[map_index].data() + noise_num * mb.n_rows * mb.n_cols,
942 mb.n_rows, mb.n_cols);
943
944 // don't need to run through the whole filter, just the numerator
945 if (filter_type=="wiener_filter") {
947 }
948 else if (filter_type=="convolve") {
949 run_convolve();
950 }
951
952 Eigen::MatrixXd ratio(n_rows,n_cols);
953
954 // divide by filtered weight
955 for (Eigen::Index i=0; i<n_rows; ++i) {
956 for (Eigen::Index j=0; j<n_cols; ++j) {
957 if (denom(i,j) != 0.0) {
958 ratio(i,j) = nume(i,j)/denom(i,j);
959 }
960 else {
961 ratio(i,j) = 0.0;
962 }
963 }
964 }
965
966 // map to tensor
967 Eigen::TensorMap<Eigen::Tensor<double, 2>> in_tensor(ratio.data(), ratio.rows(), ratio.cols());
968 mb.noise[map_index].chip(noise_num,2) = in_tensor;
969}
970
971} // namespace mapmaking
Definition utils.h:972
double x_min
Definition utils.h:974
double x_max
Definition utils.h:975
Definition fitting.h:13
auto fit_to_gaussian(Eigen::DenseBase< Derived > &, Eigen::DenseBase< Derived > &, double, double, double)
Definition fitting.h:172
@ pointing
Definition fitting.h:18
Definition toltec_io.h:10
std::map< Eigen::Index, std::string > array_name_map
Definition toltec_io.h:54
Definition wiener_filter.h:23
void filter_noise(MB &mb, const int, const int)
Definition wiener_filter.h:939
Eigen::MatrixXd denom
Definition wiener_filter.h:62
std::string template_type
Definition wiener_filter.h:29
void make_airy_template(MB &mb, const double)
Definition wiener_filter.h:189
std::string parallel_policy
Definition wiener_filter.h:59
void calc_vvq(MB &, const int)
Definition wiener_filter.h:340
std::map< std::string, double > template_fwhm_rad
Definition wiener_filter.h:50
double psd_lim
Definition wiener_filter.h:44
Eigen::MatrixXd rr
Definition wiener_filter.h:62
bool uniform_weight
Definition wiener_filter.h:33
Eigen::MatrixXd vvq
Definition wiener_filter.h:62
std::shared_ptr< spdlog::logger > logger
Definition wiener_filter.h:26
void calc_numerator()
Definition wiener_filter.h:457
std::string filter_type
Definition wiener_filter.h:29
bool normalize_error
Definition wiener_filter.h:31
int max_loops
Definition wiener_filter.h:40
void filter_maps(MB &, const int)
Definition wiener_filter.h:870
void calc_rr(MB &, const int)
Definition wiener_filter.h:330
double init_fwhm
Definition wiener_filter.h:47
engine_utils::mapFitter map_fitter
Definition wiener_filter.h:69
void get_config(config_t &, std::vector< std::vector< std::string > > &, std::vector< std::vector< std::string > > &)
Definition wiener_filter.h:126
void make_template(MB &, CD &c, const double, const int)
Definition wiener_filter.h:694
double denom_limit
Definition wiener_filter.h:42
void make_gaussian_template(MB &mb, const double)
Definition wiener_filter.h:160
Eigen::MatrixXd filtered_map
Definition wiener_filter.h:64
int n_cols
Definition wiener_filter.h:53
Eigen::MatrixXd filter_template
Definition wiener_filter.h:66
void calc_denominator()
Definition wiener_filter.h:523
void run_convolve()
Definition wiener_filter.h:752
void destripe(double)
Definition wiener_filter.h:805
int n_rows
Definition wiener_filter.h:53
Eigen::MatrixXd nume
Definition wiener_filter.h:62
double diff_cols
Definition wiener_filter.h:56
void run_filter(MB &, const int)
Definition wiener_filter.h:730
int n_loops
Definition wiener_filter.h:38
double diff_rows
Definition wiener_filter.h:56
void make_kernel_template(MB &mb, const int, CD &)
Definition wiener_filter.h:232
bool run_lowpass
Definition wiener_filter.h:35
void get_config_value(config_t config, param_t &param, key_vec_t &missing_keys, key_vec_t &invalid_keys, option_t option, std::vector< param_t > allowed={}, std::vector< param_t > min_val={}, std::vector< param_t > max_val={})
Definition config.h:58
#define FWHM_TO_STD
Definition constants.h:48
#define ASEC_TO_RAD
Definition constants.h:33
constexpr auto pi
Definition constants.h:6
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
auto shift_1D(Eigen::DenseBase< Derived > &in, std::vector< Eigen::Index > shift_indices)
Definition utils.h:865
Definition jinc_mm.h:24