Skip to content

virgil.oifits

Read and write OIFITS files with astropy.io.fits alone. This is the maintained OIFITS path; OIData uses read_oifits whenever it is given a file path or an opened HDUList (including pyoifits objects).

The reader supports:

  • several wavelength channels (every baseline × channel becomes one sample);
  • several OI_VIS2/OI_VIS/OI_T3 tables and epochs, matched by ARRNAME, INSNAME (or another INSNAME of the same array with identical wavelengths), station indices and MJD;
  • FLAG columns and non-finite values, which are left out of the observables;
  • several targets, chosen with target=;
  • absolute phases from OI_VIS VISPHI when there is no OI_T3.

Closure-phase triangles (a, b, c) find their baselines (a, b), (b, c) and (a, c) in the visibility table with the same ARRNAME and INSNAME, or else in one of the same array with identical wavelengths (the standard does not require T3 and V² tables to share an INSNAME; station numbers belong to an array, so another ARRNAME is never used). A baseline stored reversed is used as the conjugate. A baseline stored in neither orientation (some MIRC-X and ESO phase-3 files omit a baseline's V²) is placed at the OI_T3 row's own (u, v) as a flagged sample, with one warning per file giving the number of such legs.

Functions

Read and write OIFITS files using only astropy.io.fits.

This is the maintained OIFITS path of virgil.

  • read_oifits turns an OIFITS file into the record that virgil.oidata.OIData is built from. It accepts a file path or any astropy HDUList, including pyoifits objects (which subclass it). It handles several wavelength channels, several OIFITS tables, several epochs, and FLAG columns.
  • write_oifits writes a dictionary of OIFITS tables (the layout used by the legacy virgil.legacy.oifits_implaneia writer) to an OIFITS2 file.

Each (baseline, wavelength) sample becomes one element of the flat u, v and wavel arrays of the record. Samples are ordered by table, then row, then wavelength channel. Closure phases index into these samples, so a multi-wavelength closure phase is formed from visibilities at its own wavelength.

read_oifits(source, target=None, insname=None, frame_mjd='mean', extras=())

Read an OIFITS file into a record for OIData.

Parameters:

Name Type Description Default
source str, os.PathLike, astropy.io.fits.HDUList, or a list of them

File path, or an opened file (e.g. from astropy.io.fits.open or pyoifits.open). A list or tuple of files is read file by file and concatenated into one record, in the order given.

required
target str or int

Target to keep, by OI_TARGET name or TARGET_ID. Required when a file contains data on more than one target. With several files it must be the name, since TARGET_ID numbering is per file.

None
insname str or sequence of str

Keep only the tables (OI_WAVELENGTH, OI_VIS, OI_VIS2, OI_T3, OI_FLUX) with this INSNAME, or one of these. A GRAVITY product holds fringe-tracker tables (GRAVITY_FT, a few low-resolution channels) beside the science channel (GRAVITY_SC, or GRAVITY_SC_P1/_P2 in split polarisation). These are different measurements of the same baselines and must not be merged: reading such a file without insname raises an error. The two polarisations of the science channel are independent measurements and may be read together.

None
frame_mjd (mean, row)

The time given to each sample. "mean" (the default) gives every sample of a frame (one exposure; see Notes) the mean MJD of the frame's rows; "row" keeps each row's own MJD.

"mean"
extras sequence of str

Further observables to read beside the visibilities and phases (none by default, so existing analyses are unchanged); see virgil.observables:

  • "flux": OI_FLUX as a spectrum known up to a grey scale (FLUXDATA, or GRAVITY's FLUX);
  • "nflux": OI_FLUX as a spectrum normalized to its continuum (choose one of "flux" and "nflux");
  • "t3amp": the triple amplitudes T3AMP of OI_T3;
  • "visamp": OI_VIS VISAMP beside OI_VIS2, as the table's AMPTYP declares it ('absolute': |V|; 'correlated flux': |V| times the total flux, up to a grey scale);
  • "visphi": OI_VIS VISPHI as a differential phase, whatever its PHITYP, beside any closure phases.
()

Returns:

Type Description
dict

Record with flat per-sample arrays u, v (metres) and wavel (metres; a single element if every sample shares one wavelength), the visibility observables vis/d_vis with a boolean vis_flag (True = bad), the phases phi/d_phi in radians with phi_flag, the closure-phase indices i_cps1/i_cps2/i_cps3 (or None for absolute phases), the flags v2_flag and cp_flag, and per sample the time mjd (days, float64) and the integer frame. Each requested extra observable adds a dictionary under its name ("flux" or "nflux", "t3amp", "visamp", "visphi").

Notes

Squared visibilities (OI_VIS2) are preferred over amplitudes (OI_VIS), and closure phases (OI_T3) over absolute phases (OI_VIS VISPHI). VISAMP is read only when its table's AMPTYP is 'absolute', and VISPHI only when its PHITYP is 'absolute'; a missing keyword counts as 'absolute', as in OIFITS1. Differential amplitudes and phases, and correlated fluxes, raise a ValueError. Nothing in the standard marks OI_VIS2 VIS2DATA that holds squared correlated flux rather than squared visibility, as in MATISSE products reduced with corrFlux=TRUE; such data are read as visibilities, so calibrate them (e.g. with virgil-vlti) before fitting. A file with only OI_T3 gives closure phases alone: its baselines come from the triangle coordinates, and vis is empty. A file with neither OI_T3 nor VISPHI gives visibilities alone: phi is empty, and files with and without phases cannot be read together. Samples are flagged when their FLAG is set or their value or uncertainty is not finite.

Each closure-phase triangle (a, b, c) is matched to the visibility baselines (a, b), (b, c) and (a, c) with the same INSNAME (or, failing that, any INSNAME with identical wavelengths) and nearest MJD and TIME, within its own file. The MJDs (and TIMEs) must agree to within twice the longest INT_TIME in the visibility table (or about 9 seconds if there is none), since pipelines such as GRAVITY's average different frames of one exposure for each table. A leg with no such visibility row, or stored only reversed as (b, a), gets an extra flagged sample at its own (u, v) from the OI_T3 row (U1COORD...V2COORD), as the standard makes OI_T3 self-contained; for a reversed leg the model's visibility there is the conjugate of the stored one. A file with legs that have no visibility row in either orientation (V² coverage incomplete, as in some MIRC-X and ESO phase-3 files) gives one warning with their number.

A frame is one exposure of one instrument: the baselines that closure phases tie together, together with any rows of the same INSNAME at the same MJD and TIME (each within about 9 seconds). Both are compared because some OIFITS v1 writers (e.g. OYSTER) give a night one MJD and put each snapshot in TIME; closure phases are correlated only within a snapshot. Frames are numbered within the record, so different files never share one.

All files in a list must hold the same kinds of observable (squared visibilities or amplitudes; closure or absolute phases; the same extras).

With extras, a VISPHI or VISAMP row is matched to the visibility sample of its baseline and time as closure-phase legs are (a reversed baseline negates the phase). VISPHI is then never read as an absolute phase: without OI_T3 the phase block is empty. A file whose OI_VIS holds correlated fluxes and no OI_VIS2 gives flagged visibility samples, with the amplitudes in "visamp".

write_oifits(tables, filename, overwrite=True)

Write a dictionary of OIFITS tables to an OIFITS2 file.

Parameters:

Name Type Description Default
tables dict

Mapping of table name to a dict of columns, in the layout used by virgil.legacy.oifits_implaneia.save:

  • "OI_WAVELENGTH" (required): EFF_WAVE and EFF_BAND in metres, one value per channel.
  • At least one of "OI_VIS2", "OI_VIS" and "OI_T3", and optionally "OI_FLUX" (FLUXDATA, FLUXERR and a STA_INDEX per row). Data columns (e.g. VIS2DATA, T3PHI) have shape (nrow,) or (nrow, nwave); phases are in degrees. T3AMP and T3AMPERR may be omitted (they are then NaN). The keywords AMPTYP and PHITYP of OI_VIS (default 'absolute') and CALSTAT of OI_FLUX (default 'C') may be given as entries of their table. TARGET_ID, TIME, MJD and INT_TIME may be scalars. FLAG is optional (default: nothing flagged).
  • "OI_TARGET" and "OI_ARRAY" (optional): columns of those tables. When omitted, a single target named from info and an array built from info["STAXY"] (or placeholder stations) are written.
  • "info" (optional): primary-header keywords (e.g. OBJECT, TELESCOP, INSTRUME, DATE-OBS) plus INSNAME, ARRNAME, TARGET, MJD and STAXY defaults.
required
filename str or PathLike

Output path. Missing parent directories are created.

required
overwrite bool

Replace an existing file (default True).

True

Returns:

Type Description
Path

The path written.

Notes

Unlike the legacy writer, this does not query SIMBAD: target coordinates are taken from tables["OI_TARGET"] or info (RA/DEC in degrees) and are zero otherwise.

build_hdulist(tables)

Build the OIFITS2 HDUList that :func:write_oifits writes.

Useful for adding non-standard columns or keywords before writing.