PURIFY
Next-generation radio interferometric imaging
kernels.cc
Go to the documentation of this file.
1 #include "purify/kernels.h"
2 #include "purify/config.h"
3 #include "purify/logging.h"
4 namespace purify {
5 
6 namespace kernels {
7 t_real kaiser_bessel(const t_real x, const t_real J) {
8  /*
9  kaiser bessel gridding kernel
10  */
11  t_real alpha = 2.34 * J; // value said to be optimal in Fessler et. al. 2003
12  return kaiser_bessel_general(x, J, alpha);
13 }
14 
15 t_real kaiser_bessel_general(const t_real x, const t_real J, const t_real alpha) {
16  /*
17  kaiser bessel gridding kernel
18  */
19  if (2 * x > J) return 0;
20  t_real a = 2 * x / J;
21  return boost::math::cyl_bessel_i(0, std::real(alpha * std::sqrt(1 - a * a))) /
22  boost::math::cyl_bessel_i(0, alpha);
23 }
24 
25 t_real ft_kaiser_bessel_general(const t_real x, const t_real J, const t_real alpha) {
26  /*
27  Fourier transform of kaiser bessel gridding kernel
28  */
29 
30  t_complex eta = std::sqrt(
31  static_cast<t_complex>((constant::pi * x * J) * (constant::pi * x * J) - alpha * alpha));
32  const t_real normalisation = J / boost::math::cyl_bessel_i(0, alpha);
33 
34  return std::real(std::sin(eta) / eta) *
35  normalisation; // simple way of doing the calculation, the
36  // boost bessel funtions do not support
37  // complex valued arguments
38 }
39 
40 t_real ft_kaiser_bessel(const t_real x, const t_real J) {
41  /*
42  Fourier transform of kaiser bessel gridding kernel
43  */
44 
45  t_real alpha = 2.34 * J; // value said to be optimal in Fessler et. al. 2003
46  return ft_kaiser_bessel_general(x, J, alpha);
47 }
48 
49 t_real gaussian(const t_real x, const t_real J) {
50  /*
51  Guassian gridding kernel
52 
53  x:: value to evaluate
54  J:: support size
55  */
56  t_real sigma = 0.31 * std::pow(J, 0.52); // Optimal sigma according to fessler et al.
57  return gaussian_general(x, J, sigma);
58 }
59 
60 t_real ft_gaussian(const t_real x, const t_real J) {
61  /*
62  Fourier transform of guassian gridding kernel
63 
64  x:: value to evaluate
65  J:: support size of gridding kernel
66  */
67  t_real sigma = 0.31 * std::pow(J, 0.52);
68  return ft_gaussian_general(x, J, sigma);
69 }
70 
71 t_real calc_for_pswf(const t_real eta0, const t_real J, const t_real alpha) {
72  // polynomial coefficients for prolate spheriodal wave function rational approximation
73  const std::array<t_real, 6> p1 = {
74  {8.203343e-2, -3.644705e-1, 6.278660e-1, -5.335581e-1, 2.312756e-1, 2 * 0.0}};
75  const std::array<t_real, 6> p2 = {
76  {4.028559e-3, -3.697768e-2, 1.021332e-1, -1.201436e-1, 6.412774e-2, 2 * 0.0}};
77  const std::array<t_real, 3> q1 = {{1., 8.212018e-1, 2.078043e-1}};
78  const std::array<t_real, 3> q2 = {{1., 9.599102e-1, 2.918724e-1}};
79 
80  if (J != 6 or alpha != 1) {
81  return 0;
82  }
83  // Calculating numerator and denominator using Horner's rule.
84  // PSWF = numerator / denominator
85 
86  auto fraction = [](t_real eta, std::array<t_real, 6> const &p, std::array<t_real, 3> const &q) {
87  auto const p_size = sizeof(p) / sizeof(p[0]) - 1;
88  auto const q_size = sizeof(q) / sizeof(q[0]) - 1;
89 
90  auto numerator = p[p_size];
91  for (auto i = decltype(p_size){1}; i <= p_size; ++i)
92  numerator = eta * numerator + p[p_size - i];
93 
94  auto denominator = q[q_size];
95  for (auto i = decltype(q_size){1}; i <= q_size; ++i)
96  denominator = eta * denominator + q[q_size - i];
97 
98  return numerator / denominator;
99  };
100  if (0 <= std::abs(eta0) and std::abs(eta0) <= 0.75)
101  return fraction(eta0 * eta0 - 0.75 * 0.75, p1, q1);
102  if (0.75 < std::abs(eta0) and std::abs(eta0) <= 1) return fraction(eta0 * eta0 - 1 * 1, p2, q2);
103 
104  return 0;
105 }
106 
107 t_real pswf(const t_real x, const t_real J) {
108  /*
109  Calculates the standard PSWF for radio astronomy, with a support of J = 6 and alpha = 1.
110 
111  x:: value to evaluate
112  J:: support size of gridding kernel
113 
114  The tailored prolate spheroidal wave functions for gridding radio astronomy.
115  Details are explained in Optimal Gridding of Visibility Data in Radio
116  Astronomy, F. R. Schwab 1983.
117 
118 */
119  const t_real eta0 = 2 * x / J;
120  const t_real alpha = 1;
121  return calc_for_pswf(eta0, J, alpha) * std::pow(1 - eta0 * eta0, alpha);
122 }
123 
124 t_real ft_pswf(const t_real x, const t_real J) {
125  /*
126  Calculates the fourier transform of the standard PSWF for radio astronomy, with a support of J
127  = 6 and alpha = 1.
128 
129  x:: value to evaluate
130  J:: support size of gridding kernel
131 
132  The tailored prolate spheroidal wave functions for gridding radio astronomy.
133  Details are explained in Optimal Gridding of Visibility Data in Radio
134  Astronomy, F. R. Schwab 1983.
135 
136 */
137 
138  const t_real alpha = 1;
139  const t_real eta0 = 2 * x;
140 
141  return calc_for_pswf(eta0, J, alpha) * std::sqrt(5.343);
142 }
143 
144 std::vector<t_real> kernel_samples(const t_int total_samples,
145  const std::function<t_real(t_real)> kernelu) {
146  /*
147  Pre-calculates samples of a kernel, that can be used with linear interpolation (see Rapid
148  gridding reconstruction with a minimal oversampling ratio, Beatty et. al. 2005)
149  */
150  std::vector<t_real> samples(total_samples);
151  for (Vector<t_real>::Index i = 0; i < static_cast<t_int>(total_samples); ++i) {
152  samples[i] = kernelu(static_cast<t_real>(i) / total_samples);
153  }
154  return samples;
155 }
156 t_real kernel_zero_interp(const std::vector<t_real> &samples, const t_real x, const t_real J) {
157  /*
158  Calculates kernel using linear interpolation between pre-calculated samples. (see Rapid
159  gridding reconstruction with a minimal oversampling ratio, Beatty et. al. 2005)
160  */
161  const t_int total_samples = samples.size();
162 
163  const t_real i_effective = 2 * std::abs(x) * total_samples / J;
164 
165  const t_real i_0 = std::floor(i_effective);
166  if (i_0 < 0 or i_0 >= total_samples) return 0.;
167 
168  return samples[static_cast<t_int>(i_0)];
169 }
170 
171 t_real kernel_linear_interp(const Vector<t_real> &samples, const t_real x, const t_real J) {
172  /*
173  Calculates kernel using linear interpolation between pre-calculated samples. (see Rapid
174  gridding reconstruction with a minimal oversampling ratio, Beatty et. al. 2005)
175  */
176  t_int total_samples = samples.size();
177 
178  t_real i_effective = (x + J / 2) * total_samples / J;
179 
180  t_int i_0 = floor(i_effective);
181  t_int i_1 = ceil(i_effective);
182  // case where i_effective is a sample point
183  if (std::abs(i_0 - i_1) == 0) {
184  return samples(i_0);
185  }
186  // linearly interpolate from nearest neighbour
187  t_real y_0;
188  t_real y_1;
189  if (i_0 < 0 or i_0 >= total_samples) {
190  y_0 = 0;
191  } else {
192  y_0 = samples(i_0);
193  }
194  if (i_1 < 0 or i_1 >= total_samples) {
195  y_1 = 0;
196  } else {
197  y_1 = samples(i_1);
198  }
199  t_real output = y_0 + (y_1 - y_0) / (i_1 - i_0) * (i_effective - i_0);
200  return output;
201 }
202 t_real pill_box(const t_real x, const t_real J) {
203  /*
204  Gaussian gridding kernel
205 
206  x:: value to evaluate
207  J:: support size
208  */
209  return 1. / static_cast<t_real>(J);
210 }
211 t_real ft_pill_box(const t_real x, const t_real J) {
212  /*
213  Gaussian gridding kernel
214 
215  x:: value to evaluate
216  J:: support size
217  */
218  return boost::math::sinc_pi(J * x * constant::pi);
219 }
220 t_real gaussian_general(const t_real x, const t_real J, const t_real sigma) {
221  /*
222  Gaussian gridding kernel
223 
224  x:: value to evaluate
225  J:: support size
226  sigma:: standard deviation of Gaussian kernel (in pixels)
227  */
228 
229  t_real a = x / sigma;
230  return std::exp(-a * a * 0.5) / (sigma * std::sqrt(2 * constant::pi));
231 }
232 
233 t_real ft_gaussian_general(const t_real x, const t_real J, const t_real sigma) {
234  /*
235  Fourier transform of Gaussian gridding kernel
236 
237  x:: value to evaluate
238  J:: support size of gridding kernel
239  sigma:: standard deviation of Gaussian kernel (in pixels)
240  */
241 
242  t_real a = x * sigma;
243  return std::exp(a * a * 2);
244 }
245 } // namespace kernels
246 
247 std::tuple<std::function<t_real(t_real)>, std::function<t_real(t_real)>,
248  std::function<t_real(t_real)>, std::function<t_real(t_real)>>
249 create_kernels(const kernels::kernel kernel_name_, const t_uint Ju_, const t_uint Jv_,
250  const t_real imsizey_, const t_real imsizex_, const t_real oversample_ratio) {
251  // PURIFY_MEDIUM_LOG("Kernel Name: {}", kernel_name_.c_str());
252  PURIFY_MEDIUM_LOG("Kernel Support: {} x {}", Ju_, Jv_);
253  const t_uint ftsizev_ = std::floor(imsizey_ * oversample_ratio);
254  const t_uint ftsizeu_ = std::floor(imsizex_ * oversample_ratio);
255  if ((kernel_name_ == kernels::kernel::pswf) and (Ju_ != 6 or Jv_ != 6)) {
256  PURIFY_ERROR("Error: Only a support of 6 is implemented for PSWFs.");
257  throw std::runtime_error("Incorrect input: PSWF requires a support of 6");
258  }
259  switch (kernel_name_) {
260  case kernels::kernel::kb: {
261  auto kbu = [=](const t_real x) { return kernels::kaiser_bessel(x, Ju_); };
262  auto kbv = [=](const t_real x) { return kernels::kaiser_bessel(x, Jv_); };
263  auto ftkbu = [=](const t_real x) { return kernels::ft_kaiser_bessel(x / ftsizeu_ - 0.5, Ju_); };
264  auto ftkbv = [=](const t_real x) { return kernels::ft_kaiser_bessel(x / ftsizev_ - 0.5, Jv_); };
265  return std::make_tuple(kbu, kbv, ftkbu, ftkbv);
266  break;
267  }
269  if (Ju_ != Jv_)
270  throw std::runtime_error("Ju and Jv must be the same support size for presampling kernels.");
271  const auto samples = kernels::kernel_samples(
272  1e5, [&](const t_real x) { return kernels::kaiser_bessel(x, Ju_); });
273  auto kbu = [=](const t_real x) { return kernels::kernel_zero_interp(samples, x, Ju_); };
274  auto kbv = [=](const t_real x) { return kernels::kernel_zero_interp(samples, x, Jv_); };
275  auto ftkbu = [=](const t_real x) { return kernels::ft_kaiser_bessel(x / ftsizeu_ - 0.5, Ju_); };
276  auto ftkbv = [=](const t_real x) { return kernels::ft_kaiser_bessel(x / ftsizev_ - 0.5, Jv_); };
277  return std::make_tuple(kbu, kbv, ftkbu, ftkbv);
278  break;
279  }
280  case kernels::kernel::kbmin: {
281  const t_real kb_interp_alpha_Ju =
282  constant::pi * std::sqrt(Ju_ * Ju_ / (oversample_ratio * oversample_ratio) *
283  (oversample_ratio - 0.5) * (oversample_ratio - 0.5) -
284  0.8);
285  const t_real kb_interp_alpha_Jv =
286  constant::pi * std::sqrt(Jv_ * Jv_ / (oversample_ratio * oversample_ratio) *
287  (oversample_ratio - 0.5) * (oversample_ratio - 0.5) -
288  0.8);
289  auto kbu = [=](const t_real x) {
290  return kernels::kaiser_bessel_general(x, Ju_, kb_interp_alpha_Ju);
291  };
292  auto kbv = [=](const t_real x) {
293  return kernels::kaiser_bessel_general(x, Jv_, kb_interp_alpha_Jv);
294  };
295  auto ftkbu = [=](const t_real x) {
296  return kernels::ft_kaiser_bessel_general(x / ftsizeu_ - 0.5, Ju_, kb_interp_alpha_Ju);
297  };
298  auto ftkbv = [=](const t_real x) {
299  return kernels::ft_kaiser_bessel_general(x / ftsizev_ - 0.5, Jv_, kb_interp_alpha_Jv);
300  };
301  return std::make_tuple(kbu, kbv, ftkbu, ftkbv);
302  break;
303  }
304  case kernels::kernel::pswf: {
305  auto pswfu = [=](const t_real x) { return kernels::pswf(x, Ju_); };
306  auto pswfv = [=](const t_real x) { return kernels::pswf(x, Jv_); };
307  auto ftpswfu = [=](const t_real x) { return kernels::ft_pswf(x / ftsizeu_ - 0.5, Ju_); };
308  auto ftpswfv = [=](const t_real x) { return kernels::ft_pswf(x / ftsizev_ - 0.5, Jv_); };
309  return std::make_tuple(pswfu, pswfv, ftpswfu, ftpswfv);
310  break;
311  }
312  case kernels::kernel::gauss: {
313  auto gaussu = [=](const t_real x) { return kernels::gaussian(x, Ju_); };
314  auto gaussv = [=](const t_real x) { return kernels::gaussian(x, Jv_); };
315  auto ftgaussu = [=](const t_real x) { return kernels::ft_gaussian(x / ftsizeu_ - 0.5, Ju_); };
316  auto ftgaussv = [=](const t_real x) { return kernels::ft_gaussian(x / ftsizev_ - 0.5, Jv_); };
317  return std::make_tuple(gaussu, gaussv, ftgaussu, ftgaussv);
318  break;
319  }
320  case kernels::kernel::box: {
321  auto boxu = [=](const t_real x) { return kernels::pill_box(x, Ju_); };
322  auto boxv = [=](const t_real x) { return kernels::pill_box(x, Jv_); };
323  auto ftboxu = [=](const t_real x) { return kernels::ft_pill_box(x / ftsizeu_ - 0.5, Ju_); };
324  auto ftboxv = [=](const t_real x) { return kernels::ft_pill_box(x / ftsizev_ - 0.5, Jv_); };
325  return std::make_tuple(boxu, boxv, ftboxu, ftboxv);
326  break;
327  }
329  const t_real sigma = 1; // In units of radians, Rafael uses sigma = 2 * pi / ftsizeu_. However,
330  // this should be 1 in units of pixels.
331  auto gaussu = [=](const t_real x) { return kernels::gaussian_general(x, Ju_, sigma); };
332  auto gaussv = [=](const t_real x) { return kernels::gaussian_general(x, Jv_, sigma); };
333  auto ftgaussu = [=](const t_real x) {
334  return kernels::ft_gaussian_general(x / ftsizeu_ - 0.5, Ju_, sigma);
335  };
336  auto ftgaussv = [=](const t_real x) {
337  return kernels::ft_gaussian_general(x / ftsizev_ - 0.5, Jv_, sigma);
338  };
339  return std::make_tuple(gaussu, gaussv, ftgaussu, ftgaussv);
340  break;
341  }
342  default:
343  throw std::runtime_error("Did not choose valid kernel.");
344  }
345 }
346 
347 std::tuple<std::function<t_real(t_real)>, std::function<t_real(t_real)>> create_radial_ftkernel(
348  const kernels::kernel kernel_name_, const t_uint Ju_, const t_real oversample_ratio) {
349  // PURIFY_MEDIUM_LOG("Kernel Name: {}", kernel_name_.c_str());
350  PURIFY_MEDIUM_LOG("Radial Kernel Support: {}", Ju_);
351  if ((kernel_name_ == kernels::kernel::pswf) and (Ju_ != 6)) {
352  PURIFY_ERROR("Error: Only a support of 6 is implemented for PSWFs.");
353  throw std::runtime_error("Incorrect input: PSWF requires a support of 6");
354  }
355  switch (kernel_name_) {
356  case kernels::kernel::kb: {
357  return std::make_tuple(
358  [=](const t_real x) { return kernels::ft_kaiser_bessel(x, static_cast<t_real>(Ju_)); },
359  [=](const t_real x) { return kernels::kaiser_bessel(x, static_cast<t_real>(Ju_)); });
360  break;
361  }
362  case kernels::kernel::kbmin: {
363  const t_real kb_interp_alpha_Ju =
364  constant::pi * std::sqrt(Ju_ * Ju_ / (oversample_ratio * oversample_ratio) *
365  (oversample_ratio - 0.5) * (oversample_ratio - 0.5) -
366  0.8);
367  auto kbuv = [=](const t_real x) {
368  return kernels::kaiser_bessel_general(x, Ju_, kb_interp_alpha_Ju);
369  };
370  auto ftkbuv = [=](const t_real x) {
371  return kernels::ft_kaiser_bessel_general(x, Ju_, kb_interp_alpha_Ju);
372  };
373  return std::make_tuple(ftkbuv, kbuv);
374  break;
375  }
376  case kernels::kernel::pswf: {
377  return std::make_tuple(
378  [=](const t_real x) { return kernels::ft_pswf(x, static_cast<t_real>(Ju_)); },
379  [=](const t_real x) { return kernels::pswf(x, Ju_); });
380  break;
381  }
382  case kernels::kernel::gauss: {
383  return std::make_tuple(
384  [=](const t_real x) { return kernels::ft_gaussian(x, static_cast<t_real>(Ju_)); },
385  [=](const t_real x) { return kernels::gaussian(x, Ju_); });
386  break;
387  }
388  case kernels::kernel::box: {
389  return std::make_tuple(
390  [=](const t_real x) { return kernels::ft_pill_box(x, static_cast<t_real>(Ju_)); },
391  [=](const t_real x) { return kernels::pill_box(x, Ju_); });
392  break;
393  }
394  default:
395  throw std::runtime_error("Did not choose valid radial kernel.");
396  }
397 }
398 } // namespace purify
#define PURIFY_ERROR(...)
\macro Something is definitely wrong, algorithm exits
Definition: logging.h:190
#define PURIFY_MEDIUM_LOG(...)
Medium priority message.
Definition: logging.h:205
const t_real pi
mathematical constant
Definition: types.h:70
t_real ft_kaiser_bessel(const t_real x, const t_real J)
Fourier transform of kaiser bessel kernel.
Definition: kernels.cc:40
t_real ft_gaussian_general(const t_real x, const t_real J, const t_real sigma)
Fourier transform of general Gaussian kernel.
Definition: kernels.cc:233
std::vector< t_real > kernel_samples(const t_int total_samples, const std::function< t_real(t_real)> kernelu)
Calculates samples of a kernel.
Definition: kernels.cc:144
t_real kaiser_bessel(const t_real x, const t_real J)
Kaiser-Bessel kernel.
Definition: kernels.cc:7
t_real kernel_zero_interp(const std::vector< t_real > &samples, const t_real x, const t_real J)
zeroth order interpolates from samples of kernel
Definition: kernels.cc:156
t_real pill_box(const t_real x, const t_real J)
Box car function for kernel.
Definition: kernels.cc:202
t_real ft_pswf(const t_real x, const t_real J)
Fourier transform of PSWF kernel.
Definition: kernels.cc:124
t_real gaussian_general(const t_real x, const t_real J, const t_real sigma)
Fourier transform of general Gaussian kernel.
Definition: kernels.cc:220
t_real kaiser_bessel_general(const t_real x, const t_real J, const t_real alpha)
More general Kaiser-Bessel kernel.
Definition: kernels.cc:15
t_real calc_for_pswf(const t_real eta0, const t_real J, const t_real alpha)
Calculates Horner's Rule the standard PSWF for radio astronomy, with a support of J = 6 and alpha = 1...
Definition: kernels.cc:71
t_real ft_pill_box(const t_real x, const t_real J)
Fourier transform of box car function, a Sinc function.
Definition: kernels.cc:211
t_real gaussian(const t_real x, const t_real J)
Gaussian kernel.
Definition: kernels.cc:49
t_real kernel_linear_interp(const Vector< t_real > &samples, const t_real x, const t_real J)
linearly interpolates from samples of kernel
Definition: kernels.cc:171
t_real pswf(const t_real x, const t_real J)
PSWF kernel.
Definition: kernels.cc:107
t_real ft_gaussian(const t_real x, const t_real J)
Fourier transform of Gaussian kernel.
Definition: kernels.cc:60
t_real ft_kaiser_bessel_general(const t_real x, const t_real J, const t_real alpha)
Fourier transform of more general Kaiser-Bessel kernel.
Definition: kernels.cc:25
std::tuple< std::function< t_real(t_real)>, std::function< t_real(t_real)> > create_radial_ftkernel(const kernels::kernel kernel_name_, const t_uint Ju_, const t_real oversample_ratio)
Definition: kernels.cc:347
std::tuple< std::function< t_real(t_real)>, std::function< t_real(t_real)>, std::function< t_real(t_real)>, std::function< t_real(t_real)> > create_kernels(const kernels::kernel kernel_name_, const t_uint Ju_, const t_uint Jv_, const t_real imsizey_, const t_real imsizex_, const t_real oversample_ratio)
Definition: kernels.cc:249