This is a Python module for reading and writing OIFITS files. The only file you
need is oifits.py
; everything else is supplementary.
Note, OIFITS2 support is currently in development, although already mostly working for both reading and writing OIFITS2 files. Please open an issue or contact me if you find any bugs.
The module was tested with Astropy 4.2 and numpy 1.19.2 under Python 3.8.5. Earlier versions will probably work, too.
For some example functions which make use of the module, see oitools.py
in the
contrib directory. These are undocumented, messy examples only and may not work;
you should probably write your own.
Also included in the contrib directory is a sample array file for the VLTI.
This can be viewed using the plot_array function in oitools.py
:
import matplotlib.pylab as plt
import oifits
from oitools import plot_array
oidata = oifits.open('VLTI-array.fits')
plot_array(oidata.array['VLTI'])
plt.show()
If you discover bugs, have issues with the code or suggestions, please feel free to open an issue on Github or contact me via email at pboley@gmail.com.
The module is self-contained in a single file, oifits.py
. To use it, just put
it in the working directory of your project, or in your
PYTHONPATH
(recommended).
To open an existing OIFITS file, use the oifits.open(filename)
function, where
filename
can be either a filename or HDUList object. This will return an
oifits object with the following members (any of which can be empty
dictionaries or numpy arrays):
-
array
: a dictionary of interferometric arrays, as defined by the OI_ARRAY tables. The dictionary key is the name of the array (ARRNAME). -
corr
: a dictionary of correlation matrices, as defined by the OI_CORR table. The dictionary key is the name of the table (CORRNAME). -
header
: the header from the primary HDU of the file. -
target
: a numpy array of targets, as defined by the rows of the OI_TARGET table. -
wavelength
: a dictionary of wavelength tables (OI_WAVELENGTH). The dictionary key is the name of the instrument/settings (INSNAME). -
vis
,vis2
,t3
andflux
: numpy arrays of objects containing all the measurement information. Each list member corresponds to a row in an OI_VIS/OI_VIS2/OI_T3/OI_FLUX table. -
inspol
: a numpy array of objects containing the instrumental polarization information, as defined by the rows of the OI_INSPOL table.
A summary of the information in the oifits object can be obtained by using the info() method:
import oifits
oifitsobj = oifits.open('foo.fits')
oifitsobj.info()
The info()
method can also be used from many of the objects contained within
the oifits object itself, e.g. oifitsobj.array['VLTI'].info()
or
oifitsobj.vis[0].info()
.
The individual measurements can be referred to by accessing elements of the
vis
, vis2
, t3
and/or flux
numpy arrays. These are themselves objects,
which contain the measurement data, as well as references to the corresponding
wavelength tables, intererometry stations, etc.
For example, if your OIFITS file contains OI_VIS measurements, the visibility
amplitude can be found in oifitsobj.vis[0].visamp
, while the wavelengths
corresponding to the measurement can be found in
oifitsobj.vis[0].visamp.wavelength.eff_wave
.
The OI_VIS/OI_VIS2/OI_T3/OI_FLUX classes use numpy masked arrays for
convenience, where the mask is defined via the flag
member of these classes.
Beware of the following subtlety: the array data are accessed via (for
example) OI_VIS.visamp
; however, OI_VIS.visamp
itself is just a method which
constructs (on the fly) a masked array from OI_VIS._visamp
, which is where the
data are actually stored. This is done transparently, and the data can be
accessed and modified transparently via the "visamp" hidden attribute. The same
goes for correlated fluxes, differential/closure phases, triple products, total
flux measurements, etc. See the notes on the individual classes for a list of
all the "hidden" attributes.
The module provides a simple mechanism for combining multiple oifits objects,
achieved by using the +
operator on two oifits objects: result = a + b
.
This requires, however, that the information contained within all the support
tables (OI_ARRAY
, OI_WAVELENGTH
, OI_TARGET
, etc.) is either identical,
or at least not mutually exclusive.
This behavior can be somewhat relaxed by setting the matchtargetbyname
and/or
matchstationbyname
in the top level of the module to True
, in which cases
targets or interferometric stations with identical names will be considered as
identical (the values actually used in result = a + b
are taken from a
).
No mechanism is provided for the case where OI_WAVELENGTH
tables have
identical names (INSNAME), but differing contents, as this requires
reinterpolating your data and is beyond the scope of this module.
An example of creating an OIFITS file from scratch, which simulates using the
VLTI and MIDI (now decomissioned) for observations at random hour angles of the
calibrator star HD 148478, described as a uniform disk, can be found in
sample.py
in the contrib directory.
Once you have assembled your OIFITS object, you can check if it is consistent
or valid by using the isconsistent()
and isvalid()
methods of the newly
created object, respectively. Consistency checks whether the new object is
entirely self-contained, and does not refer to any (sub)objects (e.g. wavelength
stations, array tables, telescopes or interferometric stations) which are not
contained in the object itself. Validity checks whether the information
contained within the oifits object actually conforms to the OIFITS standard. A
file can be consistent without being valid. An example of this is the
VLTI-array.fits
file provided here, which does not conform to the OIFITS
standard in that it does not contain any measurements. However, because the
OI_ARRAY definition is somewhat complicated and the arrays themselves don't
really change much, it can be useful to save this information separately and
reuse it as needed.
If your OIFITS object is at least consistent, it can be written to a FITS file
using the save()
method.
OIFITS files produced by the current version of the GRAVITY pipeline are known
to be broken: the OI_ARRAY
tables are listed as conforming to OIFITS2
(OI_REVN=2
), although they are missing the FOV and FOVTYPE columns.
Additionally, the OI_FLUX
tables contain the flux measurements in the FLUX
column, instead of FLUXDATA. To fix the first problem, you can edit
the header of your file on-the-fly before trying to load it:
from astropy.io import fits
import oifits
hdulist = fits.open('foo.fits')
hdulist['OI_ARRAY'].header['OI_REVN'] = 1
oifitsobj = oifits.open(hdulist)
The problem with the FLUX/FLUXDATA error is a little more tricky, so the module handles it gracefully and warns you. Other errors in conforming to the OIFITS standards (e.g. DATE-OBS containing a date and time, instead of just a date; both MATISSE and GRAVITY make this mistake) are handled gracefully when possible, however if you see warnings I would encourage you to open a bug report with the software generating broken files.
The API documentation is contained within the module itself in the form of
Python docstrings, which can be accessed using Python's help()
function.
Besides this, you should also refer to the appropriate tables in the documents
defining the standard (see references), as the names of many
function arguments, tables, etc. are taken directly from there and not
documented explicitly.
OIFITS2 capabilities were implemented with the financial support of grant 18-72-10132 of the Russian Science Foundation.
-
A Data Exchange Standard for Optical (Visible/IR) Interferometry; Pauls et al., 2005
-
OIFITS 2: the 2nd version of the data exchange standard for optical interferometry; Duvet et al., 2017