Commit 29ac5666 authored by Fang Yuedong's avatar Fang Yuedong
Browse files

Baseline

parents
# Graph from wfc-cr-attach, page 1
0.00000 0.004684
0.5031 0.004684
0.5283 0.01873
1.509 0.01873
1.534 0.09327
2.490 0.09327
2.515 0.1034
3.496 0.1034
3.522 0.2440
4.503 0.2440
4.528 0.1107
5.509 0.1107
5.534 0.1013
6.490 0.1013
6.515 0.06090
7.496 0.06090
7.522 0.05834
8.503 0.05834
8.528 0.03875
9.509 0.03875
9.534 0.03066
10.49 0.03066
10.51 0.01788
11.47 0.01788
11.49 0.01831
13.50 0.01831
13.53 0.01235
13.53 0.01235
14.49 0.01235
14.51 0.01064
15.49 0.01064
15.52 0.008091
16.50 0.008091
16.52 0.004684
17.48 0.004684
17.53 0.003833
18.49 0.003833
18.51 0.005536
19.47 0.005536
19.52 0.004684
20.00 0.004684
from .FocalPlane import FocalPlane
import galsim
import os
import numpy as np
from astropy.table import Table
class Chip(FocalPlane):
def __init__(self, chipID, ccdEffCurve_dir, config=None, treering_func=None):
# Get focal plane (instance of paraent class) info
# TODO: use chipID to config individual chip?
super().__init__()
if config is not None:
self.npix_x = config["npix_x"]
self.npix_y = config["npix_y"]
self.read_noise = config["read_noise"]
self.dark_noise = config["dark_noise"]
self.pix_scale = config["pix_scale"]
self.gain = config["gain"]
else:
# Default setting
self.npix_x = 9232
self.npix_y = 9216
self.read_noise = 5.0 # e/pix
self.dark_noise = 0.02 # e/pix/s
self.pix_scale = 0.074 # pixel scale
self.gain = 1.0
# A chip ID must be assigned
self.chipID = int(chipID)
# Get corresponding filter info
self.filter_id, self.filter_type = self.getChipFilter()
# Get boundary (in pix)
self.bound = self.getChipLim()
self.ccdEffCurve_dir = ccdEffCurve_dir
self.effCurve = self._getChipEffCurve(self.filter_type)
# Define the sensor
# self.sensor = galsim.Sensor()
self.sensor = galsim.SiliconSensor(strength=10., treering_func=treering_func)
def _getChipEffCurve(self, filter_type):
if filter_type in ['nuv', 'u']: filename = 'UV0.txt'
if filter_type in ['g', 'r']: filename = 'Astro_MB.txt'
if filter_type in ['i', 'z', 'y']: filename = 'Basic_NIR.txt'
path = os.path.join(self.ccdEffCurve_dir, filename)
table = Table.read(path, format='ascii')
throughput = galsim.LookupTable(x=table['col1'], f=table['col2'], interpolant='linear')
bandpass = galsim.Bandpass(throughput, wave_type='nm')
return bandpass
def getChipFilter(self, chipID=None, filter_layout=None):
"""Return the filter index and type for a given chip #(chipID)
"""
filter_type_list = ["nuv","u", "g", "r", "i","z","y"]
# TODO: maybe a more elegent way other than hard coded?
# e.g. use something like a nested dict:
if filter_layout is not None:
return filter_layout[chipID][0], filter_layout[chipID][1]
if chipID == None:
chipID = self.chipID
# updated configurations
if chipID>30 or chipID<1: raise ValueError("!!! Chip ID: [1,30]")
if chipID in [10, 11, 20, 21]: filter_type = 'y'
if chipID in [15, 16]: filter_type = "z"
if chipID in [9, 22]: filter_type = "i"
if chipID in [12, 19]: filter_type = "u"
if chipID in [7, 24]: filter_type = "r"
if chipID in [14, 13, 18, 17]: filter_type = "nuv"
if chipID in [8, 23]: filter_type = "g"
if chipID in [6, 5, 25, 26]: filter_type = "GI"
if chipID in [27, 30, 1, 4]: filter_type = "GV"
if chipID in [28, 29, 2, 3]: filter_type = "GU"
filter_id = filter_type_list.index(filter_type)
return filter_id, filter_type
def getChipLim(self, chipID=None):
"""Calculate the edges in pixel for a given CCD chip on the focal plane
NOTE: There are 5*4 CCD chips in the focus plane for photometric observation.
Parameters:
chipID: int
the index of the chip
Returns:
A galsim BoundsD object
"""
if chipID == None:
chipID = self.chipID
gx = self.npix_gap_x
gy1, gy2 = self.npix_gap_y
# xlim of a given ccd chip
xrem = (chipID-1)%self.nchip_x - self.nchip_x // 2
xcen = (self.npix_x + gx) * xrem
nx0 = xcen - self.npix_x//2 + 1
nx1 = xcen + self.npix_x//2
# ylim of a given ccd chip
yrem = 2*((chipID-1)//self.nchip_x) - (self.nchip_y-1)
ycen = (self.npix_y//2 + gy1//2) * yrem
if chipID <= 6: ycen = (self.npix_y//2 + gy1//2) * yrem - (gy2-gy1)
if chipID >= 25: ycen = (self.npix_y//2 + gy1//2) * yrem + (gy2-gy1)
ny0 = ycen - self.npix_y//2 + 1
ny1 = ycen + self.npix_y//2
return galsim.BoundsD(nx0-1, nx1-1, ny0-1, ny1-1)
def getSkyCoverage(self, wcs):
return super().getSkyCoverage(wcs, self.bound.xmin, self.bound.xmax, self.bound.ymin, self.bound.ymax)
def getSkyCoverageEnlarged(self, wcs, margin=0.5):
"""The enlarged sky coverage of the chip
"""
margin /= 60.0
bound = self.getSkyCoverage(wcs)
return galsim.BoundsD(bound.xmin - margin, bound.xmax + margin, bound.ymin - margin, bound.ymax + margin)
def isContainObj(self, ra_obj, dec_obj, sky_coverage):
if (ra_obj - sky_coverage.xmin) * (ra_obj - sky_coverage.xmax) > 0.0 or (dec_obj - sky_coverage.ymin) * (dec_obj - sky_coverage.ymax) > 0.0:
return False
return True
def getChipNoise(self, exptime=150.0):
# TODO: put it here or make it a separate class?
noise = self.dark_noise * exptime + self.read_noise**2
return noise
def addNoise(self, img, exptime=150.0, sky_noise=0., seed=31415):
rng = galsim.BaseDeviate(seed)
dark_noise = galsim.DeviateNoise(galsim.PoissonDeviate(rng, self.dark_noise*exptime))
img.addNoise(dark_noise)
ccd_noise = galsim.CCDNoise(rng, sky_level=sky_noise, gain=self.gain, read_noise=self.read_noise)
img.addNoise(ccd_noise)
return img
\ No newline at end of file
import galsim
import pylab as pl
import os
import numpy as np
from ._util import photonEnergy
from .FilterParam import FilterParam
class Filter(object):
def __init__(self, filter_id, filter_type, filter_param, ccd_bandpass):
self.filter_id = filter_id
self.filter_type = filter_type
self.ccd_bandpass = ccd_bandpass
self._getParam(filter_param, filter_type)
if self.filter_dir is not None:
self.bandpass_full, self.bandpass_sub_list = self._get_bandpasses(self.filter_dir)
def _getParam(self, filter_param, filter_type, filter_id=None):
self.effective_wavelength = filter_param.param[filter_type][0]
self.effective_width = filter_param.param[filter_type][1]
self.blue_limit = filter_param.param[filter_type][2]
self.red_limit = filter_param.param[filter_type][3]
self.efficiency = filter_param.param[filter_type][4]
self.sky_background = filter_param.param[filter_type][5]
self.mag_saturation = filter_param.param[filter_type][6]
self.mag_dim = filter_param.param[filter_type][7]
self.filter_dir = filter_param.filter_dir
def is_too_bright(self, mag):
return mag <= self.mag_saturation - 1.0
def is_too_dim(self, mag):
return mag >= self.mag_dim + 1.0
def _get_bandpasses(self, filter_dir, unit='A'):
if self.filter_id < 7: # Photometric
# Get full-bandpass
filter_file = os.path.join(filter_dir, self.filter_type+".dat")
bandpass_full = galsim.Bandpass(filter_file, wave_type=unit)
bandpass_full = bandpass_full * self.ccd_bandpass
# Get sub-bandpasses
bandpass_sub_list = []
wave_bin_file = os.path.join(filter_dir, self.filter_type.lower() + "_sub.list")
wave_points = open(wave_bin_file).read().splitlines()
for i in range(2, len(wave_points), 2):
blim = max(float(wave_points[i-2])*0.1, bandpass_full.blue_limit)
rlim = min(float(wave_points[i])*0.1, bandpass_full.red_limit)
if blim >= rlim:
continue
bandpass = bandpass_full.truncate(blue_limit=blim, red_limit=rlim)
bandpass_sub_list.append(bandpass)
# print("num of sub-bandpasses for filter#%d(%s) = "%(self.filter_id, self.filter_type), len(bandpass_sub_list), flush=True)
else: # Spectroscopic
sls_lamb = np.linspace(self.blue_limit, self.red_limit, 100)
sls_flux = np.ones_like(sls_lamb)
con_spec = galsim.LookupTable(sls_lamb, sls_lamb, interpolant='nearest')
bandpass_full = galsim.Bandpass(con_spec, wave_type=unit)
bandpass_sub_list = []
wave_bin_file = os.path.join(filter_dir, self.filter_type.lower() + "_sub.list")
wave_points = open(wave_bin_file).read().splitlines()
for i in range(2, len(wave_points), 2):
blim = max(float(wave_points[i - 2]) * 0.1, bandpass_full.blue_limit)
rlim = min(float(wave_points[i]) * 0.1, bandpass_full.red_limit)
if blim >= rlim:
continue
bandpass = bandpass_full.truncate(blue_limit=blim, red_limit=rlim)
bandpass_sub_list.append(bandpass)
# print("num of sub-bandpasses for filter#%d(%s) = " % (self.filter_id, self.filter_type), len(bandpass_sub_list), flush=True)
return bandpass_full, bandpass_sub_list
def getPhotonE(self):
return photonEnergy(self.effective_wavelength)
def getSkyNoise(self, exptime, gain):
return self.sky_background * exptime / gain
import galsim
import pylab as pl
import numpy as np
class FilterParam(object):
def __init__(self, filter_dir=None, filter_param=None):
self.param = self._filtParam(filter_param)
if filter_dir is not None:
self.filter_dir = filter_dir
else:
self.filter_dir = None
def _filtParam(self, filter_param=None):
"""
TODO: subject to change
Basic parameters of the CSSOS filters.
"""
# filter parameters: name:
# 1) effective wavelength
# 2) effective width
# 3) blue end
# 4) red end
# 5) systematic efficiency:
# tau = quantum efficiency * optical transmission * filter transmission
# 6) sky background: e/pix/s
# 7) saturation magnitude
# 8) dim end magnitude
if filter_param == None:
filtP = {
"nuv": [2867.7, 705.4, 2470.0, 3270.0, 0.1404, 0.004, 15.7, 25.4],
"u": [3601.1, 852.1, 3120.0, 4090.0, 0.2176, 0.021, 16.1, 25.4],
"g": [4754.5, 1569.8, 3900.0, 5620.0, 0.4640, 0.164, 17.2, 26.3],
"r": [6199.8, 1481.2, 5370.0, 7030.0, 0.5040, 0.207, 17.0, 26.0],
"i": [7653.2, 1588.1, 6760.0, 8550.0, 0.4960, 0.212, 16.7, 25.9],
"z": [9600.6, 2490.5, 8240.0, 11000.0, 0.2000, 0.123, 15.7, 25.2],
"y": [10051.0, 1590.6, 9130.0, 11000.0, 0.0960, 0.037, 14.4, 24.4],
"GU": [0.0, 0.0, 2550.0, 4200.0, 1.0, 0.037, 14.0, 26.0],
"GV": [0.0, 0.0, 4000.0, 6500.0, 1.0, 0.037, 14.0, 26.0],
"GI": [0.0, 0.0, 6200.0, 10000.0, 1.0, 0.037, 14.0, 26.0],
}
else:
filtP = filter_param
return filtP
def filtsInterp(self, filtlistn, filtdir="./", unit="A"):
"""
read filters and save into Python dictionary
NOTE: the wavelength unit must be in "Angstrom"
Parameters:
Same as function 'seds'
Return:
Same as function 'seds'
"""
filtParams = self.param
filtype = list(filtParams.keys())
filts = {}
filtlist = filtdir + filtlistn
filtn = open(filtlist).read().splitlines()
for itype in filtype:
ifiltn = filtdir+itype+".dat"
iblim = filtParams[itype][2]
irlim = filtParams[itype][3]
ifilt = galsim.Bandpass(ifiltn,wave_type=unit,blue_limit=iblim,red_limit=irlim)
wave = ifilt.wave_list
ifilt = ifilt.func(wave)
filts[itype] = np.transpose(np.array([wave*10.0,ifilt]))
return filtype, filts
\ No newline at end of file
import galsim
import numpy as np
class FocalPlane(object):
def __init__(self, config=None, survey_type='Photometric', bad_chips=None):
"""Get the focal plane layout
Note: (If applicable) make sure the config file have the most basic info, e.g.: "nchip_x"...
"""
if bad_chips == None:
self.bad_chips = []
else:
self.bad_chips = bad_chips
self.ignore_chips = []
if survey_type == 'Photometric':
# for i in range(6):
# self.ignore_chips.append(i+1)
# self.ignore_chips.append(i+25)
for i in range(5):
self.ignore_chips.append(i+1)
self.ignore_chips.append(i+26)
self.ignore_chips.append(10)
self.ignore_chips.append(21)
elif survey_type == 'Spectroscopic':
# for i in range(18):
# self.ignore_chips.append(i+7)
for i in range(6, 26):
if i == 10 or i == 21:
continue
else:
self.ignore_chips.append(i)
if config is not None:
self.nchip_x = config["nchip_x"]
self.nchip_y = config["nchip_y"]
self.npix_tot_x = config["npix_tot_x"]
self.npix_tot_y = config["npix_tot_y"]
self.npix_gap_x = config["npix_gap_x"]
self.npix_gap_y = config["npix_gap_y"]
if "chipLabelIDs" in config:
self.chipLabelIDs = config["chipLabelIDs"]
if "bad_chips" in config:
self.bad_chips = config["bad_chips"]
else:
# self.nchip_x = 5
# self.nchip_y = 6
# self.npix_tot_x = 49752
# self.npix_tot_y = 59516
# self.npix_gap_x = 898
# self.npix_gap_y = (534, 1309)
self.nchip_x = 6
self.nchip_y = 5
self.npix_tot_x = 59516
self.npix_tot_y = 49752
self.npix_gap_x = (534, 1309)
self.npix_gap_y = 898
self._getCenter()
def _getCenter(self):
# self.cen_pix_x = 0.5 * (self.npix_tot_x + 1.0)
# self.cen_pix_y = 0.5 * (self.npix_tot_y + 1.0)
self.cen_pix_x = 0
self.cen_pix_y = 0
def getChipLabel(self, chipID):
return str("0%d"%chipID)[-2:]
def isBadChip(self, chipID):
"""Check if chip #(chipID) on the focal plane is bad or not
"""
return (chipID in self.bad_chips)
def isIgnored(self, chipID):
return (chipID in self.ignore_chips)
def getTanWCS(self, ra, dec, img_rot, pix_scale, xcen=None, ycen=None, logger=None):
""" Get the WCS of the image mosaic using Gnomonic/TAN projection
Parameter:
ra, dec: float
(RA, Dec) of pointing of optical axis
img_rot: galsim Angle object
Rotation of image
pix_scale: float
Pixel size in unit of as/pix
Returns:
WCS of the focal plane
"""
if logger is not None:
logger.info(" Construct the wcs of the entire image mosaic using Gnomonic/TAN projection")
if (xcen == None) or (ycen == None):
xcen = self.cen_pix_x
ycen = self.cen_pix_y
dudx = -np.cos(img_rot.rad) * pix_scale
dudy = -np.sin(img_rot.rad) * pix_scale
dvdx = -np.sin(img_rot.rad) * pix_scale
dvdy = +np.cos(img_rot.rad) * pix_scale
# dudx = +np.sin(img_rot.rad) * pix_scale
# dudy = +np.cos(img_rot.rad) * pix_scale
# dvdx = -np.cos(img_rot.rad) * pix_scale
# dvdy = +np.sin(img_rot.rad) * pix_scale
moscen = galsim.PositionD(x=xcen, y=ycen)
sky_center = galsim.CelestialCoord(ra=ra*galsim.degrees, dec=dec*galsim.degrees)
affine = galsim.AffineTransform(dudx, dudy, dvdx, dvdy, origin=moscen)
WCS = galsim.TanWCS(affine, sky_center, units=galsim.arcsec)
return WCS
def getSkyCoverage(self, wcs, x0, x1, y0, y1):
"""
The sky coverage of an area
"""
r2d = 180.0/np.pi
s1 = wcs.toWorld(galsim.PositionD(x0,y0))
s2 = wcs.toWorld(galsim.PositionD(x0,y1))
s3 = wcs.toWorld(galsim.PositionD(x1,y0))
s4 = wcs.toWorld(galsim.PositionD(x1,y1))
ra = [s1.ra.rad*r2d, s2.ra.rad*r2d, s3.ra.rad*r2d, s4.ra.rad*r2d]
dec = [s1.dec.rad*r2d, s2.dec.rad*r2d, s3.dec.rad*r2d, s4.dec.rad*r2d]
return galsim.BoundsD(min(ra), max(ra), min(dec), max(dec))
import numpy as np
class Telescope(object):
def __init__(self, param=None, optEffCurve_path=None):
self.diameter = 2.0 # in unit of meter
if param is not None:
self.diameter = param["diameter"]
self.pupil_area = np.pi * (0.5 * self.diameter)**2
if optEffCurve_path is not None:
self.efficiency = self._get_efficiency(optEffCurve_path)
def _get_efficiency(self, effCurve_path):
""" Read in the efficiency of optics
for each band
Parameters:
effCurve_path: the path for efficiency file
Returns:
opticsEff: a dictionary of efficiency (a scalar) for each band
"""
f = open(effCurve_path, 'r')
for _ in range(2):
header = f.readline()
iline = 0
opticsEff = {}
for line in f:
line = line.strip()
columns = line.split()
opticsEff[str(columns[0])] = float(columns[2])
f.close()
return opticsEff
def _get_effCurve(self, effCurve_path):
pass
\ No newline at end of file
from .Telescope import Telescope
from .Filter import Filter
from .FilterParam import FilterParam
from .FocalPlane import FocalPlane
from .Chip import Chip
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment