4 from astropy.io
import fits
10 galType='sersic', cosmosCat=None,
11 drawMethod='no_pixel', trunc=10.0,
12 transform=None, addShear=False,
14 sersic_prec=0.01, addPoisson=False):
16 Function called by task to make galaxy images
19 flux = calibrated total flux
20 gal = galaxy parameters in record (np.recarray)
21 psfImage = np.ndarray of psfImage
22 galType = type of galaxy we want to make, this has to agree
23 with what's in the record array, options now are:
24 'sersic' (single sersic),
25 'dsersic' (double sersic), and
26 'real' (for making galaxies from real HST images)
27 'cosmos' (Using GalSim.COSMOSCatalog)
29 All the necessary keywords need to be in the fits catalog,
30 including maybe drawMethod and trunc...
32 if galType ==
'sersic':
35 drawMethod=drawMethod,
39 addPoisson=addPoisson)
41 if galType ==
'dsersic':
46 drawMethod=drawMethod,
50 addPoisson=addPoisson)
67 drawMethod=drawMethod,
69 addPoisson=addPoisson)
71 if galType ==
'cosmos':
72 if cosmosCat
is None or calib
is None:
73 raise Exception(
"# No COSMOSCatalog() provided!")
77 sersic_prec=sersic_prec,
78 drawMethod=drawMethod,
81 addPoisson=addPoisson)
85 """Place holder for real galaxi."""
93 cat_name = gal[
'cat_name']
95 raise KeyError(
'Can not find the name of the catlog')
97 cat_dir = gal[
'cat_dir']
101 real_galaxy_catalog = galsim.RealGalaxyCatalog(cat_name,
104 return real_galaxy_catalog, index
109 Parse the input total flux [tflux] and parameter record array
110 [gal] into two parameter records for each component [comp1, comp2]
115 frac1 = float(gal[
'b2t'])
117 raise KeyError(
"No b2t parameter is found in the record!!")
120 if (frac1 <= 0)
or (frac1 >= 1):
121 raise Exception(
"b2t should be > 0 and <1 !!")
122 flux1, flux2 = (tflux * frac1), (tflux * (1.0 - frac1))
128 reff1, reff2 = float(gal[
"reff1"]), float(gal[
"reff2"])
130 raise KeyError(
"reff1 or reff2 is found in the record!!")
133 nser1, nser2 = float(gal[
"sersic_n1"]), float(gal[
"sersic_n2"])
135 raise KeyError(
"sersic_n1 or sersic_n2 is found in the record!!")
138 ba1, ba2 = float(gal[
"b_a1"]), float(gal[
"b_a2"])
140 raise KeyError(
"b_a1 or b_a2 is found in the record!!")
143 pa1, pa2 = float(gal[
"theta1"]), float(gal[
"theta2"])
145 raise KeyError(
"theta1 or theta2 is found in the record!!")
147 comp1 = np.array((galID, flux1, nser1, reff1, ba1, pa1),
148 dtype=[(
'ID',
'int'),
150 (
'sersic_n',
'float'),
154 comp2 = np.array((galID, flux2, nser2, reff2, ba2, pa2),
155 dtype=[(
'ID',
'int'),
157 (
'sersic_n',
'float'),
167 Convert an input 2-D array into a GalSim Image object
169 TODO : Check the scale here
170 According to the GalSim Doxygen
171 If provided, use this as the pixel scale for the Image;
172 this will override the pixel scale stored by the provided Image.
173 If scale is None, then take the provided image's pixel scale.
177 return galsim.InterpolatedImage(galsim.image.Image(imgArr),
179 normalization=
"flux")
181 return galsim.InterpolatedImage(galsim.image.Image(imgArr),
188 "Draw" a GalSim Object into an GalSim Image using certain method, and with
191 TODO : Think about the scale here:
192 By default scale=None
193 According to GalSim Doxygen :
194 If provided, use this as the pixel scale for the image. If scale is None
195 and image != None, then take the provided image's pixel scale.
196 If scale is None and image == None, then use the Nyquist scale.
197 If scale <= 0 (regardless of image), then use the Nyquist scale.
202 imgTemp = galsim.image.Image(size, size)
203 galImg = galObj.drawImage(imgTemp, scale=scale, method=method)
205 galImg = galObj.drawImage(scale=scale, method=method)
209 galImg.addNoise(galsim.PoissonNoise())
218 Just do convolution using GalSim
220 Make sure the inputs are both GalSim GSObj
221 The galaxy model should be the first one, and the PSF object is the second
222 one; Returns a imgArr or GSObj
224 outObj = galsim.Convolve([galObj, psfObj])
230 scale=scale, method=method)
234 def galSimAdd(galObjList, size=0, scale=1.0, method="no_pixel",
237 Just add a list of GSObjs together using GalSim
238 Make sure all elements in the input list are GSObjs
240 if len(galObjList) < 2:
241 raise Exception(
"Should be more than one GSObjs to add !")
243 outObj = galsim.Add(galObjList)
254 size=0, addPoisson=False):
256 Generate a PNG image of the model
257 By default, the image will be named as 'fake_galaxy.png'
260 import matplotlib.pyplot
as plt
263 outPNG =
'fake_galaxy'
265 outPNG =
'fake_galaxy_%i' % galID
266 if suffix
is not None:
267 outPNG = outPNG +
'_' + suffix.strip() +
'.png'
269 plt.figure(1, figsize=(8, 8))
274 addPoisson=addPoisson,
280 psfImage=None, plotFake=False,
281 returnObj=True, sersic_prec=0.02,
282 drawMethod='no_pixel', scale=1.0,
283 transform=None, addShear=False,
286 Generate fake galaxy using galSim.COSMOSCatalog objects.
289 indexUse = cosmosCat.orig_index
290 paramCat = cosmosCat.param_cat
291 objectUse = paramCat[indexUse]
293 galFound = np.where(objectUse[
'IDENT'] == gal[
'COSMOS_ID'])[0]
294 if len(galFound) == 0:
295 warnings.warn(
"# Find no match for %d" % gal[
'COSMOS_ID'])
296 elif len(galFound) > 1:
297 warnings.warn(
"# Multiple match for %d" % gal[
'COSMOS_ID'])
299 galIndex = galFound[0]
300 cosObj = cosmosCat.makeGalaxy(index=galIndex,
301 sersic_prec=sersic_prec,
302 gal_type=
'parametric')
303 hstFlux = cosObj.flux
308 g1 = float(gal[
'g1'])
309 g2 = float(gal[
'g2'])
310 cosObj = cosObj.shear(g1=g1, g2=g2)
312 warnings.warn(
"Can not find g1 or g2 in the input!\n",
313 " No shear has been added!")
316 if transform
is not None:
317 cosObj = cosObj.transform(*tuple(transform.ravel()))
321 The flux of the galaxy corresponds to a 1 second exposure time with the
322 Hubble Space Telescope. Users who wish to simulate F814W images with a
323 different telescope and an exposure time longer than 1 second should
324 multiply by that exposure time, and by the square of the ratio of the
325 effective diameter of their telescope compared to that of HST.
326 (Effective diameter may differ from the actual diameter if there is
327 significant obscuration.)
329 The catalog returns objects that are appropriate for HST in 1 second
330 exposures. So for our telescope we scale up by the relative area and
331 exposure time. Note that what is important is the *effective* area after
332 taking into account obscuration. For HST, the telescope diameter is 2.4
333 but there is obscuration (a linear factor of 0.33). Here, we assume that
334 the telescope we're simulating effectively has no obscuration factor.
335 We're also ignoring the pi/4 factor since it appears in the numerator and
336 denominator, so we use area = diam^2.
339 hstEffArea = (2.4 ** 2) * (1.0 - 0.33 ** 2)
340 subaruEffArea = (8.2 ** 2) * (1.0 - 0.1 ** 2)
341 fluxScaling = (subaruEffArea / hstEffArea)
343 hstMag = -2.5 * np.log10(hstFlux) + 25.94
345 hscFlux = calib.getFlux(hstMag)
347 cosObj = cosObj.withFlux(float(hscFlux))
350 if psfImage
is not None:
354 cosFinal = galsim.Convolve([cosObj, psfObj])
367 method=drawMethod, scale=scale,
368 addPoisson=addPoisson)
372 expAll=False, devAll=False, plotFake=False, trunc=0,
373 drawMethod="no_pixel", addPoisson=False, scale=1.0,
374 transform=None, addShear=False):
376 Make a fake single Sersic galaxy using the galSim.Sersic function
378 Inputs: total flux of the galaxy, and a record array that stores the
379 necessary parameters [reffPix, nSersic, axisRatio, posAng]
381 Output: a 2-D image array of the galaxy model OR
382 a GalSim object of the model
385 psfImage: PSF image for convolution
386 trunc: Flux of Sersic models will truncate at trunc * reffPix
387 radius; trunc=0 means no truncation
388 drawMethod: The method for drawImage: ['auto', 'fft', 'real_space']
389 addPoisson: Add Poisson noise
390 plotFake: Generate a PNG figure of the model
391 expAll: Input model will be seen as nSersic=1
392 devAll: Input model will be seen as nSersic=4
393 returnObj: If TRUE, will return the GSObj
395 reff = float(gal[
"reff"])
396 posAng = float(gal[
"theta"])
397 axisRatio = float(gal[
"b_a"])
398 nSersic = float(gal[
"sersic_n"])
406 raise ValueError(
"Sersic index is too large! Should be <= 6.0")
408 if axisRatio <= 0.05:
409 raise ValueError(
"Axis Ratio is too small! Should be >= 0.05")
412 if nSersic == 1.0
or expAll:
414 serObj = galsim.Exponential(scale_radius=reff)
416 serObj = galsim.Exponential(half_light_radius=reff)
418 print(
" * Treated as a n=1 Exponential disk : %d" % (gal[
"ID"]))
419 elif nSersic == 4.0
or devAll:
420 serObj = galsim.DeVaucouleurs(half_light_radius=reff, trunc=trunc)
422 print(
" * Treated as a n=4 De Vaucouleurs model: %d" % (gal[
"ID"]))
424 serObj = galsim.Sersic(nSersic, half_light_radius=reff)
426 serObj = galsim.Sersic(nSersic, half_light_radius=reff,
431 serObj = serObj.shear(q=axisRatio, beta=(0.0 * galsim.degrees))
435 serObj = serObj.rotate((90.0 - posAng) * galsim.degrees)
440 g1 = float(gal[
'g1'])
441 g2 = float(gal[
'g2'])
442 serObj = serObj.shear(g1=g1, g2=g2)
444 warnings.warn(
"Can not find g1 or g2 in the input!\n",
445 " No shear has been added!")
448 if transform
is not None:
449 serObj = serObj.transform(*tuple(transform.ravel()))
452 serObj = serObj.withFlux(float(flux))
455 if psfImage
is not None:
459 serFinal = galsim.Convolve([serObj, psfObj])
472 addPoisson=addPoisson)
476 returnObj=True, devExp=False,
477 plotFake=False, drawMethod='auto',
478 addPoisson=False, scale=1.0, transform=None,
481 Make a fake double Sersic galaxy using the galSim.Sersic function
483 Inputs: total flux of the galaxy, and a record array that stores the
484 necessary parameters [reffPix, nSersic, axisRatio, posAng]
486 Output: a 2-D image array of the galaxy model OR
487 a GalSim object of the model
490 psfImage: PSF image for convolution
491 trunc: Flux of Sersic models will truncate at trunc * reffPix
492 radius; trunc=0 means no truncation
493 drawMethod: The method for drawImage: ['auto', 'fft', 'real_space']
494 addPoisson: Add Poisson noise
495 plotFake: Generate a PNG figure of the model
496 devexp: The first component will be seen as a nSersic=4 bulge;
497 And, the second one will be seen as a nSersic=1 disk
498 returnObj: If TRUE, will return the GSObj
502 flux1 = float(comp1[
'mag'])
503 flux2 = float(comp2[
'mag'])
510 trunc=trunc, addShear=addShear)
512 trunc=trunc, addShear=addShear)
520 doubleSersic =
galSimAdd([serModel1, serModel2])
523 if transform
is not None:
524 doubleSersic = doubleSersic.transform(*tuple(transform.ravel()))
527 if psfImage
is not None:
531 dserFinal = galsim.Convolve([doubleSersic, psfObj])
533 dserFinal = doubleSersic
547 addPoisson=addPoisson)
551 random=False, returnObj=True, plotFake=False,
552 drawMethod='auto', addPoisson=False, scale=1.0,
560 realObj = galsim.RealGalaxy(real_galaxy_catalog, index=index,
562 index = realObj.index
565 realObj = realObj.withFlux(flux)
568 if transform
is not None:
569 realObj = realObj.transform(*tuple(transform.ravel()))
572 if psfImage
is not None:
576 realFinal = galsim.Convolve([realObj, psfObj])
578 realFinal = realFinal
589 addPoisson=addPoisson)
592 def testMakeFake(galList, asciiTab=False, single=True, double=True, real=True):
593 """Test the makeFake functions."""
595 psfGaussian = galsim.Gaussian(fwhm=2.0)
596 psfImage = psfGaussian.drawImage().array
601 galData = np.loadtxt(galList, dtype=[(
'ID',
'int'),
603 (
'sersic_n',
'float'),
608 galData = fits.open(galList)[1].data
610 for igal, gal
in enumerate(galData):
612 flux = 10.0 ** ((27.0 - gal[
'mag']) / 2.5)
614 print(
'\n---------------------------------')
615 print(
" Input Flux : ", flux)
616 print(
" Input Parameters : ", gal[
"sersic_n"], gal[
"reff"])
617 print(
" ", gal[
"b_a"], gal[
"theta"])
620 plotFake=
True, returnObj=
False,
621 trunc=12.0, drawMethod=
"no_pixel")
623 print(
" Output Flux : ", np.sum(galArray))
624 print(
" Shape of the Output Array : ", galArray.shape)
625 print(
'---------------------------------')
630 raise Exception(
"For now, only FITS input is allowed !!")
632 galData = fits.open(galList)[1].data
634 for igal, gal
in enumerate(galData):
636 flux = 10.0 ** ((27.0 - gal[
'mag']) / 2.5)
638 print(
'\n---------------------------------')
639 print(
" Input Flux : ", flux)
652 print(
" Flux for Component 1 : ", comp1[
'mag'])
653 print(
" Flux for Component 2 : ", comp2[
'mag'])
654 print(
" Comp 1 Parameters : %5.2f %8.2f" % (comp1[
"sersic_n"],
656 print(
" %5.2f %8.2f" % (comp1[
"b_a"],
658 print(
" Comp 2 Parameters : %5.2f %8.2f" % (comp2[
"sersic_n"],
660 print(
" %5.2f %8.2f" % (comp2[
"b_a"],
665 trunc=12, returnObj=
False,
666 devExp=
True, plotFake=
True,
667 drawMethod=
'no_pixel')
669 print(
" Output Flux : ", np.sum(doubleArray))
670 print(
" Shape of the Output Array : ", doubleArray.shape)
671 print(
'---------------------------------')
676 psfReal = galsim.Gaussian(fwhm=0.2)
677 psfRealImage = psfReal.drawImage().array
681 raise Exception(
"For now, only FITS input is allowed !!")
683 galData = fits.open(galList)[1].data
685 for igal, gal
in enumerate(galData):
695 flux = 10.0 ** ((27.0 - gal[
'mag']) / 2.5)
699 psfImage=psfRealImage,
703 drawMethod=
'no_pixel')
705 print(
'\n---------------------------------')
706 print(
" Input Flux : ", flux)
708 print(
" Output Flux : ", np.sum(realArray))
709 print(
" Shape of the Output Array : ", realArray.shape)
710 print(
'---------------------------------')