1.. currentmodule:: astropy.io.fits 2 3.. _fits_io_verification: 4 5Verification 6************ 7 8``astropy`` has built in a flexible scheme to verify FITS data conforming to 9the FITS standard. The basic verification philosophy in ``astropy`` is to be 10tolerant with input and strict with output. 11 12When ``astropy`` reads a FITS file which does not conform to FITS standard, it 13will not raise an error and exit. It will try to make the best educated 14interpretation and only gives up when the offending data is accessed and no 15unambiguous interpretation can be reached. 16 17On the other hand, when writing to an output FITS file, the content to be 18written must be strictly compliant to the FITS standard by default. This 19default behavior can be overwritten by several other options, so the user will 20not be held up because of a minor standard violation. 21 22 23FITS Standard 24============= 25 26Since FITS standard is a "loose" standard, there are many places the violation 27can occur and to enforce them all will be almost impossible. It is not uncommon 28for major observatories to generate data products which are not 100% FITS 29compliant. Some observatories have also developed their own nonstandard 30dialect and some of these are so prevalent that they have become de facto 31standards. Examples include the long string value and the use of the CONTINUE 32card. 33 34The violation of the standard can happen at different levels of the data 35structure. ``astropy``'s verification scheme is developed on these hierarchical 36levels. Here are the three ``astropy`` verification levels: 37 381. The HDU List 39 402. Each HDU 41 423. Each Card in the HDU Header 43 44These three levels correspond to the three categories of objects: 45:class:`HDUList`, any HDU (e.g., :class:`PrimaryHDU`, :class:`ImageHDU`, etc.), 46and :class:`Card`. They are the only objects having the ``verify()`` method. 47Most other classes in `astropy.io.fits` do not have a ``verify()`` method. 48 49If ``verify()`` is called at the HDU List level, it verifies standard 50compliance at all three levels, but a call of ``verify()`` at the Card level 51will only check the compliance of that Card. Since ``astropy`` is tolerant when 52reading a FITS file, no ``verify()`` is called on input. On output, 53``verify()`` is called with the most restrictive option as the default. 54 55 56Verification Options 57==================== 58 59There are several options accepted by all verify(option) calls in ``astropy``. 60In addition, they available for the ``output_verify`` argument of the following 61methods: ``close()``, ``writeto()``, and ``flush()``. In these cases, they are 62passed to a ``verify()`` call within these methods. The available options are: 63 64**exception** 65 66This option will raise an exception if any FITS standard is violated. This is 67the default option for output (i.e., when ``writeto()``, ``close()``, or 68``flush()`` is called). If a user wants to overwrite this default on output, the 69other options listed below can be used. 70 71**warn** 72 73This option is the same as the ignore option but will send warning messages. It 74will not try to fix any FITS standard violations whether fixable or not. 75 76**ignore** 77 78This option will ignore any FITS standard violation. On output, it will write 79the HDU List content to the output FITS file, whether or not it is conforming 80to the FITS standard. 81 82The ignore option is useful in the following situations: 83 841. An input FITS file with nonstandard formatting is read and the user wants 85 to copy or write out to an output file. The nonstandard formatting will be 86 preserved in the output file. 87 882. A user wants to create a nonstandard FITS file on purpose, possibly for 89 testing or consistency. 90 91No warning message will be printed out. This is like a silent warning option 92(see below). 93 94**fix** 95 96This option will try to fix any FITS standard violations. It is not always 97possible to fix such violations. In general, there are two kinds of FITS 98standard violations: fixable and non-fixable. For example, if a keyword has a 99floating number with an exponential notation in lower case 'e' (e.g., 1.23e11) 100instead of the upper case 'E' as required by the FITS standard, it is a fixable 101violation. On the other hand, a keyword name like 'P.I.' is not fixable, since 102it will not know what to use to replace the disallowed periods. If a violation 103is fixable, this option will print out a message noting it is fixed. If it is 104not fixable, it will throw an exception. 105 106The principle behind fixing is to do no harm. For example, it is plausible to 107'fix' a Card with a keyword name like 'P.I.' by deleting it, but ``astropy`` 108will not take such action to hurt the integrity of the data. 109 110Not all fixes may be the "correct" fix, but at least ``astropy`` will try to 111make the fix in such a way that it will not throw off other FITS readers. 112 113**silentfix** 114 115Same as fix, but will not print out informative messages. This may be useful in 116a large script where the user does not want excessive harmless messages. If the 117violation is not fixable, it will still throw an exception. 118 119In addition the following combined options are available: 120 121 * **fix+ignore** 122 * **fix+warn** 123 * **fix+exception** 124 * **silentfix+ignore** 125 * **silentfix+warn** 126 * **silentfix+exception** 127 128These options combine the semantics of the basic options. For example, 129``silentfix+exception`` is actually equivalent to just ``silentfix`` in that 130fixable errors will be fixed silently, but any unfixable errors will raise an 131exception. On the other hand, ``silentfix+warn`` will issue warnings for 132unfixable errors, but will stay silent about any fixed errors. 133 134 135Verifications at Different Data Object Levels 136============================================= 137 138We will examine what ``astropy``'s verification does at the three different 139levels: 140 141 142Verification at HDUList 143----------------------- 144 145At the HDU List level, the verification is only for two simple cases: 146 1471. Verify that the first HDU in the HDU list is a primary HDU. This is a 148 fixable case. The fix is to insert a minimal primary HDU into the HDU list. 149 1502. Verify the second or later HDU in the HDU list is not a primary HDU. 151 Violation will not be fixable. 152 153 154Verification at Each HDU 155------------------------ 156 157For each HDU, the mandatory keywords, their locations in the header, and their 158values will be verified. Each FITS HDU has a fixed set of required keywords in 159a fixed order. For example, the primary HDU's header must at least have the 160following keywords: 161 162.. parsed-literal:: 163 164 SIMPLE = T / 165 BITPIX = 8 / 166 NAXIS = 0 167 168If any of the mandatory keywords are missing or in the wrong order, the fix 169option will fix them:: 170 171 >>> from astropy.io import fits 172 >>> filename = fits.util.get_testdata_filepath('verify.fits') 173 >>> hdul = fits.open(filename) 174 >>> hdul[0].header 175 SIMPLE = T / conforms to FITS standard 176 NAXIS = 0 / NUMBER OF AXES 177 BITPIX = 8 / BITS PER PIXEL 178 >>> hdul[0].verify('fix') # doctest: +SHOW_WARNINGS 179 VerifyWarning: Verification reported errors: 180 VerifyWarning: 'BITPIX' card at the wrong place (card 2). 181 Fixed by moving it to the right place (card 1). 182 VerifyWarning: Note: astropy.io.fits uses zero-based indexing. 183 >>> hdul[0].header # voila! 184 SIMPLE = T / conforms to FITS standard 185 BITPIX = 8 / BITS PER PIXEL 186 NAXIS = 0 / NUMBER OF AXES 187 >>> hdul.close() 188 189Verification at Each Card 190------------------------- 191 192The lowest level, the Card, also has the most complicated verification 193possibilities. 194 195Examples 196^^^^^^^^ 197 198.. 199 EXAMPLE START 200 Verification at Each Card in astropy.io.fits 201 202Here is a list of fixable and not fixable Cards: 203 204Fixable Cards: 205 2061. Floating point numbers with lower case 'e' or 'd':: 207 208 >>> from astropy.io import fits 209 >>> c = fits.Card.fromstring('FIX1 = 2.1e23') 210 >>> c.verify('silentfix') 211 >>> print(c) 212 FIX1 = 2.1E23 213 2142. The equal sign is before column nine in the card image:: 215 216 >>> c = fits.Card.fromstring('FIX2= 2') 217 >>> c.verify('silentfix') 218 >>> print(c) 219 FIX2 = 2 220 2213. String value without enclosing quotes:: 222 223 >>> c = fits.Card.fromstring('FIX3 = string value without quotes') 224 >>> c.verify('silentfix') 225 >>> print(c) 226 FIX3 = 'string value without quotes' 227 2284. Missing equal sign before column nine in the card image. 229 2305. Space between numbers and E or D in floating point values:: 231 232 >>> c = fits.Card.fromstring('FIX5 = 2.4 e 03') 233 >>> c.verify('silentfix') 234 >>> print(c) 235 FIX5 = 2.4E03 236 2376. Unparsable values will be "fixed" as a string:: 238 239 >>> c = fits.Card.fromstring('FIX6 = 2 10 ') 240 >>> c.verify('fix+warn') # doctest: +SHOW_WARNINGS 241 VerifyWarning: Verification reported errors: 242 VerifyWarning: Card 'FIX6' is not FITS standard 243 (invalid value string: '2 10'). 244 Fixed 'FIX6' card to meet the FITS standard. 245 VerifyWarning: Note: astropy.io.fits uses zero-based indexing. 246 >>> print(c) 247 FIX6 = '2 10 ' 248 249Unfixable Cards: 250 2511. Illegal characters in keyword name. 252 253We will summarize the verification with a "life-cycle" example:: 254 255 >>> h = fits.PrimaryHDU() # create a PrimaryHDU 256 >>> # Try to add an non-standard FITS keyword 'P.I.' (FITS does no allow 257 >>> # '.' in the keyword), if using the update() method - doesn't work! 258 >>> h.header['P.I.'] = 'Hubble' # doctest: +SHOW_WARNINGS 259 VerifyWarning: Keyword name 'P.I.' is greater than 8 characters or 260 contains characters not allowed by the FITS standard; 261 a HIERARCH card will be created. 262 >>> # Have to do it the hard way (so a user will not do this by accident) 263 >>> # First, create a card image and give verbatim card content (including 264 >>> # the proper spacing, but no need to add the trailing blanks) 265 >>> c = fits.Card.fromstring("P.I. = 'Hubble'") 266 >>> h.header.append(c) # then append it to the header 267 >>> # Now if we try to write to a FITS file, the default output 268 >>> # verification will not take it. 269 >>> h.writeto('pi.fits') # doctest: +IGNORE_EXCEPTION_DETAIL 270 Traceback (most recent call last): 271 ... 272 VerifyError: HDU 0: 273 Card 5: 274 Card 'P.I. ' is not FITS standard (equal sign not at column 8). 275 Illegal keyword name 'P.I. ' 276 >>> # Must set the output_verify argument to 'ignore', to force writing a 277 >>> # non-standard FITS file 278 >>> h.writeto('pi.fits', output_verify='ignore') 279 >>> # Now reading a non-standard FITS file 280 >>> # astropy.io.fits is magnanimous in reading non-standard FITS files 281 >>> hdul = fits.open('pi.fits') 282 >>> hdul[0].header # doctest: +SHOW_WARNINGS 283 SIMPLE = T / conforms to FITS standard 284 BITPIX = 8 / array data type 285 NAXIS = 0 / number of array dimensions 286 EXTEND = T 287 HIERARCH P.I. = 'Hubble ' 288 P.I. = 'Hubble ' 289 VerifyWarning: Verification reported errors: 290 VerifyWarning: Card 'P.I. ' is not FITS standard (equal sign 291 not at column 8). Fixed 'P.I. ' card to meet the FITS standard. 292 VerifyWarning: Unfixable error: Illegal keyword name 'P.I. ' 293 VerifyWarning: Note: astropy.io.fits uses zero-based indexing. 294 >>> # even when you try to access the offending keyword, it does NOT 295 >>> # complain 296 >>> hdul[0].header['p.i.'] 297 'Hubble' 298 >>> # But if you want to make sure if there is anything wrong/non-standard, 299 >>> # use the verify() method 300 >>> hdul.verify() # doctest: +SHOW_WARNINGS 301 VerifyWarning: Verification reported errors: 302 VerifyWarning: HDU 0: 303 VerifyWarning: Card 5: 304 VerifyWarning: Illegal keyword name 'P.I. ' 305 VerifyWarning: Note: astropy.io.fits uses zero-based indexing. 306 >>> hdul.close() 307 308.. 309 EXAMPLE END 310 311Verification Using the FITS Checksum Keyword Convention 312======================================================= 313 314The North American FITS committee has reviewed the FITS Checksum Keyword 315Convention for possible adoption as a FITS Standard. This convention provides 316an integrity check on information contained in FITS HDUs. The convention 317consists of two header keyword cards: CHECKSUM and DATASUM. The CHECKSUM 318keyword is defined as an ASCII character string whose value forces the 32-bit 3191's complement checksum accumulated over all the 2880-byte FITS logical records 320in the HDU to equal negative zero. The DATASUM keyword is defined as a 321character string containing the unsigned integer value of the 32-bit 1's 322complement checksum of the data records in the HDU. Verifying the 323accumulated checksum is still equal to negative zero provides a fairly reliable 324way to determine that the HDU has not been modified by subsequent data 325processing operations or corrupted while copying or storing the file on 326physical media. 327 328In order to avoid any impact on performance, by default ``astropy`` will not 329verify HDU checksums when a file is opened or generate checksum values when a 330file is written. In fact, CHECKSUM and DATASUM cards are automatically removed 331from HDU headers when a file is opened, and any CHECKSUM or DATASUM cards are 332stripped from headers when an HDU is written to a file. In order to verify the 333checksum values for HDUs when opening a file, the user must supply the checksum 334keyword argument in the call to the open convenience function with a value of 335True. When this is done, any checksum verification failure will cause a 336warning to be issued (via the warnings module). If checksum verification is 337requested in the open, and no CHECKSUM or DATASUM cards exist in the HDU 338header, the file will open without comment. Similarly, in order to output the 339CHECKSUM and DATASUM cards in an HDU header when writing to a file, the user 340must supply the checksum keyword argument with a value of True in the call to 341the ``writeto()`` function. It is possible to write only the DATASUM card to the 342header by supplying the checksum keyword argument with a value of 'datasum'. 343 344Examples 345-------- 346 347.. 348 EXAMPLE START 349 Verification Using the FITS Checksum Keyword Convention 350 351To verify the checksum values for HDUs when opening a file:: 352 353 >>> # Open the file checksum.fits verifying the checksum values for all HDUs 354 >>> filename = fits.util.get_testdata_filepath('checksum.fits') 355 >>> hdul = fits.open(filename, checksum=True) 356 >>> hdul.close() 357 >>> # Open the file in.fits where checksum verification fails 358 >>> filename = fits.util.get_testdata_filepath('checksum_false.fits') 359 >>> hdul = fits.open(filename, checksum=True) # doctest: +SHOW_WARNINGS 360 AstropyUserWarning: Checksum verification failed for HDU ('PRIMARY', 1). 361 AstropyUserWarning: Datasum verification failed for HDU ('PRIMARY', 1). 362 AstropyUserWarning: Checksum verification failed for HDU ('RATE', 1). 363 AstropyUserWarning: Datasum verification failed for HDU ('RATE', 1). 364 >>> # Create file out.fits containing an HDU constructed from data 365 >>> # containing both CHECKSUM and DATASUM cards. 366 >>> data = hdul[0].data 367 >>> fits.writeto('out.fits', data=data, checksum=True) 368 >>> hdun = fits.open('out.fits', checksum=True) 369 >>> hdun.close() 370 371 >>> # Create file out.fits containing all the HDUs in the HDULIST 372 >>> # hdul with each HDU header containing only the DATASUM card 373 >>> hdul.writeto('out2.fits', checksum='datasum') 374 375 >>> # Create file out.fits containing the HDU hdu with both CHECKSUM 376 >>> # and DATASUM cards in the header 377 >>> hdu = hdul[1] 378 >>> hdu.writeto('out3.fits', checksum=True) 379 380 >>> # Append a new HDU constructed from array data to the end of 381 >>> # the file existingfile.fits with only the appended HDU 382 >>> # containing both CHECKSUM and DATASUM cards. 383 >>> fits.append('out3.fits', data, checksum=True) 384 >>> hdul.close() 385 386.. 387 EXAMPLE END 388