Coverage for src/ufig/plugins/write_image.py: 100%

48 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-12 19:08 +0000

1# Copyright (c) 2013 ETH Zurich, Institute of Astronomy, Lukas Gamper 

2# <lukas.gamper@usystems.ch> 

3 

4""" 

5Created on Oct 7, 2013 

6@author: Lukas Gamper 

7""" 

8 

9import contextlib 

10 

11from astropy.io import fits 

12from ivy.plugin.base_plugin import BasePlugin 

13 

14 

15def header_keys(ctx): 

16 """ 

17 Initializes and fills a FITS header with all relevant keywords. 

18 

19 :param par: Parameters in the context 

20 :return: List with tuples containing header entries 

21 """ 

22 

23 # Note: CRPIX1 and CRPIX2 are in the convention where the bottom left corner is 

24 # (0.5, 0.5) instead of (0, 0), how it is throughout UFig. Therefore, although the 

25 # FITS convention is used for the origin pixel (e.g. in wcs_pix2world), i.e. that 

26 # the first pixel center is (1, 1), the reference pixel is shifted by (-0.5, -0.5), 

27 # to account for this. 

28 

29 par = ctx.parameters 

30 

31 keys = [ 

32 ("RADECSYS", "ICRS", "Astrometric system"), 

33 ("CTYPE1", "RA---TAN", "CS projection type for this axis"), 

34 ("CUNIT1", "deg", "Axis unit"), 

35 ("CRVAL1", par.ra0, "World coordinate on this axis"), 

36 ] 

37 

38 try: 

39 keys += [("CRPIX1", par.crpix_ra, "Reference pixel on this axis")] 

40 

41 except AttributeError: 

42 par.crpix_ra = par.size_x / 2.0 + 0.5 

43 keys += [("CRPIX1", par.crpix_ra, "Reference pixel on this axis")] 

44 

45 keys += [ 

46 ("CD1_1", -1 * par.pixscale / 60 / 60, "Linear projection matrix"), 

47 ("CD1_2", 0.00, "Linear projection matrix"), 

48 ("CTYPE2", "DEC--TAN", "WCS projection type for this axis"), 

49 ("CUNIT2", "deg", "Axis unit"), 

50 ("CRVAL2", par.dec0, "World coordinate on this axis"), 

51 ] 

52 

53 try: 

54 keys += [("CRPIX2", par.crpix_dec, "Reference pixel on this axis")] 

55 

56 except AttributeError: 

57 par.crpix_dec = par.size_y / 2.0 + 0.5 

58 keys += [("CRPIX2", par.crpix_dec, "Reference pixel on this axis")] 

59 

60 keys += [ 

61 ("CD2_1", 0.00, "Linear projection matrix"), 

62 ("CD2_2", par.pixscale / 60 / 60, "Linear projection matrix"), 

63 ( 

64 "EXPTIME", 

65 par.n_exp * par.exposure_time, 

66 "Maximum equivalent exposure time (s)", 

67 ), 

68 ("GAIN", par.gain * par.n_exp, "Maximum equivalent gain (e-/ADU)"), 

69 ("SATURATE", par.saturation_level, "Saturation Level (ADU)"), 

70 ("SEXMGZPT", par.magzero, "Mag ZP"), 

71 ("PSF_FWHM", get_seeing_value(ctx), "Seeing in pixels"), 

72 ("SEED", par.seed, "General seed"), 

73 ("GDSEEDO", par.gal_dist_seed_offset, "Galaxy distribution seed offset"), 

74 ( 

75 "GSSEEDO", 

76 par.gal_sersic_seed_offset, 

77 "Sersic indices of gals dist seed offset", 

78 ), 

79 ( 

80 "GESEEDO", 

81 par.gal_ellipticities_seed_offset, 

82 "Ellipticities of gals dist seed offset", 

83 ), 

84 ( 

85 "GNPSEEDO", 

86 par.gal_nphot_seed_offset, 

87 "Number of photons of gals seed offset", 

88 ), 

89 ("SDSEEDO", par.star_dist_seed_offset, "Star distribution seed offset"), 

90 ( 

91 "SNPSEEDO", 

92 par.star_nphot_seed_offset, 

93 "Number of photons of stars seed offset", 

94 ), 

95 ("GRSEEDO", par.gal_render_seed_offset, "Gal rendering seed offset"), 

96 ("SRSEEDO", par.star_render_seed_offset, "Star rendering seed offset"), 

97 ("BKGSEEDO", par.background_seed_offset, "Background seed offset"), 

98 ] 

99 

100 with contextlib.suppress(AttributeError): 

101 keys += [ 

102 ( 

103 "RESAMPT1", 

104 "LANCZOS" + str(par.lanczos_n), 

105 "RESAMPLING_TYPE config parameter", 

106 ), 

107 ( 

108 "RESAMPT2", 

109 "LANCZOS" + str(par.lanczos_n), 

110 "RESAMPLING_TYPE config parameter", 

111 ), 

112 ] 

113 

114 return keys 

115 

116 

117def get_seeing_value(ctx): 

118 """ 

119 Returns the seeing value of the simulated image. 

120 

121 :param ctx: Context 

122 :return: Seeing in pixels 

123 """ 

124 

125 try: 

126 seeing = ctx.average_seeing 

127 except AttributeError: 

128 try: 

129 seeing = ctx.parameters.seeing / ctx.parameters.pixscale 

130 except AttributeError: 

131 seeing = 0.001 # Small value for SExtractor to not crash 

132 

133 return seeing 

134 

135 

136def write_image(path, image, keys, overwrite): 

137 """ 

138 Writes the image to disk 

139 :param path: The path to write to (string) 

140 :param image: the 2d array image 

141 :param keys: list of header keys 

142 :param overwrite: whether to overwrite existing files 

143 """ 

144 

145 header = fits.header.Header() 

146 header["EQUINOX"] = (2000.00000000, "Mean equinox") 

147 header["MJD-OBS"] = ( 

148 5.625609513605e04, 

149 "Modified Julian date at start (Arbitrary Value)", 

150 ) 

151 header["AUTHOR"] = ("ETHZ ", "Cosmology Research Group, ETH Zurich") 

152 header["CENTERT1"] = ("MANUAL ", "CENTER_TYPE config parameter") 

153 header["PSCALET1"] = ("MANUAL ", "PIXELSCALE_TYPE config parameter") 

154 header["CENTERT2"] = ("MANUAL ", "CENTER_TYPE config parameter") 

155 header["PSCALET2"] = ("MANUAL ", "PIXELSCALE_TYPE config parameter") 

156 header.extend(keys) 

157 

158 fits.writeto(path, data=image, header=header, overwrite=overwrite) 

159 

160 

161class Plugin(BasePlugin): 

162 """ 

163 Write a general ufig image into a FITS file with minimum basic header information. 

164 """ 

165 

166 def __call__(self): 

167 par = self.ctx.parameters 

168 

169 write_image( 

170 par.image_name, self.ctx.image, header_keys(self.ctx), par.overwrite 

171 ) 

172 

173 del self.ctx.image 

174 

175 def __str__(self): 

176 return "write image to fits"