def add_crs_this_slice( self): """ Add all GCR/SPs to the current slice """ verb = self._verb if ( verb > 1): print(' Start of add_crs_this_slice() for slice number: ' , self._slice) try: pix_val = self.fh_data_cube[0].data[self._slice,:,:] # do all pixels in this slice input_data = pix_val.copy() if ( self._slice > 0): # add cr/sp for all but 0th slice tot_nz_gcr_and_sp = N.where(self._tot_gcr_and_sp > 0.0) pix_val += self._tot_gcr_and_sp if ( verb > 0 ): print(' the number of pixels affected by gcr and sp for slice', self._slice,' = ' , len(tot_nz_gcr_and_sp[0] )) if ( verb > 1 ): print(' the amplitudes of CR/SP added for this slice: ' , self._tot_gcr_and_sp[ tot_nz_gcr_and_sp]) except Exception as errmess: print('Fatal ERROR in add_crs_this_slice() : ', errmess) sys.exit( ERROR_RETURN) try: prihdr = self.fh_data_cube[0].header except Exception as errmess: print('Fatal ERROR : unable to open primary header of data cube') sys.exit( ERROR_RETURN) # generate number of crs in this pixel total_gcr = 0 # total number of cosmic rays hitting array tot_pix_done = 0 # number of pixels looped over asize_1 = self._asize_1; asize_2 = self._asize_2 for ii_pix in range(0, self._asize_1 ): for jj_pix in range(0, self._asize_2 ): tot_pix_done += 1 if ( verb >1 ): print('Pixel [', ii_pix,',', jj_pix, '] initially has value:', pix_val[ ii_pix, jj_pix ]) # generate CRs got_num = 0 # 0 for not yet generated number of crs, reset to 1 when generated prob = random.uniform( 0.0, 1.0 ) ii_cr = 0 while (ii_cr < MAX_CR) and (got_num == 0) : if (prob < self._cumul_pois_g[ ii_cr]) : num_gcr = ii_cr got_num = 1 ii_cr += 1 if (verb >1 and num_gcr >0 ): print(' The number of cosmic rays incident on this pixel will be ' , num_gcr) # for each cr for this pixel, determine energy deposited in primary and secondary affected pixels for each_cr in range( num_gcr ): total_gcr += 1 self._grand_total_gcr += 1 # Get energy and template from library for CR affecting this primary pixel (ii_pix, jj_pix), and # through interpixel capacitance coupling, neighboring pixels. First generate random file. file_num = int(10*random.uniform( 0.0, 1.0 )) # use 'raw' (not interpixel coupling) version of files fname = self._cr_lib_dir + "_0" + str(file_num) + ".fits" if ( verb >1 ): print(' ... a cosmic ray will be grabbed from file : ',fname) fh = fits.open( fname ) cr_cube_data = fh[1].data cr_cube_energy = fh[3].data slice_num = int( 1000*random.uniform( 0.0, 1.0 ) ) # 1000 slices in library file if ( verb >1 ): print(' ... the energy deposited by this cosmic ray :', cr_cube_energy[slice_num],' MeV') cr_slice_data = cr_cube_data[slice_num,:,:] x_ctr_pix = int(cr_slice_data.shape[0]/2); y_ctr_pix = int(cr_slice_data.shape[0]/2) # center pixel of CR template if ( verb >1 ): print(' The CR template at its center is cr_slice_data[ y_ctr_pix, x_ctr_pix] =', cr_slice_data[ y_ctr_pix, x_ctr_pix]) print(' The CR max deposited energy =', cr_slice_data.max()) wh_nz = N.where( cr_slice_data > 0.0 ) num_pix_affected = len(cr_slice_data[ wh_nz ]) if ( verb >1 ): print(' The number of secondary pixels potentially affected by this cosmic ray', num_pix_affected) print(' ... and the energies deposited in these neighboring pixels : ',cr_slice_data[ wh_nz ]) num_2nd_aff = 0 # number of 2ndary pixels affected (including edge effects) for qq in range (0,num_pix_affected-0): # loop over affected secondary pixels if ( verb >1 ): print(' ') print(' For secondary potentially affected pixel: ',qq) x_tpl = wh_nz[0][qq] # template-centered coordinates y_tpl = wh_nz[1][qq] # template-centered coordinates if ( verb >1 ): print(' The template-centered coordinates for this secondary affected pixel are (',x_tpl,',',y_tpl,')') # the secondary pixel coordinates to which energy is added are xval, yval, which are : xval = ii_pix - x_ctr_pix + x_tpl yval = jj_pix - y_ctr_pix + y_tpl # start check that xval, yval are within array beyond_edge = False # not beyond edge if (( xval >= asize_1 ) or ( yval >= asize_2) or ( xval < 0 ) or ( yval < 0 )): if ( verb >1 ): print(' this 2ndary pixel is beyond the array edge so this 2ndary pixel does not exist') beyond_edge = True if ( beyond_edge == False ): # include this 2ndary pixel num_2nd_aff += 1 # number of 2ndary pixels affected (including edge effects) if ( verb >1 ): print(' the pixel coordinates to which energy is added are xval, yval, which are :', xval, yval) print(' will include this secondary pixel, just incremented num_2nd_aff to be', num_2nd_aff) delta_e_mev = cr_slice_data[ wh_nz][ qq ] # energy deposited in this pixel in MeV # convert this delta_e_mev from MeV to ADU before adding it to pix_val which is in ADU delta_e_adu = (delta_e_mev*1E6/E_BAND)/(self._electron_per_adu) # ... in ADU if ( verb >1 ): print(' the energy deposited by this GCR into this pixels is' , delta_e_mev, ' MeV, which is' , delta_e_adu, ' ADU') print(' ') # add energy deposit to arrays for this pixel self._new_gcr_and_sp[ yval, xval ] += delta_e_adu self.cr_and_sp_only_cube[ self._slice, yval, xval] += delta_e_adu # to compare to detections in a later program else: if ( verb >1 ): print(' will *NOT* include this 2ndary pixel - beyond edge') if ( verb >1 ): print(' for this CR each_cr = ' , each_cr,' the number of secondary pixels affected (including edge effects) = num_2nd_aff =', num_2nd_aff) if ( verb >1 ): print(' just before adding the latest GCR and SP, the min, mean, and max (over the slice) of the cumulative total of the amplitudes of GCR and SP for the current slice: ' , self._tot_gcr_and_sp.min(), self._tot_gcr_and_sp.mean(), self._tot_gcr_and_sp.max()) self._tot_gcr_and_sp += self._new_gcr_and_sp # add all pixels of this slice to total if ( verb >1 ): print(' just after adding the latest GCR and SP, the min, mean, and max (over the slice) of the cumulative total of the amplitudes of GCR and SP for the current slice: ' , self._tot_gcr_and_sp.min(), self._tot_gcr_and_sp.mean(), self._tot_gcr_and_sp.max()) print(' ') print(' original pix_val in ADU = ' , input_data) print(' final pix_val in ADU : ' , pix_val) if ( verb >1 ): final_minus_original_data = pix_val-input_data print(' final minus original in ADU for this slice (slice number', self._slice,'): ' , final_minus_original_data) print(' ... final minus original has min, mean, max : ' ,final_minus_original_data.min(),final_minus_original_data.mean(),final_minus_original_data.max()) if ( verb >1 ): print(' the total number of gcr hitting this slice (slice number', self._slice,'): ' , total_gcr) print(' the final array values for this slice : ', self.fh_data_cube[0].data[ self._slice,:,:]) if ( verb > 1 ) : print(' end of add crs for this slice')
def add_crs_all_slices( self ): """ Generate and add GCR/SPs for all slices in cube """ verb = self._verb self.fh_data_cube = fits.open( self._data_cube_file, mode='update' ) prihdr = self.fh_data_cube[0].header total_slices = self.fh_data_cube[0].data.shape[0] asize_1 = self.fh_data_cube[0].data.shape[1] asize_2 = self.fh_data_cube[0].data.shape[2] self._asize_1 = asize_1; self._asize_2 = asize_2 if (verb > 0): print(' ') print(' Data cube info: ', self.fh_data_cube.info()) print(' Array dimensions :', asize_1, asize_2) print(' Number of slices :', total_slices) try: print(' READMODE specified in the input header : ', prihdr['READMODE']) except Exception as errmess: print('WARNING: unable to determine readmode from header ') # read 'NAVGIMAG'] from input header to later output to output header try: self._navg = self.fh_data_cube[0].header['NAVGIMAG'] if ( verb >0 ): print(' From the input header keyword NAVGIMAG, navg = ' , self._navg) print(' ') except Exception as errmess: print('WARNING: unable to access NAVGIMAG from primary header of data cube, so assuming that the number of samples averaged over is 1 - which may not be what you want ! ') self._navg = 1 self.fh_data_cube[0].data = ( self.fh_data_cube[0].data).astype(N.float64) if (self._old_cr_file == None): # generate new GCRs and SPs if (verb > 0): print(' Generating GCRs and SPs...') try: self._instrume = prihdr['INSTRUME'] print(' This data cube is for instrument : ' , self._instrume) except Exception as errmess: print('WARNING: unable to determine the instrument from header, will default to NIRCAM ') self._instrume = 'NIRCAM' if (self._instrume == 'NIRCAM' ): self._electron_per_adu = 1.3 self._pix_length = 18. elif (self._instrume == 'MIRI' ): self._electron_per_adu = 2.2 self._pix_length = 25. else: print('Fatal ERROR - unsupported instrument : ', errmess) self._half_pix_length = self._pix_length/2. self._pix_area = self._pix_length**2. # area in micron2 self._grand_total_gcr = 0 self._tot_gcr_and_sp = N.zeros((asize_2, asize_1), dtype=N.float64) # data cube of created crs and sps : self.cr_and_sp_only_cube = N.zeros((total_slices, asize_2, asize_1), dtype=N.float64) # generate number of cosmic rays exp_gcr = self._cr_flux * self._pix_area * EXP_TIME # expected number of cosmic rays self._cumul_pois_g = calc_poisson( exp_gcr ) # generate distribution of number of CR if ( verb > 0 ): print('Instrument: ' , self._instrume) print(' cosmic ray flux = ' , self._cr_flux,'/micron^2/sec') print('Expected number of cosmic rays per pixel = ' , exp_gcr) print('Pixel length = ', self._pix_length ,' microns ') print('Pixel area = ' , self._pix_area, 'microns^2') print('Exposure time = ' , EXP_TIME, 'seconds') print(' ') for ii_slice in range( total_slices): if (verb > 0): print(' ') print(' ... beginning this slice: ', ii_slice) self._new_gcr_and_sp = N.zeros((asize_2, asize_1), dtype=N.float64) self._slice = ii_slice self.add_crs_this_slice() # add all GCRs for this slice self.write_file( self.fh_data_cube[0].data[:,:,:] , 'new_cube.fits') self.write_file( self.cr_and_sp_only_cube[ :,:,:], 'cr_and_sp.fits') if (verb > 0): print(' Wrote data cube of sources plus CRs to new_cube.fits') print(' Wrote cube of created CRs to cr_and_sp.fits') print(' Total number of GCRs summed over all slices = ', self._grand_total_gcr) else: # self._old_cr_file <> None so will use previously generated CR/SP file self.fh_old_cr = fits.open( self._old_cr_file) if (verb > 0): print(' Will be using previously generated GCRs and SPs from the specified file: ') print(' ... with info: ' , self.fh_old_cr.info()) for ii_slice in range(1, total_slices): # add cr slice 0 to data slice 1, etc self._slice = ii_slice slice_old_cr = self.fh_old_cr[0].data[self._slice-1,:,:] # note -1 if (verb > 1): print(' processing slice: ', slice_old_cr) # need to add this old cr slice to all later data slices for jj_slice in range(ii_slice, total_slices): self.fh_data_cube[0].data[ jj_slice,:,: ] += slice_old_cr self.write_file( self.fh_data_cube[0].data[:,:,:] , 'new_cube.fits') print(' Wrote data cube of sources plus GCRs and SPs to new_cube.fits')
def add_crs_all_slices(self): """ Generate and add GCR/SPs for all slices in cube """ verb = self._verb self.fh_data_cube = fits.open(self._data_cube_file, mode='update') prihdr = self.fh_data_cube[0].header total_slices = self.fh_data_cube[0].data.shape[0] asize_1 = self.fh_data_cube[0].data.shape[1] asize_2 = self.fh_data_cube[0].data.shape[2] self._asize_1 = asize_1; self._asize_2 = asize_2 if (verb > 0): print(' ') print(' Data cube info: ', self.fh_data_cube.info()) print(' Array dimensions :', asize_1, asize_2) print(' Number of slices :', total_slices) try: print(' READMODE specified in the input header : ', prihdr['READMODE']) except Exception as errmess: print('WARNING: unable to determine readmode from header ') # read 'NAVGIMAG'] from input header to later output to output header try: self._navg = self.fh_data_cube[0].header['NAVGIMAG'] if (verb > 0): print(' From the input header keyword NAVGIMAG, navg = ', self._navg) print(' ') except Exception as errmess: print('WARNING: unable to access NAVGIMAG from primary header of data cube, so assuming that the number of samples averaged over is 1 - which may not be what you want ! ') self._navg = 1 self.fh_data_cube[0].data = (self.fh_data_cube[0].data).astype(N.float64) if (self._old_cr_file == None): # generate new GCRs and SPs if (verb > 0): print(' Generating GCRs and SPs...') try: self._instrume = prihdr['INSTRUME'] print(' This data cube is for instrument : ', self._instrume) except Exception as errmess: print('WARNING: unable to determine the instrument from header, will default to NIRCAM ') self._instrume = 'NIRCAM' if (self._instrume == 'NIRCAM'): self._electron_per_adu = 1.3 self._pix_length = 18. elif (self._instrume == 'MIRI'): self._electron_per_adu = 2.2 self._pix_length = 25. else: print('Fatal ERROR - unsupported instrument : ', errmess) self._half_pix_length = self._pix_length / 2. self._pix_area = self._pix_length**2. # area in micron2 self._grand_total_gcr = 0 self._tot_gcr_and_sp = N.zeros((asize_2, asize_1), dtype=N.float64) # data cube of created crs and sps : self.cr_and_sp_only_cube = N.zeros((total_slices, asize_2, asize_1), dtype=N.float64) # generate number of cosmic rays exp_gcr = self._cr_flux * self._pix_area * EXP_TIME # expected number of cosmic rays self._cumul_pois_g = calc_poisson(exp_gcr) # generate distribution of number of CR if (verb > 0): print('Instrument: ', self._instrume) print(' cosmic ray flux = ', self._cr_flux, '/micron^2/sec') print('Expected number of cosmic rays per pixel = ', exp_gcr) print('Pixel length = ', self._pix_length, ' microns ') print('Pixel area = ', self._pix_area, 'microns^2') print('Exposure time = ', EXP_TIME, 'seconds') print(' ') for ii_slice in range(total_slices): if (verb > 0): print(' ') print(' ... beginning this slice: ', ii_slice) self._new_gcr_and_sp = N.zeros((asize_2, asize_1), dtype=N.float64) self._slice = ii_slice self.add_crs_this_slice() # add all GCRs for this slice self.write_file(self.fh_data_cube[0].data[:, :, :], 'new_cube.fits') self.write_file(self.cr_and_sp_only_cube[:, :, :], 'cr_and_sp.fits') if (verb > 0): print(' Wrote data cube of sources plus CRs to new_cube.fits') print(' Wrote cube of created CRs to cr_and_sp.fits') print(' Total number of GCRs summed over all slices = ', self._grand_total_gcr) else: # self._old_cr_file <> None so will use previously generated CR/SP file self.fh_old_cr = fits.open(self._old_cr_file) if (verb > 0): print(' Will be using previously generated GCRs and SPs from the specified file: ') print(' ... with info: ', self.fh_old_cr.info()) for ii_slice in range(1, total_slices): # add cr slice 0 to data slice 1, etc self._slice = ii_slice slice_old_cr = self.fh_old_cr[0].data[self._slice - 1, :, :] # note -1 if (verb > 1): print(' processing slice: ', slice_old_cr) # need to add this old cr slice to all later data slices for jj_slice in range(ii_slice, total_slices): self.fh_data_cube[0].data[jj_slice, :, :] += slice_old_cr self.write_file(self.fh_data_cube[0].data[:, :, :], 'new_cube.fits') print(' Wrote data cube of sources plus GCRs and SPs to new_cube.fits')
def add_crs_this_slice(self): """ Add all GCR/SPs to the current slice """ verb = self._verb if (verb > 1): print(' Start of add_crs_this_slice() for slice number: ', self._slice) try: pix_val = self.fh_data_cube[0].data[self._slice, :, :] # do all pixels in this slice input_data = pix_val.copy() if (self._slice > 0): # add cr/sp for all but 0th slice tot_nz_gcr_and_sp = N.where(self._tot_gcr_and_sp > 0.0) pix_val += self._tot_gcr_and_sp if (verb > 0): print(' the number of pixels affected by gcr and sp for slice', self._slice, ' = ', len(tot_nz_gcr_and_sp[0])) if (verb > 1): print(' the amplitudes of CR/SP added for this slice: ', self._tot_gcr_and_sp[tot_nz_gcr_and_sp]) except Exception as errmess: print('Fatal ERROR in add_crs_this_slice() : ', errmess) sys.exit(ERROR_RETURN) try: prihdr = self.fh_data_cube[0].header except Exception as errmess: print('Fatal ERROR : unable to open primary header of data cube') sys.exit(ERROR_RETURN) # generate number of crs in this pixel total_gcr = 0 # total number of cosmic rays hitting array tot_pix_done = 0 # number of pixels looped over asize_1 = self._asize_1; asize_2 = self._asize_2 for ii_pix in range(0, self._asize_1): for jj_pix in range(0, self._asize_2): tot_pix_done += 1 if (verb > 1): print('Pixel [', ii_pix, ',', jj_pix, '] initially has value:', pix_val[ii_pix, jj_pix]) # generate CRs got_num = 0 # 0 for not yet generated number of crs, reset to 1 when generated prob = random.uniform(0.0, 1.0) ii_cr = 0 while (ii_cr < MAX_CR) and (got_num == 0): if (prob < self._cumul_pois_g[ii_cr]): num_gcr = ii_cr got_num = 1 ii_cr += 1 if (verb > 1 and num_gcr > 0): print(' The number of cosmic rays incident on this pixel will be ', num_gcr) # for each cr for this pixel, determine energy deposited in primary and secondary affected pixels for each_cr in range(num_gcr): total_gcr += 1 self._grand_total_gcr += 1 # Get energy and template from library for CR affecting this primary pixel (ii_pix, jj_pix), and # through interpixel capacitance coupling, neighboring pixels. First generate random file. file_num = int(10 * random.uniform(0.0, 1.0)) # use 'raw' (not interpixel coupling) version of files fname = self._cr_lib_dir + "_0" + str(file_num) + ".fits" if (verb > 1): print(' ... a cosmic ray will be grabbed from file : ', fname) fh = fits.open(fname) cr_cube_data = fh[1].data cr_cube_energy = fh[3].data slice_num = int(1000 * random.uniform(0.0, 1.0)) # 1000 slices in library file if (verb > 1): print(' ... the energy deposited by this cosmic ray :', cr_cube_energy[slice_num], ' MeV') cr_slice_data = cr_cube_data[slice_num, :, :] x_ctr_pix = int(cr_slice_data.shape[0] / 2); y_ctr_pix = int(cr_slice_data.shape[0] / 2) # center pixel of CR template if (verb > 1): print(' The CR template at its center is cr_slice_data[y_ctr_pix, x_ctr_pix] =', cr_slice_data[y_ctr_pix, x_ctr_pix]) print(' The CR max deposited energy =', cr_slice_data.max()) wh_nz = N.where(cr_slice_data > 0.0) num_pix_affected = len(cr_slice_data[wh_nz]) if (verb > 1): print(' The number of secondary pixels potentially affected by this cosmic ray', num_pix_affected) print(' ... and the energies deposited in these neighboring pixels : ', cr_slice_data[wh_nz]) num_2nd_aff = 0 # number of 2ndary pixels affected (including edge effects) for qq in range(0, num_pix_affected - 0): # loop over affected secondary pixels if (verb > 1): print(' ') print(' For secondary potentially affected pixel: ', qq) x_tpl = wh_nz[0][qq] # template-centered coordinates y_tpl = wh_nz[1][qq] # template-centered coordinates if (verb > 1): print(' The template-centered coordinates for this secondary affected pixel are (', x_tpl, ',', y_tpl, ')') # the secondary pixel coordinates to which energy is added are xval, yval, which are : xval = ii_pix - x_ctr_pix + x_tpl yval = jj_pix - y_ctr_pix + y_tpl # start check that xval, yval are within array beyond_edge = False # not beyond edge if ((xval >= asize_1) or (yval >= asize_2) or (xval < 0) or (yval < 0)): if (verb > 1): print(' this 2ndary pixel is beyond the array edge so this 2ndary pixel does not exist') beyond_edge = True if (beyond_edge == False): # include this 2ndary pixel num_2nd_aff += 1 # number of 2ndary pixels affected (including edge effects) if (verb > 1): print(' the pixel coordinates to which energy is added are xval, yval, which are :', xval, yval) print(' will include this secondary pixel, just incremented num_2nd_aff to be', num_2nd_aff) delta_e_mev = cr_slice_data[wh_nz][qq] # energy deposited in this pixel in MeV # convert this delta_e_mev from MeV to ADU before adding it to pix_val which is in ADU delta_e_adu = (delta_e_mev * 1E6 / E_BAND) / (self._electron_per_adu) # ... in ADU if (verb > 1): print(' the energy deposited by this GCR into this pixels is', delta_e_mev, ' MeV, which is', delta_e_adu, ' ADU') print(' ') # add energy deposit to arrays for this pixel self._new_gcr_and_sp[yval, xval] += delta_e_adu self.cr_and_sp_only_cube[self._slice, yval, xval] += delta_e_adu # to compare to detections in a later program else: if (verb > 1): print(' will *NOT* include this 2ndary pixel - beyond edge') if (verb > 1): print(' for this CR each_cr = ', each_cr, ' the number of secondary pixels affected (including edge effects) = num_2nd_aff =', num_2nd_aff) if (verb > 1): print(' just before adding the latest GCR and SP, the min, mean, and max (over the slice) of the cumulative total of the amplitudes of GCR and SP for the current slice: ', self._tot_gcr_and_sp.min(), self._tot_gcr_and_sp.mean(), self._tot_gcr_and_sp.max()) self._tot_gcr_and_sp += self._new_gcr_and_sp # add all pixels of this slice to total if (verb > 1): print(' just after adding the latest GCR and SP, the min, mean, and max (over the slice) of the cumulative total of the amplitudes of GCR and SP for the current slice: ', self._tot_gcr_and_sp.min(), self._tot_gcr_and_sp.mean(), self._tot_gcr_and_sp.max()) print(' ') print(' original pix_val in ADU = ', input_data) print(' final pix_val in ADU : ', pix_val) if (verb > 1): final_minus_original_data = pix_val - input_data print(' final minus original in ADU for this slice (slice number', self._slice, '): ', final_minus_original_data) print(' ... final minus original has min, mean, max : ', final_minus_original_data.min(), final_minus_original_data.mean(), final_minus_original_data.max()) if (verb > 1): print(' the total number of gcr hitting this slice (slice number', self._slice, '): ', total_gcr) print(' the final array values for this slice : ', self.fh_data_cube[0].data[self._slice, :, :]) if (verb > 1): print(' end of add crs for this slice')
def Num(x) : hdu = None if type(x) == list : return ( map(num, x), None ) if type(x) == str : try: return ( int(x), None ) except ValueError: pass try: return ( float(x), None ) except ValueError: pass if x[:4] == "ds9:" : (target, frame) = extparse(x[4:], "ds9", 0) if ( frame != 0 ) : xpa.xpa(target).set("frame " + frame) x = xpa.xpa(target).get("file").strip() if x == "" : print "imrpn: cannot get file name from ds9: " + target + "," + str(frame) exit(1) if x == "stdin" : hdu = fits.open(xpa.xpa(target).get("fits", xpa.xpa.fp))[0] return ( hdu.data, hdu ) if x == "stdin" : try: hdu = fits.open(sys.stdin)[0] x = hdu.data except IOError: sys.stderr.write("Error opening fits from stdin.\n") exit(1) else: (file, extn) = extparse(x) try: x = fits.open(file) if type(extn) == list or type(extn) == slice: x = numpy.dstack([hdu.data for hdu in x[extn]]) else : found = 0 try: x = x[int(extn)].data except: for hdu in x[1:]: try: if hdu.EXTNAME == extn: found = 1 break except IndexError: pass if not found : print "imrpn: hdu has no EXTNAME : " + file + " " + extn exit(1) else : x = hdu.data if x == None : print "imrpn: hdu has no data : " + file + " " + str(extn) exit(1) except IOError: print "imrpn: cannot read file: " + x exit(1) return ( x, hdu )