Skip to content

Commit 4006294

Browse files
committed
Allow Aman's dipole amps to be ingested.
Aman Chokshi is working with adjusting MWA dipole gains/amps, and this commit uses these gains if available from a metafits file ("DipAmps" column in the second HDU's table).
1 parent 5698b62 commit 4006294

File tree

12 files changed

+281
-95
lines changed

12 files changed

+281
-95
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
88

99
## [0.3.0] - Unreleased
1010
### Added
11+
- Support for the "DipAmps" column in a metafits file. This allows users to
12+
control dipole gains in beam code.
1113
- Support for averaging incoming visibilities in time and frequency *before*
1214
doing any work on them.
1315
- When writing out visibilities, it is now possible to write out the smallest

mdbook/src/defs/mwa/dead_dipoles.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ account for dead dipoles if possible.
1010
Beam responses are generated with
1111
[`hyperbeam`](https://github.com/MWATelescope/mwa_hyperbeam) and dead dipole
1212
information is encoded as a "dipole gain" of 1 ("alive") or 0 ("dead"). It is
13-
possible to supply other values for dipole gains, although at the time of
14-
writing `hyperdrive` only uses ones or zeros.
13+
possible to supply other values for dipole gains with a "DipAmps" column; see
14+
the [metafits page](metafits.md).
1515

1616
For the relevant functions, dead dipole information can be ignored by supplying
1717
a flag `--unity-dipole-gains`. This sets all dipole gains to 1.

mdbook/src/defs/mwa/metafits.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,12 @@ that is required to be present by
2929
[`mwalib`](https://github.com/MWATelescope/mwalib), which is used by
3030
`hyperdrive` when reading metafits files.
3131
~~~
32+
33+
## Controlling dipole gains
34+
35+
If the "TILEDATA" HDU of a metafits contains a "DipAmps" column, each row
36+
containing 16 double-precision values for bowties in the M&C order, these are
37+
used as the dipole gains in beam calculations. If the "DipAmps" column isn't
38+
available, the default behaviour is to use gains of 1.0 for all dipoles, except
39+
those that have delays of 32 in the "Delays" column (they will have a gain of
40+
0.0, and are considered [dead](dead_dipoles.md)).

src/cli/common/beam/mod.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,31 @@ impl BeamArgs {
177177
dipole_gains
178178
};
179179
if let Some(dipole_gains) = dipole_gains.as_ref() {
180-
let num_tiles_with_dead_dipoles = dipole_gains
181-
.outer_iter()
182-
.filter(|tile_dipole_gains| {
183-
tile_dipole_gains.iter().any(|g| g.abs() < f64::EPSILON)
184-
})
185-
.count();
186-
printer.push_line(
187-
format!(
188-
"Using dead dipole information ({num_tiles_with_dead_dipoles} tiles affected)"
189-
)
190-
.into(),
191-
);
180+
trace!("Attempting to use dipole gains:");
181+
for row in dipole_gains.outer_iter() {
182+
trace!("{row}");
183+
}
184+
185+
// Currently, the only way to have dipole gains other than
186+
// zero or one is by using Aman's "DipAmps" metafits column.
187+
if dipole_gains.iter().any(|&g| g != 0.0 && g != 1.0) {
188+
printer.push_line(
189+
"Using Aman's 'DipAmps' dipole gains from the metafits".into(),
190+
);
191+
} else {
192+
let num_tiles_with_dead_dipoles = dipole_gains
193+
.outer_iter()
194+
.filter(|tile_dipole_gains| {
195+
tile_dipole_gains.iter().any(|g| g.abs() < f64::EPSILON)
196+
})
197+
.count();
198+
printer.push_line(
199+
format!(
200+
"Using dead dipole information ({num_tiles_with_dead_dipoles} tiles affected)"
201+
)
202+
.into(),
203+
);
204+
}
192205
} else {
193206
// If we don't have dipole gains, we must assume all dipoles
194207
// are "alive". But, if any dipole delays are 32, then the

src/cli/common/beam/tests.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

5+
use approx::assert_abs_diff_eq;
56
use ndarray::array;
67

78
use super::BeamArgs;
@@ -91,3 +92,23 @@ fn test_unity_dipole_gains() {
9192
// Verify that there are no dead dipoles in the delays.
9293
assert!(beam.get_dipole_delays().unwrap().iter().all(|d| *d != 32));
9394
}
95+
96+
#[test]
97+
fn test_aman_dipole_gains() {
98+
let f = |metafits| {
99+
let metafits = mwalib::MetafitsContext::new(metafits, None).unwrap();
100+
let delays = crate::metafits::get_dipole_delays(&metafits);
101+
let gains = crate::metafits::get_dipole_gains(&metafits);
102+
(delays, gains)
103+
};
104+
105+
let (vanilla_delays, vanilla_gains) = f("test_files/1120082744/1120082744.metafits");
106+
let (dipamps_delays, dipamps_gains) = f("test_files/1120082744/1120082744_DipAmps.metafits");
107+
assert_eq!(vanilla_delays, dipamps_delays);
108+
assert_ne!(vanilla_gains, dipamps_gains);
109+
110+
// First X dipole for Tile011
111+
assert_abs_diff_eq!(dipamps_gains[(0, 0)] as f32, 0.89985347);
112+
// First Y dipole for Tile011
113+
assert_abs_diff_eq!(dipamps_gains[(0, 16)] as f32, 0.8930142);
114+
}

src/io/read/fits/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,11 @@ pub enum FitsError {
7575
source_line: u32,
7676
source_column: u32,
7777
},
78+
79+
#[error("When attempting to read column {col_name} from HDU {hdu_num}, cfitsio gave an error: {err}")]
80+
ReadCellArray {
81+
col_name: &'static str,
82+
hdu_num: usize,
83+
err: Box<fitsio::errors::Error>,
84+
},
7885
}

src/io/read/fits/mod.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) use error::FitsError;
1111
use std::{
1212
ffi::{CStr, CString},
1313
fmt::Display,
14+
os::raw::c_char,
1415
ptr,
1516
};
1617

@@ -329,3 +330,57 @@ pub(crate) fn _fits_get_float_image_into_buffer(
329330

330331
Ok(())
331332
}
333+
334+
/// Pull out fits array-in-a-cell values; useful for e.g. STABXYZ. This function
335+
/// must be pointed at data containing f64s (i.e. TDOUBLE).
336+
#[track_caller]
337+
pub(crate) fn fits_read_cell_f64_array<const NUM_ELEM: usize>(
338+
fits_ptr: &mut fitsio::FitsFile,
339+
hdu: &fitsio::hdu::FitsHdu,
340+
col_name: &'static str,
341+
row: i64,
342+
) -> Result<[f64; NUM_ELEM], FitsError> {
343+
unsafe {
344+
// With the column name, get the column number.
345+
let mut status = 0;
346+
let mut col_num = -1;
347+
let keyword = std::ffi::CString::new(col_name).expect("CString::new failed");
348+
// ffgcno = fits_get_colnum
349+
fitsio_sys::ffgcno(
350+
fits_ptr.as_raw(),
351+
0,
352+
keyword.as_ptr() as *mut c_char,
353+
&mut col_num,
354+
&mut status,
355+
);
356+
// Check the status.
357+
fitsio::errors::check_status(status).map_err(|err| FitsError::ReadCellArray {
358+
col_name,
359+
hdu_num: hdu.number + 1,
360+
err: Box::new(err),
361+
})?;
362+
363+
// Now get the specified row from that column.
364+
let mut array = [0.0; NUM_ELEM];
365+
// ffgcv = fits_read_col
366+
fitsio_sys::ffgcv(
367+
fits_ptr.as_raw(),
368+
82, // TDOUBLE (fitsio.h)
369+
col_num,
370+
row + 1,
371+
1,
372+
NUM_ELEM.try_into().expect("not larger than i64::MAX"),
373+
std::ptr::null_mut(),
374+
array.as_mut_ptr().cast(),
375+
&mut 0,
376+
&mut status,
377+
);
378+
fitsio::errors::check_status(status).map_err(|err| FitsError::ReadCellArray {
379+
col_name,
380+
hdu_num: hdu.number + 1,
381+
err: Box::new(err),
382+
})?;
383+
384+
Ok(array)
385+
}
386+
}

src/io/read/uvfits/error.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,6 @@ pub enum UvfitsReadError {
9595
#[error("When attempting to read uvfits baseline metadata, cfitsio gave an error: {0}")]
9696
Metadata(fitsio::errors::Error),
9797

98-
#[error("When attempting to read uvfits column {col_name} from HDU {hdu_num}, cfitsio gave an error: {err}")]
99-
ReadCellArray {
100-
col_name: &'static str,
101-
hdu_num: usize,
102-
err: fitsio::errors::Error,
103-
},
104-
10598
#[error("When attempting to read uvfits row {row_num}, cfitsio gave an error: {err}")]
10699
ReadVis {
107100
row_num: usize,

src/io/read/uvfits/mod.rs

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use std::{
1717
borrow::Cow,
1818
collections::{HashMap, HashSet},
1919
num::NonZeroU16,
20-
os::raw::c_char,
2120
path::{Path, PathBuf},
2221
};
2322

@@ -40,6 +39,7 @@ use crate::{
4039
io::read::{
4140
fits::{
4241
fits_get_col, fits_get_optional_key, fits_get_required_key, fits_open, fits_open_hdu,
42+
fits_read_cell_f64_array,
4343
},
4444
VisRead, VisReadError,
4545
},
@@ -149,7 +149,7 @@ impl UvfitsReader {
149149
let mut tile_xyzs: Vec<XyzGeodetic> = Vec::with_capacity(total_num_tiles);
150150
let mut average_xyz = XyzGeocentric::default();
151151
for i in 0..total_num_tiles {
152-
let fits_xyz = read_cell_array::<3>(
152+
let fits_xyz = fits_read_cell_f64_array::<3>(
153153
&mut uvfits_fptr,
154154
&antenna_table_hdu,
155155
"STABXYZ",
@@ -1845,57 +1845,3 @@ impl Indices {
18451845
})
18461846
}
18471847
}
1848-
1849-
/// Pull out fits array-in-a-cell values; useful for e.g. STABXYZ. This function
1850-
/// assumes that the output datatype is f64, and that the fits datatype is
1851-
/// TDOUBLE, so it is not to be used generally!
1852-
fn read_cell_array<const NUM_ELEM: usize>(
1853-
fits_ptr: &mut fitsio::FitsFile,
1854-
hdu: &fitsio::hdu::FitsHdu,
1855-
col_name: &'static str,
1856-
row: i64,
1857-
) -> Result<[f64; NUM_ELEM], UvfitsReadError> {
1858-
unsafe {
1859-
// With the column name, get the column number.
1860-
let mut status = 0;
1861-
let mut col_num = -1;
1862-
let keyword = std::ffi::CString::new(col_name).expect("CString::new failed");
1863-
// ffgcno = fits_get_colnum
1864-
fitsio_sys::ffgcno(
1865-
fits_ptr.as_raw(),
1866-
0,
1867-
keyword.as_ptr() as *mut c_char,
1868-
&mut col_num,
1869-
&mut status,
1870-
);
1871-
// Check the status.
1872-
fits_check_status(status).map_err(|err| UvfitsReadError::ReadCellArray {
1873-
col_name,
1874-
hdu_num: hdu.number + 1,
1875-
err,
1876-
})?;
1877-
1878-
// Now get the specified row from that column.
1879-
let mut array = [0.0; NUM_ELEM];
1880-
// ffgcv = fits_read_col
1881-
fitsio_sys::ffgcv(
1882-
fits_ptr.as_raw(),
1883-
82, // TDOUBLE (fitsio.h)
1884-
col_num,
1885-
row + 1,
1886-
1,
1887-
NUM_ELEM.try_into().expect("not larger than i64::MAX"),
1888-
std::ptr::null_mut(),
1889-
array.as_mut_ptr().cast(),
1890-
&mut 0,
1891-
&mut status,
1892-
);
1893-
fits_check_status(status).map_err(|err| UvfitsReadError::ReadCellArray {
1894-
col_name,
1895-
hdu_num: hdu.number + 1,
1896-
err,
1897-
})?;
1898-
1899-
Ok(array)
1900-
}
1901-
}

0 commit comments

Comments
 (0)