0

Similar question was asked before, but asked in an ambigous way and using a different code. My problem: I want to make an exact copy of a .fits-file header into a new file. (I need to process a fits file in way, that I change the data, keep the header the same and save the result in a new file). Here a short example code, just demonstrating the tools I use and the discrepancy I arrive at:

data_old, header_old = fits.getdata("input_file.fits", header=True)
fits.writeto('output_file.fits', data_old, header_old, overwrite=True)

I would expect now that the the files are exact copies (headers and data of both being same). But if I check for difference, e.g. in this way -

fits.printdiff("input_file.fits", "output_file.fits")

I see that the two files are not exact copies of each other. The report says:

...
Files contain different numbers of HDUs:
 a: 3
 b: 2

Primary HDU:

   Headers contain differences:
     Headers have different number of cards:
      a: 54
      b: 4
...

Extension HDU 1:

   Headers contain differences:
     Keyword GCOUNT   has different comments:
...

Why is there no exact copy? How can I do an exact copy of a header (and/or the data)? Is a key forgotten? Is there an alternative simple way of copy-pasting a fits-file-header?

NeStack
  • 719
  • 6
  • 20
  • It's not exactly clear what your goal is. In the code you demonstrate, if you wanted an exact copy you could just as well copy the file. You also didn't mention how the headers were different, or how you were comparing them. Sometimes when Astropy writes out a FITS file it will reorganize things in headers, slightly, if they're not standard-compliant. But this usually mostly cosmetic (e.g. the relative ordering of keywords). – Iguananaut Apr 27 '18 at 13:42
  • However, if you just want to deal with FITS headers directly without any high-level "magic", there are lower-level methods in the `Header` class for reading and writing headers directly. Depending on what your actual end goals are you're better off exploring the more direct object-oriented APIs instead of relying on the high-level functions, which are mostly just useful for quick use in interactive sessions. – Iguananaut Apr 27 '18 at 13:44
  • Thanks for answering! I added the intentions of my work. The code I presented is just demonstrative, it is not exactly what I am using, though the methods are the same. And I would like to use some "high level 'magic' " as you say, something "pythonic" that works quick and easy. Can't you suggest me some simple code for altering fits-file-data and saving in into a new file while keeping the header the same? – NeStack Apr 28 '18 at 15:31
  • 1
    It's still not clear what you mean by creating "an exact copy of a header". FITS files are organized as one or more "Header Data Units" (HDUs) that each consist of a header and optionally some data. So if some HDU has both, it only makes sense to copy the entire HDU, not just the header. – Iguananaut Apr 28 '18 at 15:59
  • 1
    Now, when you use a high-level function like `getdata()` it assumes you actually want some data array out of the file. This might actually skip the entire first HDU, as it's common to have a PRIMARY HDU that contains only a header but no data, so `getdata()` skips that and finds the actual data in the second HDU. At the end of the day all it returns is one data array and one header, whereas a FITS file could contain many such structures, so it stands to reason that when you use `fits.getdata()` followed by `fits.writeto()` much of that structure is not preserved. – Iguananaut Apr 28 '18 at 16:05
  • That's at least in part what's meant by the [admonition in the docs](http://docs.astropy.org/en/stable/io/fits/index.html#convenience-functions): The "convenience" functions are entirely stateless, and don't necessarily know anything about each other between calls. – Iguananaut Apr 28 '18 at 16:06
  • Now I understand. I didn't know previously that there could be multiple HDUs in a single fits-file. With my code I was just copying and writting into a new file just the data and header of HDU[1] (default for .getdata) and therefore not copying the complete fits-file. – NeStack Apr 30 '18 at 15:52

1 Answers1

1

If you just want to update the data array in an existing file while preserving the rest of the structure, have you tried the update function?

The only issue with that is it doesn't appear to have an option to write to a new file rather than update the existing file (maybe it should have this option). However, you can still use it by first copying the existing file, and then updating the copy.

Alternatively, you can do things more directly using the object-oriented API. Something like:

with fits.open(filename) as hdu_list:
    hdu = hdu_list[<name or index of the HDU to update>]
    hdu.data = <new ndarray>
    # or hdu.data[<some index>] = <some value> i.e. just directly modify the existing array
    hdu.writeto('updated.fits')  # to write just that HDU to a new file, or
    # hdu_list.writeto('updated.fits')  # to write all HDUs, including the updated one, to a new file

There's nothing not "pythonic" about this :)

Iguananaut
  • 15,675
  • 4
  • 43
  • 50
  • Indeed, as you said I found my solution by making a copy of the file I used and than aplied the update-method on it! – NeStack Apr 30 '18 at 16:02