marlu/
selection.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! Selecting a subset of correlator visibilities from an observation using mwalib indices.
6//!
7//! Observations can sometimes be too large to fit in memory. This method will only load
8//! visibilities from the selected timesteps, coarse channels and baselines in order to enable
9//! processing in "chunks"
10//!
11//! The timesteps are specified as a range of indices in the [`marlu::mwalib::CorrelatorContext`]'s
12//! timestep array, which should be a contiguous superset of times from all provided coarse gpubox
13//! files. A similar concept applies to coarse channels. Instead of reading visibilities for all
14//! known timesteps / coarse channels, it is recommended to use `common_coarse_chan_indices` and
15//! `common_timestep_indices`, as these ignore timesteps and coarse channels which are missing
16//! contiguous data. `common_good_timestep_indices` is also a good choice to avoid quack time.
17//!
18//! For more details, see the [documentation](https://docs.rs/mwalib/latest/mwalib/struct.CorrelatorContext.html).
19//!
20//! Note: it doesn't make sense to ask aoflagger to flag non-contiguous timesteps
21//! or coarse channels, and so this interface only allows to ranges to be used.
22//! For flagging an obeservation with "picket fence" coarse channels or timesteps,
23//! contiguous ranges should be flagged separately.
24//!
25//! [`marlu::mwalib::CorrelatorContext`]: https://docs.rs/mwalib/latest/mwalib/struct.CorrelatorContext.html
26//!
27//! # Examples
28//!
29//! ```rust
30//! use marlu::{VisSelection};
31//!
32//! let mut vis_sel = VisSelection {
33//!     timestep_range: 0..1,
34//!     coarse_chan_range: 0..1,
35//!     baseline_idxs: vec![0, 1],
36//! };
37//!
38//! // Create a blank array to store flags and visibilities
39//! let fine_chans_per_coarse = 2;
40//! let mut jones_array = vis_sel.allocate_jones(fine_chans_per_coarse).unwrap();
41//!
42//! let dims = jones_array.dim();
43//!
44//! // now try only with a different range of timesteps
45//! vis_sel.timestep_range = 0..2;
46//!
47//! let mut jones_array = vis_sel.allocate_jones(fine_chans_per_coarse).unwrap();
48//!
49//! // different selections have different sized arrays.
50//! assert_ne!(dims, jones_array.dim());
51//! ```
52
53use std::ops::Range;
54
55use thiserror::Error;
56
57use crate::{ndarray::Array3, num_traits::Zero, Jones};
58
59#[cfg(feature = "mwalib")]
60use mwalib::{CorrelatorContext, MetafitsContext};
61
62#[derive(Error, Debug)]
63pub enum SelectionError {
64    #[error("No common timesteps found. CorrelatorContext hdu info: {hdu_info}")]
65    /// Error for when gpuboxes provided have no overlapping visibilities
66    NoCommonTimesteps {
67        /// display of `mwalib::CorrelatorContext::gpubox_time_map`
68        hdu_info: String,
69    },
70
71    #[error("No timesteps were provided. CorrelatorContext hdu info: {hdu_info}")]
72    /// Error for when gpuboxes provided have no overlapping visibilities
73    NoProvidedTimesteps {
74        /// display of `mwalib::CorrelatorContext::gpubox_time_map`
75        hdu_info: String,
76    },
77
78    #[error("Insufficient memory available; need {need_gib} GiB of memory.\nPlease specify the maximum amount of memory to use.")]
79    /// Error when we asked for too much memory
80    InsufficientMemory {
81        /// The amount of memory we think we need
82        need_gib: usize,
83    },
84
85    #[error("bad array shape supplied to argument {argument} of function {function}. expected {expected}, received {received}")]
86    /// Error for bad array shape in provided argument
87    BadArrayShape {
88        /// The argument name within the funciton
89        argument: String,
90        /// The function name
91        function: String,
92        /// The expected shape
93        expected: String,
94        /// The shape that was received instead
95        received: String,
96    },
97
98    #[error("bad baseline index supplied to function {function}. expected {expected}, received {received}")]
99    BadBaselineIdx {
100        /// The function name
101        function: String,
102        /// Predicate on `bl_idxs`
103        expected: String,
104        /// The baseline index that was received instead
105        received: String,
106    },
107
108    #[cfg(feature = "mwalib")]
109    #[error(transparent)]
110    Mwalib(#[from] mwalib::GpuboxError),
111}
112
113/// Keep track of which mwalib indices the values in a jones array, its' weights and its' flags
114/// came from. Similar to a `VisContext`, but requires an `mwalib::CorrelatorContext` to be
115/// fully interpreted
116///
117/// TODO: this definitely needs `fine_chans_per_coarse`
118/// TODO: what about <https://doc.rust-lang.org/std/ops/trait.RangeBounds.html> insetad of Range?
119#[derive(Debug, Default, Clone)]
120pub struct VisSelection {
121    /// selected range of mwalib timestep indices
122    pub timestep_range: Range<usize>,
123    /// selected range of mwalib coarse channel indices
124    pub coarse_chan_range: Range<usize>,
125    /// selected mwalib baseline indices
126    pub baseline_idxs: Vec<usize>,
127}
128
129impl VisSelection {
130    /// Produce a [`VisSelection`] from a given [`marlu::mwalib::CorrelatorContext`].
131    ///
132    /// - timesteps are selected from the first [common](https://docs.rs/mwalib/latest/mwalib/struct.CorrelatorContext.html#structfield.common_timestep_indices) to the last [provided](https://docs.rs/mwalib/latest/mwalib/struct.CorrelatorContext.html#structfield.provided_timestep_indices).
133    /// - only [common](https://docs.rs/mwalib/latest/mwalib/struct.CorrelatorContext.html#structfield.common_coarse_chan_indices) coarse channels are selected
134    /// - all baselines are selected
135    ///
136    /// For more details, see [mwalib core concepts](https://github.com/MWATelescope/mwalib/wiki/Key-Concepts#timesteps-and-coarse-channels)
137    ///
138    /// # Examples
139    ///
140    /// ```rust
141    /// use marlu::{mwalib::CorrelatorContext, VisSelection};
142    ///
143    /// // define our input files
144    /// let metafits_path = "tests/data/1297526432_mwax/1297526432.metafits";
145    /// let gpufits_paths = vec![
146    ///     "tests/data/1297526432_mwax/1297526432_20210216160014_ch117_000.fits",
147    ///     "tests/data/1297526432_mwax/1297526432_20210216160014_ch117_001.fits",
148    ///     "tests/data/1297526432_mwax/1297526432_20210216160014_ch118_000.fits",
149    ///     "tests/data/1297526432_mwax/1297526432_20210216160014_ch118_001.fits",
150    /// ];
151    ///
152    /// let corr_ctx = CorrelatorContext::new(metafits_path, &gpufits_paths).unwrap();
153    /// let vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
154    ///
155    /// assert_eq!(vis_sel.timestep_range.len(), 4);
156    /// ```
157    ///
158    /// # Errors
159    /// This will return [`SelectionError::NoProvidedTimesteps`] if mwalib has determined that no
160    /// timesteps have been provided, [`SelectionError::NoCommonTimesteps`] if none of the provided
161    /// gpuboxes have timesteps in common
162    ///
163    /// [`marlu::mwalib::CorrelatorContext.common_timestep_indices`]:
164    /// [`marlu::mwalib::CorrelatorContext.provided_timestep_indices`]:
165    #[cfg(feature = "mwalib")]
166    pub fn from_mwalib(corr_ctx: &CorrelatorContext) -> Result<Self, SelectionError> {
167        Ok(Self {
168            timestep_range: match (
169                corr_ctx.common_timestep_indices.first(),
170                corr_ctx.provided_timestep_indices.last(),
171            ) {
172                (Some(&first), Some(&last)) if first <= last => first..last + 1,
173                (.., None) => {
174                    return Err(SelectionError::NoProvidedTimesteps {
175                        hdu_info: format!("{:?}", &corr_ctx.gpubox_time_map),
176                    })
177                }
178                _ => {
179                    return Err(SelectionError::NoCommonTimesteps {
180                        hdu_info: format!("{:?}", &corr_ctx.gpubox_time_map),
181                    })
182                }
183            },
184            coarse_chan_range: match (
185                corr_ctx.common_coarse_chan_indices.first(),
186                corr_ctx.common_coarse_chan_indices.last(),
187            ) {
188                (Some(&first), Some(&last)) if first <= last => (first)..(last + 1),
189                _ => {
190                    return Err(SelectionError::NoCommonTimesteps {
191                        hdu_info: format!("{:?}", &corr_ctx.gpubox_time_map),
192                    })
193                }
194            },
195            baseline_idxs: (0..corr_ctx.metafits_context.num_baselines).collect(),
196        })
197    }
198
199    /// Exclude `baseline_idxs` that don't include antennas from `ant_idxs`
200    #[cfg(feature = "mwalib")]
201    pub fn retain_antennas(&mut self, meta_ctx: &MetafitsContext, ant_idxs: &[usize]) {
202        self.baseline_idxs.retain(|&bl_idx| {
203            let bl = &meta_ctx.baselines[bl_idx];
204            ant_idxs.contains(&bl.ant1_index) && ant_idxs.contains(&bl.ant2_index)
205        });
206    }
207
208    /// Exclude `baseline_idxs` that include antennas from `ant_idxs`
209    #[cfg(feature = "mwalib")]
210    pub fn filter_antennas(&mut self, meta_ctx: &MetafitsContext, ant_idxs: &[usize]) {
211        self.baseline_idxs.retain(|&bl_idx| {
212            let bl = &meta_ctx.baselines[bl_idx];
213            !ant_idxs.contains(&bl.ant1_index) && !ant_idxs.contains(&bl.ant2_index)
214        });
215    }
216
217    /// Exclude `baseline_idxs` from autocorrelations
218    #[cfg(feature = "mwalib")]
219    pub fn filter_autos(&mut self, meta_ctx: &MetafitsContext) {
220        self.baseline_idxs.retain(|&bl_idx| {
221            let bl = &meta_ctx.baselines[bl_idx];
222            bl.ant1_index != bl.ant2_index
223        });
224    }
225
226    /// The selected antenna index pairs corresponding to `sel_baselines_idxs`
227    #[cfg(feature = "mwalib")]
228    pub fn get_ant_pairs(&self, meta_ctx: &MetafitsContext) -> Vec<(usize, usize)> {
229        self.baseline_idxs
230            .iter()
231            .map(|&idx| {
232                (
233                    meta_ctx.baselines[idx].ant1_index,
234                    meta_ctx.baselines[idx].ant2_index,
235                )
236            })
237            .collect()
238    }
239
240    /// Get the shape of the jones, flag or weight array for this selection
241    pub fn get_shape(&self, fine_chans_per_coarse: usize) -> (usize, usize, usize) {
242        let num_chans = self.coarse_chan_range.len() * fine_chans_per_coarse;
243        let num_baselines = self.baseline_idxs.len();
244        let num_timesteps = self.timestep_range.len();
245        (num_timesteps, num_chans, num_baselines)
246    }
247
248    /// Estimate the memory size in bytes required to store the given selection without redundant pols.
249    pub fn estimate_bytes_best(&self, fine_chans_per_coarse: usize) -> usize {
250        let shape = self.get_shape(fine_chans_per_coarse);
251        shape.0
252            * shape.1
253            * shape.2
254            * (std::mem::size_of::<Jones<f32>>()
255                + std::mem::size_of::<f32>()
256                + std::mem::size_of::<bool>())
257    }
258
259    /// Allocate a jones array to store visibilities for the selection
260    ///
261    /// # Errors
262    ///
263    /// can raise `SelectionError::InsufficientMemory` if not enough memory.
264    pub fn allocate_jones(
265        &self,
266        fine_chans_per_coarse: usize,
267    ) -> Result<Array3<Jones<f32>>, SelectionError> {
268        let shape = self.get_shape(fine_chans_per_coarse);
269        let num_elems = shape.0 * shape.1 * shape.2;
270        let mut v = Vec::new();
271
272        if v.try_reserve_exact(num_elems) == Ok(()) {
273            // Make the vector's length equal to its new capacity.
274            v.resize(num_elems, Jones::zero());
275            Ok(Array3::from_shape_vec(shape, v).unwrap())
276        } else {
277            // Instead of erroring out with how many GiB we need for *this*
278            // array, error out with how many we need for the whole selection.
279            let need_gib = self.estimate_bytes_best(fine_chans_per_coarse) / 1024_usize.pow(3);
280            Err(SelectionError::InsufficientMemory { need_gib })
281        }
282    }
283
284    /// Allocate a flag array to store flags for the selection
285    ///
286    /// # Errors
287    ///
288    /// can raise `SelectionError::InsufficientMemory` if not enough memory.
289    pub fn allocate_flags(
290        &self,
291        fine_chans_per_coarse: usize,
292    ) -> Result<Array3<bool>, SelectionError> {
293        let shape = self.get_shape(fine_chans_per_coarse);
294        let num_elems = shape.0 * shape.1 * shape.2;
295        let mut v = Vec::new();
296
297        if v.try_reserve_exact(num_elems) == Ok(()) {
298            // Make the vector's length equal to its new capacity.
299            v.resize(num_elems, false);
300            Ok(Array3::from_shape_vec(shape, v).unwrap())
301        } else {
302            // Instead of erroring out with how many GiB we need for *this*
303            // array, error out with how many we need for the whole selection.
304            let need_gib = self.estimate_bytes_best(fine_chans_per_coarse) / 1024_usize.pow(3);
305            Err(SelectionError::InsufficientMemory { need_gib })
306        }
307    }
308
309    /// Allocate a weight array to store weights for the selection
310    ///
311    /// # Errors
312    ///
313    /// can raise `SelectionError::InsufficientMemory` if not enough memory.
314    pub fn allocate_weights(
315        &self,
316        fine_chans_per_coarse: usize,
317    ) -> Result<Array3<f32>, SelectionError> {
318        let shape = self.get_shape(fine_chans_per_coarse);
319        let num_elems = shape.0 * shape.1 * shape.2;
320        let mut v = Vec::new();
321
322        if v.try_reserve_exact(num_elems) == Ok(()) {
323            // Make the vector's length equal to its new capacity.
324            v.resize(num_elems, 0.);
325            Ok(Array3::from_shape_vec(shape, v).unwrap())
326        } else {
327            // Instead of erroring out with how many GiB we need for *this*
328            // array, error out with how many we need for the whole selection.
329            let need_gib = self.estimate_bytes_best(fine_chans_per_coarse) / 1024_usize.pow(3);
330            Err(SelectionError::InsufficientMemory { need_gib })
331        }
332    }
333
334    /// This is a legacy function only to be used for testing.
335    #[cfg(all(test, feature = "mwalib"))]
336    pub(crate) fn read_mwalib(
337        &self,
338        corr_ctx: &CorrelatorContext,
339        mut jones_array: ndarray::ArrayViewMut3<Jones<f32>>,
340        mut flag_array: ndarray::ArrayViewMut3<bool>,
341    ) -> Result<(), SelectionError> {
342        use itertools::izip;
343        use ndarray::prelude::*;
344        use rayon::prelude::*;
345
346        let fine_chans_per_coarse = corr_ctx.metafits_context.num_corr_fine_chans_per_coarse;
347        let shape = self.get_shape(fine_chans_per_coarse);
348
349        if jones_array.dim() != shape {
350            return Err(SelectionError::BadArrayShape {
351                argument: "jones_array".to_string(),
352                function: "VisSelection::read_mwalib".to_string(),
353                expected: format!("{shape:?}"),
354                received: format!("{:?}", jones_array.dim()),
355            });
356        };
357
358        if flag_array.dim() != shape {
359            return Err(SelectionError::BadArrayShape {
360                argument: "flag_array".to_string(),
361                function: "VisSelection::read_mwalib".to_string(),
362                expected: format!("{shape:?}"),
363                received: format!("{:?}", flag_array.dim()),
364            });
365        };
366
367        let max_bl_idx = corr_ctx.metafits_context.baselines.len();
368        // check all selected baseline idxs are < max_bl_idx
369        if self.baseline_idxs.iter().any(|&idx| idx >= max_bl_idx) {
370            return Err(SelectionError::BadBaselineIdx {
371                function: "VisSelection::read_mwalib".to_string(),
372                expected: format!(" < {max_bl_idx}"),
373                received: format!("{:?}", self.baseline_idxs.clone()),
374            });
375        }
376
377        // since we are using read_by_baseline_into_buffer, the visibilities are read in order:
378        // baseline,frequency,pol,r,i
379
380        // compiler optimization
381        let floats_per_chan = 8;
382        assert_eq!(
383            corr_ctx.metafits_context.num_visibility_pols * 2,
384            floats_per_chan
385        );
386
387        let floats_per_baseline = floats_per_chan * fine_chans_per_coarse;
388        let floats_per_hdu = floats_per_baseline * corr_ctx.metafits_context.num_baselines;
389
390        // Load HDUs from each coarse channel. arrays: [timestep][chan][baseline]
391        jones_array
392            .axis_chunks_iter_mut(Axis(1), fine_chans_per_coarse)
393            .into_par_iter()
394            .zip(flag_array.axis_chunks_iter_mut(Axis(1), fine_chans_per_coarse))
395            .zip(self.coarse_chan_range.clone())
396            .try_for_each(|((mut jones_array, mut flag_array), coarse_chan_idx)| {
397                // buffer: [baseline][chan][pol][complex]
398                let mut hdu_buffer: Vec<f32> = vec![0.0; floats_per_hdu];
399
400                // arrays: [chan][baseline]
401                for (mut jones_array, mut flag_array, timestep_idx) in izip!(
402                    jones_array.outer_iter_mut(),
403                    flag_array.outer_iter_mut(),
404                    self.timestep_range.clone(),
405                ) {
406                    match corr_ctx.read_by_baseline_into_buffer(
407                        timestep_idx,
408                        coarse_chan_idx,
409                        hdu_buffer.as_mut_slice(),
410                    ) {
411                        Ok(()) => {
412                            // arrays: [chan]
413                            for (mut jones_array, baseline_idx) in izip!(
414                                jones_array.axis_iter_mut(Axis(1)),
415                                self.baseline_idxs.iter()
416                            ) {
417                                // buffer: [chan][pol][complex]
418                                let hdu_baseline_chunk = &hdu_buffer
419                                    [baseline_idx * floats_per_baseline..][..floats_per_baseline];
420                                for (jones, hdu_chan_chunk) in izip!(
421                                    jones_array.iter_mut(),
422                                    hdu_baseline_chunk.chunks_exact(floats_per_chan)
423                                ) {
424                                    *jones = Jones::from([
425                                        hdu_chan_chunk[0],
426                                        hdu_chan_chunk[1],
427                                        hdu_chan_chunk[2],
428                                        hdu_chan_chunk[3],
429                                        hdu_chan_chunk[4],
430                                        hdu_chan_chunk[5],
431                                        hdu_chan_chunk[6],
432                                        hdu_chan_chunk[7],
433                                    ]);
434                                }
435                            }
436                        }
437                        Err(mwalib::GpuboxError::NoDataForTimeStepCoarseChannel { .. }) => {
438                            eprintln!(
439                                "Flagging missing HDU @ ts={}, cc={}",
440                                timestep_idx, coarse_chan_idx
441                            );
442                            flag_array.fill(true);
443                        }
444                        Err(e) => return Err(e),
445                    }
446                }
447                Ok(())
448            })?;
449
450        Ok(())
451    }
452}
453
454#[cfg(test)]
455#[cfg(feature = "mwalib")]
456mod tests {
457    use approx::assert_abs_diff_eq;
458
459    use crate::Complex;
460
461    use super::*;
462
463    pub fn get_mwax_context() -> CorrelatorContext {
464        CorrelatorContext::new(
465            "tests/data/1297526432_mwax/1297526432.metafits",
466            &[
467                "tests/data/1297526432_mwax/1297526432_20210216160014_ch117_000.fits",
468                "tests/data/1297526432_mwax/1297526432_20210216160014_ch117_001.fits",
469                "tests/data/1297526432_mwax/1297526432_20210216160014_ch118_000.fits",
470                "tests/data/1297526432_mwax/1297526432_20210216160014_ch118_001.fits",
471            ],
472        )
473        .unwrap()
474    }
475
476    pub fn get_mwa_legacy_context() -> CorrelatorContext {
477        CorrelatorContext::new(
478            "tests/data/1196175296_mwa_ord/1196175296.metafits",
479            &[
480                "tests/data/1196175296_mwa_ord/1196175296_20171201145440_gpubox01_00.fits",
481                "tests/data/1196175296_mwa_ord/1196175296_20171201145440_gpubox02_00.fits",
482                "tests/data/1196175296_mwa_ord/1196175296_20171201145540_gpubox01_01.fits",
483                "tests/data/1196175296_mwa_ord/1196175296_20171201145540_gpubox02_01.fits",
484            ],
485        )
486        .unwrap()
487    }
488
489    /// Get a dummy MWA Ord `corr_ctx` with multiple holes in the data
490    ///
491    /// The gpubox (batch, hdu) tuples look like this:
492    /// - ts is according to [`marlu::mwalib::correlatorContext`]
493    ///
494    /// |                   | ts=0   | 1      | 2      | 3      | 4      |
495    /// | ----------------- | ------ | ------ | ------ | ------ | ------ |
496    /// | gpubox=00         | (0, 0) | (0, 1) | .      | (1, 0) | .      |
497    /// | 01                | .      | (0, 0) | (0, 1) | (1, 0) | (1, 1) |
498    pub fn get_mwa_dodgy_context() -> CorrelatorContext {
499        CorrelatorContext::new(
500            "tests/data/1196175296_mwa_ord/1196175296.metafits",
501            &[
502                "tests/data/1196175296_mwa_ord/adjusted_-1/1196175296_20171201145440_gpubox01_00.fits",
503                "tests/data/1196175296_mwa_ord/limited_1/1196175296_20171201145540_gpubox01_01.fits",
504                "tests/data/1196175296_mwa_ord/1196175296_20171201145440_gpubox02_00.fits",
505                "tests/data/1196175296_mwa_ord/1196175296_20171201145540_gpubox02_01.fits",
506            ]
507        ).unwrap()
508    }
509
510    #[test]
511    #[allow(clippy::unnecessary_cast)]
512    /// We expect coarse channel 0 ( fine channels 0,1 ) to be the same as in `get_mwa_ord_context`,
513    /// but coarse channel 0 (fine channels 2, 3 ) should be shifted.
514    fn test_read_mwalib_mwax_flags_missing_hdus() {
515        let corr_ctx = get_mwa_dodgy_context();
516        let vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
517        // Create a blank array to store flags and visibilities
518        let fine_chans_per_coarse = corr_ctx.metafits_context.num_corr_fine_chans_per_coarse;
519        let mut flag_array = vis_sel.allocate_flags(fine_chans_per_coarse).unwrap();
520        let mut jones_array = vis_sel.allocate_jones(fine_chans_per_coarse).unwrap();
521        // read visibilities out of the gpubox files
522        vis_sel
523            .read_mwalib(&corr_ctx, jones_array.view_mut(), flag_array.view_mut())
524            .unwrap();
525
526        // ts 0, chan 0, baseline 0
527        assert_abs_diff_eq!(
528            jones_array[(0, 0, 0)],
529            Jones::from([
530                Complex::new(0x10c5be as f32, -0x10c5bf as f32),
531                Complex::new(0x10c5ae as f32, 0x10c5af as f32),
532                Complex::new(0x10c5ae as f32, -0x10c5af as f32),
533                Complex::new(0x10bec6 as f32, -0x10bec7 as f32),
534            ])
535        );
536        // ts 1, chan 0, baseline 0
537        assert_abs_diff_eq!(
538            jones_array[(1, 0, 0)],
539            Jones::from([
540                Complex::new(0x14c5be as f32, -0x14c5bf as f32),
541                Complex::new(0x14c5ae as f32, 0x14c5af as f32),
542                Complex::new(0x14c5ae as f32, -0x14c5af as f32),
543                Complex::new(0x14bec6 as f32, -0x14bec7 as f32),
544            ])
545        );
546        // ts 2, chan 0, baseline 0
547        assert_abs_diff_eq!(
548            jones_array[(2, 0, 0)],
549            Jones::from([
550                Complex::new(0x18c5be as f32, -0x18c5bf as f32),
551                Complex::new(0x18c5ae as f32, 0x18c5af as f32),
552                Complex::new(0x18c5ae as f32, -0x18c5af as f32),
553                Complex::new(0x18bec6 as f32, -0x18bec7 as f32),
554            ])
555        );
556        // ts 3, chan 0, baseline 0
557        assert_abs_diff_eq!(
558            jones_array[(3, 0, 0)],
559            Jones::from([
560                Complex::new(0x1cc5be as f32, -0x1cc5bf as f32),
561                Complex::new(0x1cc5ae as f32, 0x1cc5af as f32),
562                Complex::new(0x1cc5ae as f32, -0x1cc5af as f32),
563                Complex::new(0x1cbec6 as f32, -0x1cbec7 as f32),
564            ])
565        );
566
567        // Fine channel 2 is in the modified coarse channel, so should have drifted
568
569        // ts 0, chan 2, baseline 0
570        assert_abs_diff_eq!(
571            jones_array[(0, 2, 0)],
572            Jones::from([
573                Complex::new(0x04c5be as f32, -0x04c5bf as f32),
574                Complex::new(0x04c5ae as f32, 0x04c5af as f32),
575                Complex::new(0x04c5ae as f32, -0x04c5af as f32),
576                Complex::new(0x04bec6 as f32, -0x04bec7 as f32),
577            ])
578        );
579        assert!(!flag_array[(0, 2, 0)]);
580        // ts 1, chan 2, baseline 0
581        assert_abs_diff_eq!(jones_array[(1, 2, 0)], Jones::<f32>::default());
582        assert!(flag_array[(1, 2, 0)]);
583        // ts 2, chan 2, baseline 0 - unchanged
584        assert_abs_diff_eq!(
585            jones_array[(2, 2, 0)],
586            Jones::from([
587                Complex::new(0x08c5be as f32, -0x08c5bf as f32),
588                Complex::new(0x08c5ae as f32, 0x08c5af as f32),
589                Complex::new(0x08c5ae as f32, -0x08c5af as f32),
590                Complex::new(0x08bec6 as f32, -0x08bec7 as f32),
591            ])
592        );
593        assert!(!flag_array[(2, 2, 0)]);
594        // ts 3, chan 2, baseline 0
595        assert_abs_diff_eq!(jones_array[(3, 2, 0)], Jones::<f32>::default());
596        assert!(flag_array[(3, 2, 0)]);
597    }
598
599    #[test]
600    fn test_read_mwalib_mwax() {
601        let corr_ctx = get_mwax_context();
602        let vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
603        // Create a blank array to store flags and visibilities
604        let fine_chans_per_coarse = corr_ctx.metafits_context.num_corr_fine_chans_per_coarse;
605        let mut flag_array = vis_sel.allocate_flags(fine_chans_per_coarse).unwrap();
606        let mut jones_array = vis_sel.allocate_jones(fine_chans_per_coarse).unwrap();
607        // read visibilities out of the gpubox files
608        vis_sel
609            .read_mwalib(&corr_ctx, jones_array.view_mut(), flag_array.view_mut())
610            .unwrap();
611
612        // ts 0, chan 0, baseline 0
613        assert_abs_diff_eq!(
614            jones_array[(0, 0, 0)],
615            Jones::from([
616                Complex::new(0x410000 as f32, 0x410001 as f32),
617                Complex::new(0x410002 as f32, 0x410003 as f32),
618                Complex::new(0x410004 as f32, 0x410005 as f32),
619                Complex::new(0x410006 as f32, 0x410007 as f32),
620            ])
621        );
622
623        // ts 0, chan 0 (cc 0, fc 0), baseline 1
624        assert_abs_diff_eq!(
625            jones_array[(0, 0, 1)],
626            Jones::from([
627                Complex::new(0x410010 as f32, 0x410011 as f32),
628                Complex::new(0x410012 as f32, 0x410013 as f32),
629                Complex::new(0x410014 as f32, 0x410015 as f32),
630                Complex::new(0x410016 as f32, 0x410017 as f32),
631            ])
632        );
633
634        // ts 0, chan 0, baseline 2
635        assert_abs_diff_eq!(
636            jones_array[(0, 0, 2)],
637            Jones::from([
638                Complex::new(0x410020 as f32, 0x410021 as f32),
639                Complex::new(0x410022 as f32, 0x410023 as f32),
640                Complex::new(0x410024 as f32, 0x410025 as f32),
641                Complex::new(0x410026 as f32, 0x410027 as f32),
642            ])
643        );
644
645        // ts 0, chan 1, baseline 2
646        assert_abs_diff_eq!(
647            jones_array[(0, 1, 2)],
648            Jones::from([
649                Complex::new(0x410028 as f32, 0x410029 as f32),
650                Complex::new(0x41002a as f32, 0x41002b as f32),
651                Complex::new(0x41002c as f32, 0x41002d as f32),
652                Complex::new(0x41002e as f32, 0x41002f as f32),
653            ])
654        );
655
656        // ts 1, chan 0, baseline 0
657        assert_abs_diff_eq!(
658            jones_array[(1, 0, 0)],
659            Jones::from([
660                Complex::new(0x410100 as f32, 0x410101 as f32),
661                Complex::new(0x410102 as f32, 0x410103 as f32),
662                Complex::new(0x410104 as f32, 0x410105 as f32),
663                Complex::new(0x410106 as f32, 0x410107 as f32),
664            ])
665        );
666
667        // ts 2, chan 0, baseline 0
668        assert_abs_diff_eq!(
669            jones_array[(2, 0, 0)],
670            Jones::from([
671                Complex::new(0x410200 as f32, 0x410201 as f32),
672                Complex::new(0x410202 as f32, 0x410203 as f32),
673                Complex::new(0x410204 as f32, 0x410205 as f32),
674                Complex::new(0x410206 as f32, 0x410207 as f32),
675            ])
676        );
677
678        // ts 3, chan 3 (cc 1, fc 1), baseline 1
679        assert_abs_diff_eq!(
680            jones_array[(3, 3, 1)],
681            Jones::from([
682                Complex::new(0x410718 as f32, 0x410719 as f32),
683                Complex::new(0x41071a as f32, 0x41071b as f32),
684                Complex::new(0x41071c as f32, 0x41071d as f32),
685                Complex::new(0x41071e as f32, 0x41071f as f32),
686            ])
687        );
688    }
689
690    #[test]
691    fn test_read_mwalib_bad_baseline_idxs() {
692        let corr_ctx = get_mwax_context();
693        let mut vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
694        vis_sel.baseline_idxs = vec![99999999];
695        // Create a blank array to store flags and visibilities
696        let fine_chans_per_coarse = corr_ctx.metafits_context.num_corr_fine_chans_per_coarse;
697        let mut flag_array = vis_sel.allocate_flags(fine_chans_per_coarse).unwrap();
698        let mut jones_array = vis_sel.allocate_jones(fine_chans_per_coarse).unwrap();
699        // read visibilities out of the gpubox files
700        assert!(vis_sel
701            .read_mwalib(&corr_ctx, jones_array.view_mut(), flag_array.view_mut())
702            .is_err());
703    }
704
705    #[test]
706    #[allow(clippy::unnecessary_cast)]
707    fn test_read_mwalib_mwa_legacy() {
708        let corr_ctx = get_mwa_legacy_context();
709        let vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
710        // Create a blank array to store flags and visibilities
711        let fine_chans_per_coarse = corr_ctx.metafits_context.num_corr_fine_chans_per_coarse;
712        let mut flag_array = vis_sel.allocate_flags(fine_chans_per_coarse).unwrap();
713        let mut jones_array = vis_sel.allocate_jones(fine_chans_per_coarse).unwrap();
714        // read visibilities out of the gpubox files
715        vis_sel
716            .read_mwalib(&corr_ctx, jones_array.view_mut(), flag_array.view_mut())
717            .unwrap();
718
719        // ts 0, chan 0, baseline 0
720        assert_abs_diff_eq!(
721            jones_array[(0, 0, 0)],
722            Jones::from([
723                Complex::new(0x10c5be as f32, -0x10c5bf as f32),
724                Complex::new(0x10c5ae as f32, 0x10c5af as f32),
725                Complex::new(0x10c5ae as f32, -0x10c5af as f32),
726                Complex::new(0x10bec6 as f32, -0x10bec7 as f32),
727            ])
728        );
729        // ts 1, chan 0, baseline 0
730        assert_abs_diff_eq!(
731            jones_array[(1, 0, 0)],
732            Jones::from([
733                Complex::new(0x14c5be as f32, -0x14c5bf as f32),
734                Complex::new(0x14c5ae as f32, 0x14c5af as f32),
735                Complex::new(0x14c5ae as f32, -0x14c5af as f32),
736                Complex::new(0x14bec6 as f32, -0x14bec7 as f32),
737            ])
738        );
739        // ts 2, chan 0, baseline 0
740        assert_abs_diff_eq!(
741            jones_array[(2, 0, 0)],
742            Jones::from([
743                Complex::new(0x18c5be as f32, -0x18c5bf as f32),
744                Complex::new(0x18c5ae as f32, 0x18c5af as f32),
745                Complex::new(0x18c5ae as f32, -0x18c5af as f32),
746                Complex::new(0x18bec6 as f32, -0x18bec7 as f32),
747            ])
748        );
749        // ts 3, chan 0, baseline 0
750        assert_abs_diff_eq!(
751            jones_array[(3, 0, 0)],
752            Jones::from([
753                Complex::new(0x1cc5be as f32, -0x1cc5bf as f32),
754                Complex::new(0x1cc5ae as f32, 0x1cc5af as f32),
755                Complex::new(0x1cc5ae as f32, -0x1cc5af as f32),
756                Complex::new(0x1cbec6 as f32, -0x1cbec7 as f32),
757            ])
758        );
759
760        // ts 0, chan 0, baseline 5
761        assert_abs_diff_eq!(
762            jones_array[(0, 0, 5)],
763            Jones::from([
764                Complex::new(0x10f1ce as f32, -0x10f1cf as f32),
765                Complex::new(0x10ea26 as f32, -0x10ea27 as f32),
766                Complex::new(0x10f1be as f32, -0x10f1bf as f32),
767                Complex::new(0x10ea16 as f32, -0x10ea17 as f32),
768            ])
769        );
770        // ts 1, chan 0, baseline 5
771        assert_abs_diff_eq!(
772            jones_array[(1, 0, 5)],
773            Jones::from([
774                Complex::new(0x14f1ce as f32, -0x14f1cf as f32),
775                Complex::new(0x14ea26 as f32, -0x14ea27 as f32),
776                Complex::new(0x14f1be as f32, -0x14f1bf as f32),
777                Complex::new(0x14ea16 as f32, -0x14ea17 as f32),
778            ])
779        );
780        // ts 2, chan 0, baseline 5
781        assert_abs_diff_eq!(
782            jones_array[(2, 0, 5)],
783            Jones::from([
784                Complex::new(0x18f1ce as f32, -0x18f1cf as f32),
785                Complex::new(0x18ea26 as f32, -0x18ea27 as f32),
786                Complex::new(0x18f1be as f32, -0x18f1bf as f32),
787                Complex::new(0x18ea16 as f32, -0x18ea17 as f32),
788            ])
789        );
790        // ts 3, chan 0, baseline 5
791        assert_abs_diff_eq!(
792            jones_array[(3, 0, 5)],
793            Jones::from([
794                Complex::new(0x1cf1ce as f32, -0x1cf1cf as f32),
795                Complex::new(0x1cea26 as f32, -0x1cea27 as f32),
796                Complex::new(0x1cf1be as f32, -0x1cf1bf as f32),
797                Complex::new(0x1cea16 as f32, -0x1cea17 as f32),
798            ])
799        );
800
801        // ts 0, chan 2, baseline 0
802        assert_abs_diff_eq!(
803            jones_array[(0, 2, 0)],
804            Jones::from([
805                Complex::new(0x00c5be as f32, -0x00c5bf as f32),
806                Complex::new(0x00c5ae as f32, 0x00c5af as f32),
807                Complex::new(0x00c5ae as f32, -0x00c5af as f32),
808                Complex::new(0x00bec6 as f32, -0x00bec7 as f32),
809            ])
810        );
811        // ts 1, chan 2, baseline 0
812        assert_abs_diff_eq!(
813            jones_array[(1, 2, 0)],
814            Jones::from([
815                Complex::new(0x04c5be as f32, -0x04c5bf as f32),
816                Complex::new(0x04c5ae as f32, 0x04c5af as f32),
817                Complex::new(0x04c5ae as f32, -0x04c5af as f32),
818                Complex::new(0x04bec6 as f32, -0x04bec7 as f32),
819            ])
820        );
821        // ts 2, chan 2, baseline 0
822        assert_abs_diff_eq!(
823            jones_array[(2, 2, 0)],
824            Jones::from([
825                Complex::new(0x08c5be as f32, -0x08c5bf as f32),
826                Complex::new(0x08c5ae as f32, 0x08c5af as f32),
827                Complex::new(0x08c5ae as f32, -0x08c5af as f32),
828                Complex::new(0x08bec6 as f32, -0x08bec7 as f32),
829            ])
830        );
831        // ts 3, chan 2, baseline 0
832        assert_abs_diff_eq!(
833            jones_array[(3, 2, 0)],
834            Jones::from([
835                Complex::new(0x0cc5be as f32, -0x0cc5bf as f32),
836                Complex::new(0x0cc5ae as f32, 0x0cc5af as f32),
837                Complex::new(0x0cc5ae as f32, -0x0cc5af as f32),
838                Complex::new(0x0cbec6 as f32, -0x0cbec7 as f32),
839            ])
840        );
841        // ts 3, chan 3, baseline 5
842        assert_abs_diff_eq!(
843            jones_array[(3, 3, 5)],
844            Jones::from([
845                Complex::new(0x0df3ce as f32, -0x0df3cf as f32),
846                Complex::new(0x0dec26 as f32, -0x0dec27 as f32),
847                Complex::new(0x0df3be as f32, -0x0df3bf as f32),
848                Complex::new(0x0dec16 as f32, -0x0dec17 as f32),
849            ])
850        );
851    }
852
853    #[test]
854    fn test_retain_antennas() {
855        let corr_ctx = get_mwa_legacy_context();
856        let mut vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
857        assert_eq!(vis_sel.baseline_idxs.len(), 8256);
858
859        // remove all baselines with ant1=0 or ant2=0
860        vis_sel.retain_antennas(&corr_ctx.metafits_context, &[1, 2]);
861
862        assert_eq!(vis_sel.baseline_idxs, vec![128, 129, 255]);
863    }
864
865    #[test]
866    fn test_filter_antennas() {
867        let corr_ctx = get_mwa_legacy_context();
868        let mut vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
869        assert_eq!(vis_sel.baseline_idxs.len(), 8256);
870
871        // remove all baselines with ant1=0 or ant2=0
872        vis_sel.filter_antennas(&corr_ctx.metafits_context, &[1, 2]);
873
874        assert_eq!(vis_sel.baseline_idxs.len(), 8001);
875
876        println!("baseline_idxs: {:?}", vis_sel.baseline_idxs);
877
878        assert!(vis_sel.baseline_idxs.contains(&0));
879        assert!(!vis_sel.baseline_idxs.contains(&1));
880        assert!(!vis_sel.baseline_idxs.contains(&2));
881        assert!(vis_sel.baseline_idxs.contains(&3));
882        assert!(!vis_sel.baseline_idxs.contains(&128));
883        assert!(!vis_sel.baseline_idxs.contains(&129));
884        assert!(!vis_sel.baseline_idxs.contains(&130));
885        assert!(!vis_sel.baseline_idxs.contains(&254));
886        assert!(!vis_sel.baseline_idxs.contains(&255));
887        assert!(!vis_sel.baseline_idxs.contains(&256));
888        assert!(vis_sel.baseline_idxs.contains(&381));
889        assert!(vis_sel.baseline_idxs.contains(&8255));
890    }
891
892    #[test]
893    fn test_filter_autos() {
894        let corr_ctx = get_mwa_legacy_context();
895        let mut vis_sel = VisSelection::from_mwalib(&corr_ctx).unwrap();
896        assert_eq!(vis_sel.baseline_idxs.len(), 8256);
897
898        vis_sel.filter_autos(&corr_ctx.metafits_context);
899
900        assert_eq!(vis_sel.baseline_idxs.len(), 8128);
901        assert!(!vis_sel.baseline_idxs.contains(&0));
902        assert!(vis_sel.baseline_idxs.contains(&1));
903        assert!(vis_sel.baseline_idxs.contains(&2));
904        assert!(vis_sel.baseline_idxs.contains(&3));
905        assert!(!vis_sel.baseline_idxs.contains(&128));
906        assert!(vis_sel.baseline_idxs.contains(&129));
907        assert!(vis_sel.baseline_idxs.contains(&130));
908        assert!(vis_sel.baseline_idxs.contains(&254));
909        assert!(!vis_sel.baseline_idxs.contains(&255));
910        assert!(vis_sel.baseline_idxs.contains(&256));
911        assert!(!vis_sel.baseline_idxs.contains(&381));
912        assert!(vis_sel.baseline_idxs.contains(&8127));
913        assert!(!vis_sel.baseline_idxs.contains(&8255));
914    }
915}