Writing FITS files

Three builder functions convert numpy / Python data into HDU specs:

Hand the resulting list to fitsy.write().

"""Write a new FITS file: image + binary table.

Run from the repo root:

    python examples/python/writing_files.py
"""

import os
import tempfile

import fitsy
import numpy as np

img = np.random.default_rng(0).normal(size=(64, 64)).astype("f4")
tbl = {
    "RA": np.array([10.0, 11.0, 12.0]),
    "DEC": np.array([-5.0, -5.5, -6.0]),
    "NAME": ["a", "bb", "ccc"],
}

with tempfile.TemporaryDirectory() as td:
    path = os.path.join(td, "out.fits")
    fitsy.write(
        path,
        [
            fitsy.image(img, header={"OBJECT": "noise"}),
            fitsy.bintable(tbl, extname="CATALOG"),
        ],
    )

    # Round-trip: read it back and check.
    with fitsy.open(path) as f:
        print("HDU count:", len(f))
        print("primary axes:", f[0].axes)
        print("table columns:", f[1].column_names)

By default fitsy.write() refuses to clobber an existing file. Pass overwrite=True to replace it.

Headers

The header argument to fitsy.image() accepts a plain dict. Values may be scalars or (value, comment) tuples.

Pixel scaling on write (BSCALE / BZERO)

fitsy.image() writes the supplied numpy array verbatim: the buffer’s dtype determines BITPIX and the pixel bytes are emitted without further transformation. fitsy does not invert BSCALE / BZERO from physical units back to a raw integer representation. This matches the behavior of astropy.io.fits.

Two consequences:

  • If the input header carries BSCALE and BZERO, the values in the array are interpreted on read as physical = BZERO + BSCALE * raw. Writing them back without changing the keywords means the new file’s “raw” pixels are your current array, not the original raw integers.

  • To round-trip a scaled integer image (e.g. one that was opened with hdu.data returning floats), drop the BSCALE / BZERO cards from the new header and write the data as the intended dtype, or apply the inverse transform yourself before building the HDU.

Unsigned integer images (uint16, uint32, uint64) are the one exception: both fitsy.image() and the Rust ImageBuilder::from_u16 / from_u32 / from_u64 constructors offset-encode pixels into the matching signed BITPIX and emit BSCALE = 1 with the standard BZERO offset automatically (FITS Standard Sec.4.4.2.5). Round-tripping uint* arrays through fitsy is lossless.

ASCII tables

For text-formatted TABLE extensions, use fitsy.ascii_table(). formats overrides the per-column TFORM; tnulls supplies a string sentinel for None cells in integer columns.