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

Baseline

parents
import os
class ChipOutput(object):
def __init__(self, config, focal_plane, chip, filt, imgKey0="", imgKey1="", imgKey2="", exptime=150., mjdTime="", ra_cen=None, dec_cen=None, pointing_ID='0', subdir="./", prefix=""):
self.focal_plane = focal_plane
self.chip = chip
self.filt = filt
self.imgKey0 = imgKey0
self.imgKey1 = imgKey1
self.imgKey2 = imgKey2
self.exptime = exptime
self.mjdTime = mjdTime
if (ra_cen is not None) and (dec_cen is not None):
self.ra_cen = ra_cen
self.dec_cen = dec_cen
else:
self.ra_cen = config["ra_center"]
self.dec_cen = config["dec_center"]
exp_name = imgKey0 + "_%s_%s.fits"
self.chipLabel = focal_plane.getChipLabel(chip.chipID)
self.img_name = prefix + exp_name%(self.chipLabel, filt.filter_type)
# self.cat_name = self.img_name[:-5] + ".cat"
self.cat_name = 'MSC_' + config["date_obs"] + config["time_obs"] + "_" + str(pointing_ID).rjust(7, '0') + "_" + self.chipLabel.rjust(2,'0') + ".cat"
self.subdir = subdir
# hdr1 = "#ID ID_chip filter xImage yImage ra dec z mag flag SNR "
hdr1 = "#ID ID_chip filter xImage yImage ra dec z mag flag "
hdr2 = "thetaR bfrac hlr_disk hlr_bulge e1_disk e2_disk e1_bulge e2_bulge e1_total e2_total"
hdr3 = "e1PSF e2PSF e1 e2 g1 g2 e1OBS e2OBS"
hdr4 = "sed_type av redden "
hdr5 = "star_model teff logg feh\n"
# fmt1 = "%10d %4d %5s %10.3f %10.3f %15.6f %15.6f %7.4f %8.4f %2d %9.2f "
fmt1 = "%10d %4d %5s %10.3f %10.3f %15.6f %15.6f %7.4f %8.4f %2d "
fmt2 = "%8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f"
fmt3 = "%8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f"
fmt4 = "%2d %8.4f %8.4f "
fmt5 = "%10s %8.4f %8.4f %8.4f\n"
self.hdr = hdr1 + hdr2 + hdr3 + hdr4 + hdr5
self.fmt = fmt1 + fmt2 + fmt3 + fmt4 + fmt5
self.cat = open(os.path.join(self.subdir, self.cat_name), "w")
self.cat.write(self.hdr)
def updateHDR(self, hdr):
hdrNew = [{"name":"RDNOISE", "value":self.chip.read_noise, "comment":"read noise in e-/pixel"},
{"name":"DARK", "value":self.chip.dark_noise, "comment":"Dark noise (e-/pixel/s)"},
{"name":"EXPTIME", "value":self.exptime, "comment":"exposure time in second"},
{"name":"GAIN", "value":self.chip.gain, "comment":"CCD gain in e-/ADU"},
{"name":"SATURATE","value":65535.0, "comment":"saturation level"},
{"name":"CCDCHIP", "value":int(self.chipLabel), "comment":"chip ID in the CCD mosaic"},
{"name":"FILTER", "value":self.filt.filter_type, "comment":"filter name"},
{"name":"MJD-OBS", "value":self.mjdTime, "comment":"Modified Julian Date (MJD) of observation"},
{"name":"DATE-OBS","value":self.imgKey1, "comment":"Date of observation"},
{"name":"EQUINOX", "value":2000.0},
{"name":"RADECSYS","value":"ICRS"},
{"name":"RA", "value":self.ra_cen, "comment":"telescope pointing center"},
{"name":"DEC", "value":self.dec_cen, "comment":"telescope pointing center"},
{"name":"OBJECT", "value":"CSS-OS"},
{"name":"WCSDIM", "value":2.0, "comment":"WCS Dimensionality"},
{"name":"EXTNAME", "value":"IM1", "comment":"Extension name"},
{"name":"BSCALE", "value":1.0},
{"name":"BZERO", "value":0.0},
{"name":"OBSID", "value":self.imgKey0, "comment":"Observation ID"},
{"name":"CCDNAME", "value":"ccd"+self.chipLabel,"comment":"CCD name"},
{"name":"RSPEED", "value":10.0, "comment":"Read speed"},
{"name":"CHIPTEMP","value":-100.0, "comment":"Chip temperature"},
{"name":"DATASEC", "value":"1:%d,1:%d"%(self.chip.npix_x,self.chip.npix_y), "comment":"Data section"},
{"name":"CCDSUM", "value":self.chip.npix_x*self.chip.npix_y, "comment":"CCD pixel summing"},
{"name":"NSUM", "value":self.chip.npix_x*self.chip.npix_y, "comment":"CCD pixel summing"},
{"name":"AUTHOR", "value":"CSST-Sim Group"},
{"name":"GROUP", "value":"Weak Lensing Working Group for CSST"}]
for item in hdrNew:
hdr.add_record(item)
return hdr
# def cat_add_obj(self, obj, pos_img, snr, pos_shear, g1, g2):
def cat_add_obj(self, obj, pos_img, pos_shear, g1, g2):
ximg = pos_img.x - self.chip.bound.xmin + 1.0
yimg = pos_img.y - self.chip.bound.ymin + 1.0
e1, e2, g1, g2, e1OBS, e2OBS = obj.getObservedEll(g1, g2)
if obj.type == 'galaxy':
line = self.fmt%(obj.id, int(self.chipLabel), self.filt.filter_type, ximg, yimg, obj.ra, obj.dec, obj.z, obj.getMagFilter(self.filt), obj.param["star"], obj.thetaR, obj.bfrac, obj.hlr_disk, obj.hlr_bulge,
obj.e1_disk, obj.e2_disk, obj.e1_bulge, obj.e2_bulge, obj.e1_total, obj.e2_total,
pos_shear.g1, pos_shear.g2, e1, e2, g1, g2, e1OBS, e2OBS, obj.sed_type, obj.param['av'], obj.param['redden'], 'n', 0, 0, 0)
elif obj.type == "quasar":
line = self.fmt % (obj.id, int(self.chipLabel), self.filt.filter_type, ximg, yimg, obj.ra, obj.dec, obj.z,
obj.getMagFilter(self.filt), obj.param["star"], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0,
pos_shear.g1, pos_shear.g2, e1, e2, g1, g2, e1OBS, e2OBS, obj.sed_type, obj.param['av'], obj.param['redden'], 'n', 0.0, 0.0, 0.0)
else:
line = self.fmt%(obj.id, int(self.chipLabel), self.filt.filter_type, ximg, yimg, obj.ra, obj.dec, obj.z, obj.getMagFilter(self.filt), obj.param["star"], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
pos_shear.g1, pos_shear.g2, e1, e2, g1, g2, e1OBS, e2OBS, 0, 0.0, 0.0, obj.param['model_tag'], obj.param['teff'], obj.param['logg'],obj.param['feh'])
# line = self.fmt%(obj.id, int(self.chipLabel), self.filt.filter_type, ximg, yimg, obj.ra, obj.dec, obj.z, obj.getMagFilter(self.filt), obj.param["star"], pos_shear.g1, pos_shear.g2, e1, e2, g1, g2, e1OBS, e2OBS)
self.cat.write(line)
\ No newline at end of file
import galsim
import os
from astropy.time import Time as asTime
def ConfigDir(cat_dir=None, work_dir=None, data_dir=None, config_file_path=None):
path_dict = {}
# Working directory
if work_dir == None:
dirname, _ = os.path.split(os.path.abspath(__file__))
path_dict["work_dir"] = "/".join(dirname.split("/")[:-1]) + "/"
else:
path_dict["work_dir"] = work_dir
# Configuration file
if config_file_path is not None:
path_dict["config_file"] = config_file_path
else:
path_dict["config_file"] = os.path.join(path_dict["work_dir"], "ObservationSim.cfg")
# Output directories
# path_dict["output_fig_dir"] = os.path.join(path_dict["work_dir"], "figure/")
# if not os.path.exists(path_dict["output_fig_dir"]):
# os.system("mkdir %s"%path_dict["output_fig_dir"])
# path_dict["output_cat_dir"] = os.path.join(path_dict["work_dir"], "scat/")
# if not os.path.exists(path_dict["output_cat_dir"]):
# os.system("mkdir %s"%path_dict["output_cat_dir"])
path_dict["output_img_dir"] = os.path.join(path_dict["work_dir"], "simg/")
if not os.path.exists(path_dict["output_img_dir"]):
os.system("mkdir %s"%path_dict["output_img_dir"])
# Data directory
if data_dir == None:
path_dict["data_dir"] =os.path.join(path_dict["work_dir"], "data/")
else:
path_dict["data_dir"] = data_dir
# Data sub-catalogs
# Object catalog direcotry
# path_dict["cat_dir"] = os.path.join(path_dict["data_dir"], "catalog_points_7degree2/", cat_dir)
path_dict["cat_dir"] = os.path.join(path_dict["data_dir"], "Catalog_20210126")
# PSF data directory
path_dict["psf_dir"] = os.path.join(path_dict["data_dir"], "csstPSFdata/CSSOS_psf_20210108/CSST_psf_ciomp_2p5um_cycle3_ccr90_proc")
# SED catalog directory
path_dict["SED_dir"] = os.path.join(path_dict["data_dir"], "imageSims/Catalog/SEDObject")
path_dict["template_dir"] = path_dict["data_dir"] + "Templates/"
# Directories/files for instrument parameters, e.g. efficiency curves.
path_dict["filter_dir"] = os.path.join(path_dict["data_dir"], "Filters")
path_dict["ccd_dir"] = os.path.join(path_dict["data_dir"], "Filter_CCD_Mirror/ccd")
path_dict["mirror_file"] = os.path.join(path_dict["data_dir"], "Filter_CCD_Mirror/mirror_ccdnote.txt")
# Cosmic-ray data directory:
path_dict["CRdata_dir"] = os.path.join(path_dict["data_dir"], "CRdata")
# Slitless spectroscopy realted
path_dict["sls_dir"] = os.path.join(path_dict["data_dir"], "CONF/")
path_dict["normalize_dir"] = os.path.join(path_dict["data_dir"], "normalize_filter/")
return path_dict
def ReadConfig(config_filename):
"""Read in a configuration file and return the corresponding dict(s).
Parameters:
config_filename: The name of the configuration file to read.
Returns:
(list) of config dicts
"""
config = {}
config_file = open(config_filename).readlines()
nlines = len(config_file)
for i in range(nlines):
row = config_file[i].split()
if len(row) <= 1: continue # blank row
if not "#" in row:
if len(row) == 2:
key, val = row[0:2]
config.update({key:val})
else:
print("!! Something is wrong with parameter '%s'."%row[0])
return
elif row.index("#") == 2:
key, val = row[0:2]
config.update({key:val})
elif row.index("#") == 0:
continue # annotation
else:
print("!! Something is wrong with parameter '%s'."%row[0])
return
config = ParseConfig(config)
return config
def ParseConfig(config):
"""Parse the config values to the right type
Parameters:
config: raw config dict
Returns:
Parsed config dict
"""
config["ra_center"] = float(config["ra_center"])
config["dec_center"] = float(config["dec_center"])
config["psf_rcont"] = config["psf_rcont"].split(",")
config["psfRa"] = float(config["psf_rcont"][0])
config["psfCont"] = float(config["psf_rcont"][1])
config["image_rot"] = float(config["image_rot"])*galsim.degrees
config["sigma_spin"] = float(config["sigma_spin"])
config["reduced_g1"] = float(config["reduced_g1"])
config["reduced_g2"] = float(config["reduced_g2"])
config["rotateEll"] = float(config["rotateEll"])
config["reEll"] = int(config["rotateEll"]/45.0)
if config["reEll"]==0: config["reIndex"] = "P"
if config["reEll"]==1: config["reIndex"] = "X"
if config["reEll"]==2: config["reIndex"] = "N"
if config["reEll"]==3: config["reIndex"] = "Y"
config["seed_flat"] = int(config["seed_flat"])
config["seed_prnu"] = int(config["seed_prnu"])
config["seed_star"] = int(config["seed_star"])
config["seed_gal"] = int(config["seed_gal"])
config["seed_Av"] = int(config["seed_Av"])
config["bias_level"] = int(config["bias_level"])
config["df_strength"] = float(config["df_strength"])
return config
\ No newline at end of file
"""
generate image header
"""
import numpy as np
from astropy.io import fits
import astropy.wcs as pywcs
from collections import OrderedDict
from scipy import math
import random
import os
import sys
def chara2digit(char):
""" Function to judge and convert characters to digitals
Parameters
----------
"""
try:
float(char) # for int, long and float
except ValueError:
pass
return char
else:
data = float(char)
return data
def read_header_parameter(filename='global_header.param'):
""" Function to read the header parameters
Parameters
----------
"""
name = []
value = []
description = []
for line in open(filename):
line = line.strip("\n")
arr = line.split('|')
# csvReader = csv.reader(csvDataFile)
# for arr in csvReader:
name.append(arr[0])
# print(arr[0],arr[1])
value.append(chara2digit(arr[1]))
description.append(arr[2])
# print(value)
return name, value, description
def rotate_CD_matrix(cd, pa_aper):
"""Rotate CD matrix
Parameters
----------
cd: (2,2) array
CD matrix
pa_aper: float
Position angle, in degrees E from N, of y axis of the detector
Returns
-------
cd_rot: (2,2) array
Rotated CD matrix
Comments
--------
`astropy.wcs.WCS.rotateCD` doesn't work for non-square pixels in that it
doesn't preserve the pixel scale! The bug seems to come from the fact
that `rotateCD` assumes a transposed version of its own CD matrix.
"""
rad = np.deg2rad(-pa_aper)
mat = np.zeros((2,2))
mat[0,:] = np.array([np.cos(rad),-np.sin(rad)])
mat[1,:] = np.array([np.sin(rad),np.cos(rad)])
cd_rot = np.dot(mat, cd)
return cd_rot
def Header_extention(xlen = 9216, ylen = 9232, gain = 1.0, readout = 5.0, dark = 0.02,saturation=90000, row_num = 1, col_num = 1):
""" Creat an image frame for CCST with multiple extensions
Parameters
----------
"""
flag_ltm_x = [0,1,-1,1,-1]
flag_ltm_y = [0,1,1,-1,-1]
flag_ltv_x = [0,0,1,0,1]
flag_ltv_y = [0,0,0,1,1]
detector_size_x = int(xlen)
detector_size_y = int(ylen)
data_x = str(int(detector_size_x))
data_y = str(int(detector_size_y))
data_sec = '[1:'+data_x+',1:'+data_y+']'
e_header_fn = os.path.split(os.path.realpath(__file__))[0] + '/extension_header.param'
name, value, description = read_header_parameter(e_header_fn)
f = open(os.path.split(os.path.realpath(__file__))[0] + '/filter.lst')
s = f.readline()
s = s.strip("\n")
filters = s.split(' ')
s = f.readline()
s = s.strip("\n")
filterID = s.split()
s = f.readline()
s = s.strip("\n")
CCDID = s.split()
k = (row_num-1)*6+col_num
h_iter = 0
for n1,v1,d1 in zip(name, value, description):
if n1=='EXTNAME':
value[h_iter] = 'RAW,'+CCDID[k-1].rjust(2,'0')
if n1=='CCDNAME':
value[h_iter] = 'ccd' + CCDID[k-1].rjust(2,'0')
if n1=='AMPNAME':
value[h_iter] = 'ccd' + CCDID[k-1].rjust(2,'0') + ':A'
if n1=='GAIN':
value[h_iter] = gain
if n1=='RDNOISE':
value[h_iter] = readout
if n1=='SATURATE':
value[h_iter] = saturation
if n1=='CCDCHIP':
value[h_iter] = 'ccd' + CCDID[k-1].rjust(2,'0')
if n1=='CCDLABEL':
value[h_iter] = filters[k-1] + '-' + filterID[k-1]
if n1=='DATASEC':
value[h_iter] = data_sec
h_iter = h_iter + 1
return name, value, description
##9232 9216 898 534 1309 60 -40 -23.4333
def WCS_def(xlen = 9216, ylen = 9232, gapy = 898.0, gapx1 = 534, gapx2 = 1309, ra = 60, dec = -40, pa = -23.433,psize = 0.074, row_num = 1, col_num = 1):
""" Creat a wcs frame for CCST with multiple extensions
Parameters
----------
"""
flag_x = [0, 1, -1, 1, -1]
flag_y = [0, 1, 1, -1, -1]
flag_ext_x = [0,-1,1,-1,1]
flag_ext_y = [0,-1,-1,1,1]
x_num = 6
y_num = 5
detector_num = x_num*y_num
detector_size_x = xlen
detector_size_y = ylen
gap_y = gapy
gap_x = [gapx1,gapx2]
ra_ref = ra
dec_ref = dec
pa_aper = pa
pixel_size = psize
gap_x1_num = 3
gap_x2_num = 2
y_center = (detector_size_y*y_num+gap_y*(y_num-1))/2
x_center = (detector_size_x*x_num+gap_x[0]*gap_x1_num+gap_x[1]*gap_x2_num)/2
gap_x_map = np.array([[0,0,0,0,0],[gap_x[0],gap_x[1],gap_x[1],gap_x[1],gap_x[1]],[gap_x[1],gap_x[0],gap_x[0],gap_x[0],gap_x[0]],[gap_x[0],gap_x[0],gap_x[0],gap_x[0],gap_x[0]],[gap_x[0],gap_x[0],gap_x[0],gap_x[0],gap_x[1]],[gap_x[1],gap_x[1],gap_x[1],gap_x[1],gap_x[0]]])
# frame_array = np.empty((5,6),dtype=np.float64)
# print(x_center,y_center)
j = row_num
i = col_num
# ccdnum = str((j-1)*5+i)
x_ref, y_ref = detector_size_x*i + sum(gap_x_map[0:i,j-1]) - detector_size_x/2. , (detector_size_y+gap_y)*j-gap_y-detector_size_y/2
# print(i,j,x_ref,y_ref,ra_ref,dec_ref)
r_dat = OrderedDict()
# name = []
# value = []
# description = []
for k in range(1,2):
cd = np.array([[ pixel_size, 0], [0, pixel_size]])/3600.*flag_x[k]
cd_rot = rotate_CD_matrix(cd, pa_aper)
# f = open("CCD"+ccdnum.rjust(2,'0')+"_extension"+str(k)+"_wcs.param","w")
r_dat['EQUINOX'] = 2000.0
r_dat['WCSDIM'] = 2.0
r_dat['CTYPE1'] = 'RA---TAN'
r_dat['CTYPE2'] = 'DEC--TAN'
r_dat['CRVAL1'] = ra_ref
r_dat['CRVAL2'] = dec_ref
r_dat['CRPIX1'] = flag_ext_x[k]*((x_ref+flag_ext_x[k]*detector_size_x/2)-x_center)
r_dat['CRPIX2'] = flag_ext_y[k]*((y_ref+flag_ext_y[k]*detector_size_y/2)-y_center)
r_dat['CD1_1'] = cd_rot[0,0]
r_dat['CD1_2'] = cd_rot[0,1]
r_dat['CD2_1'] = cd_rot[1,0]
r_dat['CD2_2'] = cd_rot[1,1]
return r_dat
# name = ['EQUINOX',
# 'WCSDIM',
# 'CTYPE1',
# 'CTYPE2',
# 'CRVAL1',
# 'CRVAL2',
# 'CRPIX1',
# 'CRPIX2',
# 'CD1_1',
# 'CD1_2',
# 'CD2_1',
# 'CD2_2']
# value = [2000.0,
# 2.0,
# 'RA---TAN',
# 'DEC--TAN',
# ra_ref,
# dec_ref,
# flag_ext_x[k]*((x_ref+flag_ext_x[k]*detector_size_x/2)-x_center),
# flag_ext_y[k]*((y_ref+flag_ext_y[k]*detector_size_y/2)-y_center),
# cd_rot[0,0],
# cd_rot[0,1],
# cd_rot[1,0],
# cd_rot[1,1]]
# description = ['Equinox of WCS',
# 'WCS Dimensionality',
# 'Coordinate type',
# 'Coordinate typ',
# 'Coordinate reference value',
# 'Coordinate reference value',
# 'Coordinate reference pixel',
# 'Coordinate reference pixel',
# 'Coordinate matrix',
# 'Coordinate matrix',
# 'Coordinate matrix',
# 'Coordinate matrix']
# return name, value, description
def generatePrimaryHeader(xlen = 9216, ylen = 9232, pointNum = '1', ra = 60, dec = -40, psize = 0.074, row_num = 1, col_num = 1, date='200930', time_obs='120000'):
# array_size1, array_size2, flux, sigma = int(argv[1]), int(argv[2]), 1000.0, 5.0
k = (row_num-1)*6+col_num
# ccdnum = str(k)
g_header_fn = os.path.split(os.path.realpath(__file__))[0] + '/global_header.param'
name, value, description = read_header_parameter(g_header_fn)
f = open(os.path.split(os.path.realpath(__file__))[0] + '/filter.lst')
s = f.readline()
s = s.strip("\n")
filters = s.split(' ')
s = f.readline()
s = s.strip("\n")
filterID = s.split()
s = f.readline()
s = s.strip("\n")
CCDID = s.split()
h_prim = fits.Header()
for i in range(len(name)):
if(name[i]=='FILENAME'):
# value[i] = 'CSST_' + date + '_' +time_obs + '_' + pointNum.rjust(6,'0') + '_' +ccdnum.rjust(2,'0')+'_raw'
# value[i] = 'CSST_' + date + '_' +time_obs + '_' + pointNum.rjust(6,'0') + '_' +CCDID[k-1].rjust(2,'0')+'_raw'
value[i] = 'MSC_' + date + time_obs + '_' + pointNum.rjust(7,'0') + '_' +CCDID[k-1].rjust(2,'0')+'_raw'
if(name[i]=='DETSIZE'):
value[i] = '[1:' + str(int(xlen)) + ',1:'+ str(int(ylen)) + ']'
if(name[i]=='PIXSCAL1'):
value[i] = str(psize)
if(name[i]=='PIXSCAL2'):
value[i] = str(psize)
h_prim[name[i]] = (value[i],description[i])
h_prim.add_comment('==================================================================',after='FILETYPE')
h_prim.add_comment('Target information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='EQUINOX')
h_prim.add_comment('Exposure information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='MJDEND')
h_prim.add_comment('Telescope information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='REFFRAME')
h_prim.add_comment('Detector information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='DETETEMP')
h_prim.add_comment('Other information')
h_prim.add_comment('==================================================================')
return h_prim
def generateExtensionHeader(xlen = 9216, ylen = 9232,ra = 60, dec = -40, pa = -23.433, gain = 1.0, readout = 5.0, dark = 0.02, saturation=90000, psize = 0.074, row_num = 1, col_num = 1):
h_ext = fits.Header()
for i in range(1,2):
# NAXIS1:Number of pixels per row; NAXIS2:Number of rows
h_ext['NAXIS1'] = xlen
h_ext['NAXIS2'] = ylen
name, value, description = Header_extention(xlen = xlen, ylen = ylen, gain = gain, readout = readout, dark = dark, saturation=saturation, row_num = row_num, col_num = col_num)
for j in range(len(name)):
# print(name[j],value[j],description[j])
h_ext[name[j]] = (value[j],description[j])
header_wcs = WCS_def(xlen = xlen, ylen = ylen, gapy = 898.0, gapx1 = 534, gapx2 = 1309, ra = ra, dec = dec, pa = pa ,psize = psize, row_num = row_num, col_num = col_num)
for k,v in header_wcs.items():
h_ext.set(k,v)
# for j in range(len(name)):
# h_ext[name[j]] = (value[j],description[j])
h_ext.add_comment('==================================================================',after='OBSID')
h_ext.add_comment('Readout information')
h_ext.add_comment('==================================================================')
h_ext.add_comment('==================================================================',after='CHIPTEMP')
h_ext.add_comment('Chip information')
h_ext.add_comment('==================================================================')
h_ext.add_comment('==================================================================',after='TRIMSEC')
h_ext.add_comment('WCS information')
h_ext.add_comment('==================================================================')
h_ext.add_comment('==================================================================',after='CD2_2')
h_ext.add_comment('Other information')
h_ext.add_comment('==================================================================')
return h_ext
def main(argv):
xlen = int(argv[1])
ylen = int(argv[2])
pointingNum = argv[3]
ra = float(argv[4])
dec = float(argv[5])
pSize = float(argv[6])
ccd_row_num = int(argv[7])
ccd_col_num = int(argv[8])
pa_aper = float(argv[9])
gain = float(argv[10])
readout = float(argv[11])
dark = float(argv[12])
fw = float(argv[13])
h_prim = generatePrimaryHeader(xlen = xlen, ylen = ylen,ra = ra, dec = dec, psize = pSize, row_num = ccd_row_num, col_num = ccd_col_num, pointNum = pointingNum)
h_ext = generateExtensionHeader(xlen = xlen, ylen = ylen,ra = ra, dec = dec, pa = pa_aper, gain = gain, readout = readout, dark = dark, saturation=fw, psize = pSize, row_num = ccd_row_num, col_num = ccd_col_num)
hdu1 = fits.PrimaryHDU(header=h_prim)
hdu2 = fits.ImageHDU(np.zeros([ylen,xlen]),header = h_ext)
hdul = fits.HDUList([hdu1,hdu2])
hdul.writeto(h_prim['FILENAME']+'.fits',output_verify='ignore')
# if __name__ == "__main__":
# main(sys.argv)
"""
generate image header
"""
import numpy as np
from astropy.io import fits
import astropy.wcs as pywcs
from scipy import math
import random
import os
import sys
def chara2digit(char):
""" Function to judge and convert characters to digitals
Parameters
----------
"""
try:
float(char) # for int, long and float
except ValueError:
pass
return char
else:
data = float(char)
return data
def read_header_parameter(filename='global_header.param'):
""" Function to read the header parameters
Parameters
----------
"""
name = []
value = []
description = []
for line in open(filename):
line = line.strip("\n")
arr = line.split('|')
# csvReader = csv.reader(csvDataFile)
# for arr in csvReader:
name.append(arr[0])
value.append(chara2digit(arr[1]))
description.append(arr[2])
# print(value)
return name, value, description
def rotate_CD_matrix(cd, pa_aper):
"""Rotate CD matrix
Parameters
----------
cd: (2,2) array
CD matrix
pa_aper: float
Position angle, in degrees E from N, of y axis of the detector
Returns
-------
cd_rot: (2,2) array
Rotated CD matrix
Comments
--------
`astropy.wcs.WCS.rotateCD` doesn't work for non-square pixels in that it
doesn't preserve the pixel scale! The bug seems to come from the fact
that `rotateCD` assumes a transposed version of its own CD matrix.
"""
rad = np.deg2rad(-pa_aper)
mat = np.zeros((2,2))
mat[0,:] = np.array([np.cos(rad),-np.sin(rad)])
mat[1,:] = np.array([np.sin(rad),np.cos(rad)])
cd_rot = np.dot(mat, cd)
return cd_rot
def Header_extention(xlen = 9232, ylen = 9216, gain = 1.0, readout = 5.0, dark = 0.02,saturation=90000, row_num = 1, col_num = 1):
""" Creat an image frame for CCST with multiple extensions
Parameters
----------
"""
flag_ltm_x = [0,1,-1,1,-1]
flag_ltm_y = [0,1,1,-1,-1]
flag_ltv_x = [0,0,1,0,1]
flag_ltv_y = [0,0,0,1,1]
detector_size_x = int(xlen)
detector_size_y = int(ylen)
data_x = str(int(detector_size_x))
data_y = str(int(detector_size_y))
data_sec = '[1:'+data_x+',1:'+data_y+']'
name = []
value = []
description = []
for k in range(1,2):
# f = open("extension"+str(k)+"_image.param","w")
j = row_num
i = col_num
ccdnum = str((j-1)*5+i)
name = ['EXTNAME',
'BSCALE',
'BZERO',
'OBSID',
'CCDNAME',
'AMPNAME',
'GAIN',
'RDNOISE',
'DARK',
'SATURATE',
'RSPEED',
'CHIPTEMP',
'CCDCHIP',
'DATASEC',
'CCDSUM',
'NSUM',
'LTM1_1',
'LTM2_2',
'LTV1',
'LTV2',
'ATM1_1',
'ATM2_2',
'ATV1',
'ATV2',
'DTV1',
'DTV2',
'DTM1_1',
'DTM2_2']
value = ['IM'+str(k),
1.0,
0.0,
'CSST.20200101T000000',
'ccd' + ccdnum.rjust(2,'0'),
'ccd' + ccdnum.rjust(2,'0') + ':'+str(k),
gain,
readout,
dark,
saturation,
10.0,
-100.0,
'ccd' + ccdnum.rjust(2,'0'),
data_sec,
'1 1',
'1 1',
flag_ltm_x[k],
flag_ltm_y[k],
flag_ltv_x[k]*(detector_size_x-20*2+1),
flag_ltv_y[k]*(detector_size_y+1),
flag_ltm_x[k],
flag_ltm_y[k],
flag_ltv_x[k]*(detector_size_x-20*2+1),
flag_ltv_y[k]*(detector_size_y+1),
0,
0,
1,
1]
description = ['Extension name',
' ',
' ',
'Observation ID',
'CCD name',
'Amplifier name',
'Gain (e-/ADU)',
'Readout noise (e-/pixel)',
'Dark noise (e-/pixel/s)',
'Saturation (e-)',
'Read speed',
'Chip temperature',
'CCD chip ID',
'Data section',
'CCD pixel summing',
'CCD pixel summing',
'CCD to image transformation',
'CCD to image transformation',
'CCD to image transformation',
'CCD to image transformation',
'CCD to amplifier transformation',
'CCD to amplifier transformation',
'CCD to amplifier transformation',
'CCD to amplifier transformation',
'CCD to detector transformatio',
'CCD to detector transformatio',
'CCD to detector transformatio',
'CCD to detector transformatio']
return name, value, description
##9232 9216 898 534 1309 60 -40 -23.4333
def WCS_def(xlen = 9232, ylen = 9216, gapx = 898.0, gapy1 = 534, gapy2 = 1309, ra = 60, dec = -40, pa = -23.433,psize = 0.074, row_num = 1, col_num = 1):
""" Creat a wcs frame for CCST with multiple extensions
Parameters
----------
"""
flag_x = [0, 1, -1, 1, -1]
flag_y = [0, 1, 1, -1, -1]
flag_ext_x = [0,-1,1,-1,1]
flag_ext_y = [0,-1,-1,1,1]
x_num = 5
y_num = 6
detector_num = x_num*y_num
detector_size_x = xlen
detector_size_y = ylen
gap_x = gapx
gap_y = [gapy1,gapy2]
ra_ref = ra
dec_ref = dec
pa_aper = pa
pixel_size = psize
gap_y1_num = 3
gap_y2_num = 2
x_center = (detector_size_x*x_num+gap_x*(x_num-1))/2
y_center = (detector_size_y*y_num+gap_y[0]*gap_y1_num+gap_y[1]*gap_y2_num)/2
gap_y_map = np.array([[0,0,0,0,0],[gap_y[0],gap_y[1],gap_y[1],gap_y[1],gap_y[1]],[gap_y[1],gap_y[0],gap_y[0],gap_y[0],gap_y[0]],[gap_y[0],gap_y[0],gap_y[0],gap_y[0],gap_y[0]],[gap_y[0],gap_y[0],gap_y[0],gap_y[0],gap_y[1]],[gap_y[1],gap_y[1],gap_y[1],gap_y[1],gap_y[0]]])
frame_array = np.empty((5,6),dtype=np.float64)
# print(x_center,y_center)
j = row_num
i = col_num
ccdnum = str((j-1)*5+i)
x_ref, y_ref = (detector_size_x+gap_x)*i-gap_x-detector_size_x/2, detector_size_y*j + sum(gap_y_map[0:j,i-1]) - detector_size_y/2
# print(i,j,x_ref,y_ref,ra_ref,dec_ref)
name = []
value = []
description = []
for k in range(1,2):
cd = np.array([[ pixel_size, 0], [0, pixel_size]])/3600.*flag_x[k]
cd_rot = rotate_CD_matrix(cd, pa_aper)
# f = open("CCD"+ccdnum.rjust(2,'0')+"_extension"+str(k)+"_wcs.param","w")
name = ['EQUINOX',
'WCSDIM',
'CTYPE1',
'CTYPE2',
'CRVAL1',
'CRVAL2',
'CRPIX1',
'CRPIX2',
'CD1_1',
'CD1_2',
'CD2_1',
'CD2_2']
value = [2000.0,
2.0,
'RA---TAN',
'DEC--TAN',
ra_ref,
dec_ref,
flag_ext_x[k]*((x_ref+flag_ext_x[k]*detector_size_x/2)-x_center),
flag_ext_y[k]*((y_ref+flag_ext_y[k]*detector_size_y/2)-y_center),
cd_rot[0,0],
cd_rot[0,1],
cd_rot[1,0],
cd_rot[1,1]]
description = ['Equinox of WCS',
'WCS Dimensionality',
'Coordinate type',
'Coordinate typ',
'Coordinate reference value',
'Coordinate reference value',
'Coordinate reference pixel',
'Coordinate reference pixel',
'Coordinate matrix',
'Coordinate matrix',
'Coordinate matrix',
'Coordinate matrix']
return name, value, description
def generatePrimaryHeader(xlen = 9232, ylen = 9216,pointNum = '1', ra = 60, dec = -40, psize = 0.074, row_num = 1, col_num = 1):
# array_size1, array_size2, flux, sigma = int(argv[1]), int(argv[2]), 1000.0, 5.0
filerParm_fn = os.path.split(os.path.realpath(__file__))[0] + '/filter.lst'
f = open(filerParm_fn)
s = f.readline()
s = s.strip("\n")
filter = s.split(' ')
k = (row_num-1)*5+col_num
ccdnum = str(k)
g_header_fn = os.path.split(os.path.realpath(__file__))[0] + '/global_header.param'
name, value, description = read_header_parameter(g_header_fn)
h_prim = fits.Header()
date = '200930'
time_obs = '120000'
for i in range(len(name)):
if(name[i]=='FILTER'):
value[i] = filter[k-1]
if(name[i]=='FILENAME'):
value[i] = 'CSST_' + date + '_' +time_obs + '_' + pointNum.rjust(6,'0') + '_' +ccdnum.rjust(2,'0')+'_raw'
if(name[i]=='DETSIZE'):
value[i] = '[1:' + str(int(xlen)) + ',1:'+ str(int(ylen)) + ']'
if(name[i]=='PIXSCAL1'):
value[i] = str(psize)
if(name[i]=='PIXSCAL2'):
value[i] = str(psize)
h_prim[name[i]] = (value[i],description[i])
h_prim.add_comment('==================================================================',after='FILETYPE')
h_prim.add_comment('Target information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='EQUINOX')
h_prim.add_comment('Exposure information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='MJDEND')
h_prim.add_comment('Telescope information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='REFFRAME')
h_prim.add_comment('Detector information')
h_prim.add_comment('==================================================================')
h_prim.add_comment('==================================================================',after='FILTER')
h_prim.add_comment('Other information')
h_prim.add_comment('==================================================================')
return h_prim
def generateExtensionHeader(xlen = 9232, ylen = 9216,ra = 60, dec = -40, pa = -23.433, gain = 1.0, readout = 5.0, dark = 0.02, saturation=90000, psize = 0.074, row_num = 1, col_num = 1):
h_ext = fits.Header()
for i in range(1,2):
# NAXIS1:Number of pixels per row; NAXIS2:Number of rows
h_ext['NAXIS1'] = xlen
h_ext['NAXIS2'] = ylen
name, value, description = Header_extention(xlen = xlen, ylen = ylen, gain = gain, readout = readout, dark = dark, saturation=saturation, row_num = row_num, col_num = col_num)
for j in range(len(name)):
h_ext[name[j]] = (value[j],description[j])
name, value, description = WCS_def(xlen = xlen, ylen = ylen, gapx = 898.0, gapy1 = 534, gapy2 = 1309, ra = ra, dec = dec, pa = pa ,psize = psize, row_num = row_num, col_num = col_num)
for j in range(len(name)):
h_ext[name[j]] = (value[j],description[j])
h_ext.add_comment('==================================================================',after='OBSID')
h_ext.add_comment('Readout information')
h_ext.add_comment('==================================================================')
h_ext.add_comment('==================================================================',after='CHIPTEMP')
h_ext.add_comment('Chip information')
h_ext.add_comment('==================================================================')
h_ext.add_comment('==================================================================',after='DTM2_2')
h_ext.add_comment('WCS information')
h_ext.add_comment('==================================================================')
return h_ext
def main(argv):
xlen = int(argv[1])
ylen = int(argv[2])
pointingNum = argv[3]
ra = float(argv[4])
dec = float(argv[5])
pSize = float(argv[6])
ccd_row_num = int(argv[7])
ccd_col_num = int(argv[8])
pa_aper = float(argv[9])
gain = float(argv[10])
readout = float(argv[11])
dark = float(argv[12])
fw = float(argv[13])
h_prim = generatePrimaryHeader(xlen = xlen, ylen = ylen,ra = ra, dec = dec, psize = pSize, row_num = ccd_row_num, col_num = ccd_col_num, pointNum = pointingNum)
h_ext = generateExtensionHeader(xlen = xlen, ylen = ylen,ra = ra, dec = dec, pa = pa_aper, gain = gain, readout = readout, dark = dark, saturation=fw, psize = pSize, row_num = ccd_row_num, col_num = ccd_col_num)
hdu1 = fits.PrimaryHDU(header=h_prim)
hdu2 = fits.ImageHDU(np.zeros([ylen,xlen]),header = h_ext)
hdul = fits.HDUList([hdu1,hdu2])
hdul.writeto(h_prim['FILENAME']+'.fits',output_verify='ignore')
# if __name__ == "__main__":
# main(sys.argv)
from .ImageHeader import generatePrimaryHeader
from .ImageHeader import generateExtensionHeader
\ No newline at end of file
EXTNAME|IM1|Extension name
EXTVER|1|The ID number
BSCALE|1|
BZERO|8|
OBSID|CSST.20200101T000000|Observation ID
CCDNAME|CCD02|CCD name
AMPNAME|ccd02:B|Amplifier name
GAIN|1.00|Gain (e/ADU)
RDNOISE|5.00|Readout noise (e)
SATURATE|90000.0|Saturation (ADU)
RSPEED|10.0|Read speed (in MHz)
CHIPTEMP|-100.0|Chip temperature (in K)
CCDCHIP|1|CCD chip ID
CCDLABEL|GI-1|CCD chip label
HOSCAN|2000|Horizontal overscan width, per readout channel
VOSCAN|2000|Virtical overscan height, per readout channel
CCDSUM|1 1|CCD pixel summing
CCDSEC|[1:9216:9232]|CCD section
AMPSEC|[1:9216:9232]|Amplifier section
DATASEC|[1:9216,1: 9232]|Data section
DETSEC|[1:9216,1:9232]|Detector section
BIASSEC|[9216:9217,9232:9234]|Bias section
TRIMSEC|[1:9216,1:9232]|Trim section
WCSDIM|2|WCS dimensionality
EQUINOX|2000|Epoch (year)
CTYPE1|RA---TPV|Coordinate type
CTYPE2|DEC---TPV|Coordinate type
CRVAL1|1.00|Coordinate reference value
CRVAL2|1.00|Coordinate reference value
CRPIX1|1.00|Coordinate reference pixel
CRPIX2|1.00|Coordinate reference pixel
CD1_1|1|Coordinate matrix
CD2_1|0|Coordinate matrix
CD1_2|0|Coordinate matrix
CD2_2|1|Coordinate matrix
CHECKSUM|C65|SHA256 checksum of global headers
\ No newline at end of file
GV GI y z y GI GU r u NUV i GV GU g NUV NUV g GU GV i NUV u r GU GI y z y GI GV
3 3 3 1 1 1 3 2 2 1 1 1 4 2 3 2 1 1 4 2 4 1 1 2 4 2 2 4 2 2
26 21 16 11 6 1 27 22 17 12 7 2 28 23 18 13 8 3 29 24 19 14 9 4 30 25 20 15 10 5
DATE|2020-01-01|Date this file was written
FILENAME|CSST_01_999999|Name of file
FILETYPE|zero|Type of data
EXPNUM|99999999|Exposure sequence number
TARGNAME|CSST_|Observation title
RADECSYS|FK5|Default coordinate system
RADECEQ|2000.0|Default equinox
RA|00:00:00.00|RA of observation (hr)
DEC|+00:00:00.00|Dec of observation (deg)
EQUINOX|2000|Epoch(year)
TIMESYS|UT|Time system
DATE-OBS|2020-01-01|UTC date of start of observation
TIME-OBS|00:00:00|UTC time of start of observation
EXPTIME|0.000|Exposure time (sec)
MJD-OBS|58849.00000|MJD of observation start
MJDSTART|58849.00000|MJD of observation start
MJDEND|58849.00000|MJD of observation end
ST|16:32:58|Sidereal time
TELESCOP|CSST|Telescope name
TELRADEC|ICRS|Telescope coordinate system
TELEQUIN|2000|Equinox of tel coords
TELRA|00:00:00.00|RA of telescope (hr)
TELDEC|00:00:00.00|Dec of telescope (deg)
TELFOCUS|-9999|Telescope focus
SUNANGLE|30.00|Angle between sun and direction
MOONANGL|30.00|Angle between moon and direction
SUN_ALT|30.00|Sun altitude
REFFRAME|GSC1|guide star catalog version
INSTRUME|MBI|Instrument used to acquire data
DETSIZE|[1:9232,1:9216]|Detector size
NUMDETE|1|Number of detectors
NUMAMPS|4|Number of amplifiers
PIXSCAL1|0.074|Pixel scale for axis 1 (arcsec/pixel)
PIXSCAL2|0.074|Pixel scale for axis 2 (arcsec/pixel)
SHUTSTAT|closed|Shutter status
DETETEMP|-100.00|Detector temperature
CHECHSUM|abcdefg|checksum of global headers
ITL-HEAD|OK|ITL Header flag
N-AMPS-X|2|Number of amplifiers in X
N-AMPS-Y|2|Number of amplifiers in Y
from .Config import ConfigDir, ReadConfig, ParseConfig
from .ChipOutput import ChipOutput
\ No newline at end of file
import galsim
import os
import numpy as np
import Instrument.Chip.Effects as effects
from Instrument.FocalPlane import FocalPlane
from astropy.table import Table
from numpy.random import Generator, PCG64
class Chip(FocalPlane):
def __init__(self, chipID, ccdEffCurve_dir, CRdata_dir, normalize_dir=None, sls_dir=None, 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"]
# self.bias_level = config["bias_level"]
# self.overscan = config["overscan"]
# else:
# Default setting
self.npix_x = 9216
self.npix_y = 9232
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
# self.bias_level = 1000 # e-/pix
self.gain = float(config["gain"])
self.bias_level = float(config["bias_level"])
self.overscan = 1000
self.exptime = 150 # second
# A chip ID must be assigned
self.chipID = int(chipID)
self._getChipRowCol()
# Get corresponding filter info
self.filter_id, self.filter_type = self.getChipFilter()
self.survey_type = self._getSurveyType()
# Get boundary (in pix)
self.bound = self.getChipLim()
self.ccdEffCurve_dir = ccdEffCurve_dir
self.CRdata_dir = CRdata_dir
self.normalize_dir = normalize_dir
self.sls_dir=sls_dir
# self.sls_conf = os.path.join(self.sls_dir, self.getChipSLSConf())
slsconfs = self.getChipSLSConf()
if np.size(slsconfs) == 1:
self.sls_conf = [os.path.join(self.sls_dir, slsconfs)]
else:
self.sls_conf = [os.path.join(self.sls_dir, slsconfs[0]), os.path.join(self.sls_dir, slsconfs[1])]
if self.normalize_dir is not None:
self._getNormF()
self.effCurve = self._getChipEffCurve(self.filter_type)
self._getCRdata()
# Define the sensor
if config["bright_fatter"].lower() == "y":
self.sensor = galsim.SiliconSensor(strength=config["df_strength"], treering_func=treering_func)
else:
self.sensor = galsim.Sensor()
# def _getChipRowCol(self):
# self.rowID = (self.chipID - 1) // 5 + 1
# self.colID = (self.chipID - 1) % 5 + 1
def _getChipRowCol(self):
self.rowID, self.colID = self.getChipRowCol(self.chipID)
def getChipRowCol(self, chipID):
rowID = ((chipID - 1) % 5) + 1
colID = 6 - ((chipID - 1) // 5)
return rowID, colID
def _getSurveyType(self):
if self.filter_type in ["GI", "GV", "GU"]:
return "spectroscopic"
else:
return "photometric"
def _getNormF(self):
self.normF_star = Table.read(os.path.join(self.normalize_dir, 'SLOAN_SDSS.g.fits'))
self.normF_galaxy = Table.read(os.path.join(self.normalize_dir, 'lsst_throuput_g.fits'))
def _getChipEffCurve(self, filter_type):
# CCD efficiency curves
if filter_type in ['nuv', 'u', 'GU']: filename = 'UV0.txt'
if filter_type in ['g', 'r', 'GV']: filename = 'Astro_MB.txt'
if filter_type in ['i', 'z', 'y', 'GI']: filename = 'Basic_NIR.txt'
# Mirror efficiency:
if filter_type == 'nuv': mirror_eff = 0.54
if filter_type == 'u': mirror_eff = 0.68
if filter_type in ['g', 'r', 'i', 'z', 'y']: mirror_eff = 0.8
if filter_type in ['GU', 'GV', 'GI']: mirror_eff = 1. # Not sure if this is right
path = os.path.join(self.ccdEffCurve_dir, filename)
table = Table.read(path, format='ascii')
throughput = galsim.LookupTable(x=table['col1'], f=table['col2']*mirror_eff, interpolant='linear')
bandpass = galsim.Bandpass(throughput, wave_type='nm')
return bandpass
def _getCRdata(self):
path = os.path.join(self.CRdata_dir, 'wfc-cr-attachpixel.dat')
self.attachedSizes = np.loadtxt(path)
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","GU", "GV", "GI"]
# 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, 15, 16, 21]: filter_type = 'y'
# if chipID in [11, 20]: 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"
if chipID in [6, 15, 16, 25]: filter_type = "y"
if chipID in [11, 20]: filter_type = "z"
if chipID in [7, 24]: filter_type = "i"
if chipID in [14, 17]: filter_type = "u"
if chipID in [9, 22]: filter_type = "r"
if chipID in [12, 13, 18, 19]: filter_type = "nuv"
if chipID in [8, 23]: filter_type = "g"
if chipID in [1, 10, 21, 30]: filter_type = "GI"
if chipID in [2, 5, 26, 29]: filter_type = "GV"
if chipID in [3, 4, 27, 28]: 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
if chipID == None:
chipID = self.chipID
rowID, colID = self.rowID, self.colID
else:
rowID, colID = self.getChipRowCol(chipID)
gx1, gx2 = self.npix_gap_x
gy = self.npix_gap_y
# xlim of a given CCD chip
xrem = 2*(colID - 1) - (self.nchip_x - 1)
xcen = (self.npix_x//2 + gx1//2) * xrem
if chipID >= 26 or chipID == 21:
xcen = (self.npix_x//2 + gx1//2) * xrem - (gx2-gx1)
if chipID <= 5 or chipID == 10:
xcen = (self.npix_x//2 + gx1//2) * xrem + (gx2-gx1)
nx0 = xcen - self.npix_x//2 + 1
nx1 = xcen + self.npix_x//2
# ylim of a given CCD chip
yrem = (rowID - 1) - self.nchip_y // 2
ycen = (self.npix_y + gy) * yrem
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, wcs=None, margin=1):
# magin in number of pix
if wcs is None:
wcs = self.img.wcs
pos_obj = wcs.toImage(galsim.CelestialCoord(ra=ra_obj*galsim.degrees, dec=dec_obj*galsim.degrees))
xmin, xmax = self.bound.xmin - margin, self.bound.xmax + margin
ymin, ymax = self.bound.ymin - margin, self.bound.ymax + margin
if (pos_obj.x - xmin) * (pos_obj.x - xmax) > 0.0 or (pos_obj.y - ymin) * (pos_obj.y - ymax) > 0.0:
return False
return True
def getChipNoise(self, exptime=150.0):
noise = self.dark_noise * exptime + self.read_noise**2
return noise
def addNoise_phot(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
def addNoise_spec(self, config, tel, img, sky_map, exptime=150.0, seed=31415):
if img.array.shape != sky_map.shape:
raise ValueError("The shape img and sky_map must be equal.")
# n_img_arrar = (img.array + sky_map) * tel.pupil_area * exptime
# Should be the following?
rng = galsim.BaseDeviate(seed)
noise_img = galsim.Image(img, copy=True)
dark_noise = galsim.DeviateNoise(galsim.PoissonDeviate(rng, self.dark_noise*exptime))
img.addNoise(dark_noise)
if config["abs_back"].lower() == "y":
img += (sky_map * tel.pupil_area * exptime)
ccd_noise = galsim.CCDNoise(rng, gain=self.gain, read_noise=self.read_noise)
img.addNoise(ccd_noise)
return img
# pNoise = self.dark_noise * exptime
# noise_img = galsim.Image(n_img_arrar, copy=True)
# dNose = galsim.PoissonNoise(rng=rng, sky_level=pNoise)
# noise_img.addNoise(dNose)
# rdNoise = galsim.GaussianNoise(rng=rng, sigma=self.read_noise)
# noise_img.addNoise(rdNoise)
# return noise_img
def addNoise(self, config, tel, filt, img, sky_map=None, exptime=150.0):
if self.survey_type == "photometric":
sky_level = filt.getSkyNoise(exptime=exptime, gain=self.gain)
img = self.addNoise_phot(
img=img,
exptime=exptime,
sky_noise=filt.getSkyNoise(exptime=exptime, gain=self.gain),
seed=int(config["seed_skynoise"]))
if config["abs_back"].lower() == "y":
img += sky_level
elif self.survey_type == "spectroscopic":
if sky_map is None:
raise ValueError("Must provide sky_map for spectroscopic chip")
img = self.addNoise_spec(
config=config,
tel=tel,
img=img,
sky_map=sky_map,
exptime=exptime,
seed=int(config["seed_skynoise"]))
return img
def getChipSLSConf(self):
confFile = ''
if self.chipID == 1: confFile = ['CSST_GI2.conf', 'CSST_GI1.conf']
if self.chipID == 2: confFile = ['CSST_GV4.conf', 'CSST_GV3.conf']
if self.chipID == 3: confFile = ['CSST_GU2.conf', 'CSST_GU1.conf']
if self.chipID == 4: confFile = ['CSST_GU4.conf', 'CSST_GU3.conf']
if self.chipID == 5: confFile = ['CSST_GV2.conf', 'CSST_GV1.conf']
if self.chipID == 10: confFile = ['CSST_GI4.conf', 'CSST_GI3.conf']
if self.chipID == 21: confFile = ['CSST_GI6.conf', 'CSST_GI5.conf']
if self.chipID == 26: confFile = ['CSST_GV8.conf', 'CSST_GV7.conf']
if self.chipID == 27: confFile = ['CSST_GU6.conf', 'CSST_GU5.conf']
if self.chipID == 28: confFile = ['CSST_GU8.conf', 'CSST_GU7.conf']
if self.chipID == 29: confFile = ['CSST_GV6.conf', 'CSST_GV5.conf']
if self.chipID == 30: confFile = ['CSST_GI8.conf', 'CSST_GI7.conf']
return confFile
def addEffects(self, config, img, chip_output, filt, exptime=150., pointing_ID=0):
SeedGainNonuni=int(config["seed_gainNonUniform"])
SeedBiasNonuni=int(config["seed_biasNonUniform"])
SeedRnNonuni = int(config["seed_rnNonUniform"])
SeedBadColumns = int(config["seed_badcolumns"])
SeedDefective = int(config["seed_defective"])
SeedCosmicRay = int(config["seed_CR"])
fullwell = int(config["full_well"])
if config["add_hotpixels"].lower() == "y":
BoolHotPix = True
else:
BoolHotPix = False
if config["add_deadpixels"].lower() == "y":
BoolDeadPix = True
else:
BoolDeadPix = False
# Apply flat-field large scale structure for one chip
if config["flat_fielding"].lower() == "y":
print(" Creating and applying Flat-Fielding", flush=True)
print(img.bounds, flush=True)
flat_img = effects.MakeFlatSmooth(
img.bounds,
int(config["seed_flat"]))
flat_normal = flat_img / np.mean(flat_img.array)
img *= flat_normal
del flat_normal
if config["flat_output"].lower() == "n":
del flat_img
# Apply Shutter-effect for one chip
if config["shutter_effect"].lower() == "y":
print(" Apply shutter effect", flush=True)
shuttimg = effects.ShutterEffectArr(img, t_shutter=1.3, dist_bearing=735, dt=1E-3) # shutter effect normalized image for this chip
img *= shuttimg
if config["shutter_output"].lower() == "y": # output 16-bit shutter effect image with pixel value <=65535
shutt_gsimg = galsim.ImageUS(shuttimg*6E4)
shutt_gsimg.write("%s/ShutterEffect_%s_1.fits" % (chip_output.subdir, self.chipID))
del shutt_gsimg
del shuttimg
# Apply PRNU effect and output PRNU flat file:
if config["prnu_effect"].lower() == "y":
print(" Applying PRNU effect", flush=True)
prnu_img = effects.PRNU_Img(
xsize=self.npix_x,
ysize=self.npix_y,
sigma=0.01,
seed=int(config["seed_prnu"]+self.chipID))
img *= prnu_img
if config["prnu_output"].lower() == "y":
prnu_img.write("%s/FlatImg_PRNU_%s.fits" % (chip_output.subdir,self.chipID))
if config["flat_output"].lower() == "n":
del prnu_img
# Add dark current
img += self.dark_noise*exptime
# Add cosmic-rays
if config["cosmic_ray"].lower() == "y":
print(" Adding Cosmic-Ray", flush=True)
cr_map = effects.produceCR_Map(
xLen=self.npix_x, yLen=self.npix_y,
exTime=exptime,
cr_pixelRatio=0.003,
gain=self.gain,
attachedSizes=self.attachedSizes,
seed=SeedCosmicRay+pointing_ID*30+self.chipID)
img += cr_map
cr_map[cr_map > 65535] = 65535
cr_map[cr_map < 0] = 0
crmap_gsimg = galsim.Image(cr_map, dtype=np.uint16)
# crmap_gsimg.write("%s/CosmicRay_%s_1.fits" % (chip_output.subdir, self.chipID))
crmap_gsimg.write("%s/CosmicRay_%s.fits" % (chip_output.subdir, self.chipID))
del crmap_gsimg
# Add Bias level
if config["add_bias"].lower() == "y":
print(" Adding Bias level and 16-channel non-uniformity")
# img += float(config["bias_level"])
img = effects.AddBiasNonUniform16(img,
bias_level=float(config["bias_level"]),
nsecy = 2, nsecx=8,
seed=SeedBiasNonuni+self.chipID)
# Bias output
if config["bias_output"].lower() == "y":
print(" Output N frame Bias files", flush=True)
NBias = int(config["NBias"])
for i in range(NBias):
BiasCombImg, BiasTag = effects.MakeBiasNcomb(
self.npix_x, self.npix_y,
bias_level=float(config["bias_level"]),
ncombine=1, read_noise=self.read_noise, gain=1,
seed=SeedBiasNonuni+self.chipID)
# Non-Linearity for Bias
if config["non_linear"].lower() == "y":
print(" Applying Non-Linearity on the Bias image", flush=True)
BiasCombImg = effects.NonLinearity(GSImage=BiasCombImg, beta1=1.e-7, beta2=1.e-10)
BiasCombImg = effects.ApplyGainNonUniform16(BiasCombImg, gain=self.gain,
nsecy = 2, nsecx=8,
seed=SeedGainNonuni+self.chipID)
# BiasCombImg = effects.AddOverscan(
# BiasCombImg,
# overscan=float(config["bias_level"])-2, gain=self.gain,
# widthl=27, widthr=27, widtht=8, widthb=8)
BiasCombImg.replaceNegative(replace_value=0)
BiasCombImg.quantize()
BiasCombImg = galsim.ImageUS(BiasCombImg)
BiasCombImg.write("%s/BiasImg_%s_%s_%s.fits" % (chip_output.subdir, BiasTag, self.chipID, i+1))
del BiasCombImg
# Export combined (ncombine, Vignetting + PRNU) & single vignetting flat-field file
if config["flat_output"].lower() == "y":
print(" Output N frame Flat-Field files", flush=True)
NFlat = int(config["NFlat"])
if config["add_bias"].lower() == "y":
biaslevel = self.bias_level
overscan = biaslevel-2
elif config["add_bias"].lower() == "n":
biaslevel = 0
overscan = 0
darklevel = self.dark_noise*self.exptime
for i in range(NFlat):
FlatSingle = flat_img * prnu_img + darklevel
FlatCombImg, FlatTag = effects.MakeFlatNcomb(
flat_single_image=FlatSingle,
ncombine=1,
read_noise=self.read_noise,
gain=1,
overscan=overscan,
biaslevel=biaslevel,
seed_bias=SeedDefective+self.chipID
)
if config["cosmic_ray"].lower() == "y":
FlatCombImg += cr_map
if config["non_linear"].lower() == "y":
print(" Applying Non-Linearity on the Flat image", flush=True)
FlatCombImg = effects.NonLinearity(GSImage=FlatCombImg, beta1=1.e-7, beta2=1.e-10)
if config["cte_trail"].lower() == "y":
FlatCombImg = effects.CTE_Effect(GSImage=FlatCombImg, threshold=3)
# Apply Bad lines
if config["add_badcolumns"].lower() == "y":
FlatCombImg = effects.BadColumns(FlatCombImg, seed=SeedBadColumns, chipid=self.chipID)
# Add Hot Pixels or/and Dead Pixels
rgbadpix = Generator(PCG64(int(SeedDefective+self.chipID)))
badfraction = 5E-5*(rgbadpix.random()*0.5+0.7)
FlatCombImg = effects.DefectivePixels(FlatCombImg, IfHotPix=BoolHotPix, IfDeadPix=BoolDeadPix, fraction=badfraction, seed=SeedDefective+self.chipID, biaslevel=self.bias_level)
FlatCombImg = effects.ApplyGainNonUniform16(FlatCombImg, gain=self.gain,
nsecy = 2, nsecx=8,
seed=SeedGainNonuni+self.chipID)
# FlatCombImg = effects.AddOverscan(FlatCombImg, overscan=overscan, gain=self.gain, widthl=27, widthr=27, widtht=8, widthb=8)
FlatCombImg.replaceNegative(replace_value=0)
FlatCombImg.quantize()
FlatCombImg = galsim.ImageUS(FlatCombImg)
FlatCombImg.write("%s/FlatImg_%s_%s_%s.fits" % (chip_output.subdir, FlatTag, self.chipID, i+1))
del FlatCombImg, FlatSingle, prnu_img
# flat_img.replaceNegative(replace_value=0)
# flat_img.quantize()
# galsim.ImageUS(flat_img).write("%s/FlatImg_Vignette_%s.fits" % (chip_output.subdir, self.chipID))
del flat_img
# Export Dark current images
if config["dark_output"].lower() == "y":
print(" Output N frame Dark Current files", flush=True)
NDark = int(config["NDark"])
if config["add_bias"].lower() == "y":
biaslevel = self.bias_level
overscan = biaslevel-2
elif config["add_bias"].lower() == "n":
biaslevel = 0
overscan = 0
for i in range(NDark):
DarkCombImg, DarkTag = effects.MakeDarkNcomb(
self.npix_x, self.npix_y,
overscan=overscan, bias_level=biaslevel, darkpsec=0.02, exptime=150,
ncombine=1, read_noise=self.read_noise,
gain=1, seed_bias=SeedBiasNonuni+self.chipID)
if config["cosmic_ray"].lower() == "y":
DarkCombImg += cr_map
# Non-Linearity for Dark
if config["non_linear"].lower() == "y":
print(" Applying Non-Linearity on the Dark image", flush=True)
DarkCombImg = effects.NonLinearity(GSImage=DarkCombImg, beta1=1.e-7, beta2=1.e-10)
if config["cte_trail"].lower() == "y":
DarkCombImg = effects.CTE_Effect(GSImage=DarkCombImg, threshold=3)
# Apply Bad lines
if config["add_badcolumns"].lower() == "y":
DarkCombImg = effects.BadColumns(DarkCombImg, seed=SeedBadColumns, chipid=self.chipID)
# Add Hot Pixels or/and Dead Pixels
rgbadpix = Generator(PCG64(int(SeedDefective+self.chipID)))
badfraction = 5E-5*(rgbadpix.random()*0.5+0.7)
DarkCombImg = effects.DefectivePixels(DarkCombImg, IfHotPix=BoolHotPix, IfDeadPix=BoolDeadPix, fraction=badfraction, seed=SeedDefective+self.chipID, biaslevel=self.bias_level)
DarkCombImg = effects.ApplyGainNonUniform16(
DarkCombImg, gain=self.gain,
nsecy = 2, nsecx=8,
seed=SeedGainNonuni+self.chipID)
# DarkCombImg = effects.AddOverscan(
# DarkCombImg,
# overscan=overscan, gain=self.gain,
# widthl=27, widthr=27, widtht=8, widthb=8)
DarkCombImg.replaceNegative(replace_value=0)
DarkCombImg.quantize()
DarkCombImg = galsim.ImageUS(DarkCombImg)
DarkCombImg.write("%s/DarkImg_%s_%s_%s.fits" % (chip_output.subdir, DarkTag, self.chipID, i+1))
del DarkCombImg
# garbage collection of cosmic-ray array
if config["cosmic_ray"].lower() == "y":
del cr_map
# Apply Nonlinearity on the chip image
if config["non_linear"].lower() == "y":
print(" Applying Non-Linearity on the chip image", flush=True)
img = effects.NonLinearity(GSImage=img, beta1=1.e-7, beta2=1.e-10)
# Apply CCD Saturation & Blooming
if config["saturbloom"].lower() == "y":
print(" Applying CCD Saturation & Blooming")
img = effects.SaturBloom(GSImage=img, nsect_x=1, nsect_y=1, fullwell=fullwell)
# Apply CTE Effect
if config["cte_trail"].lower() == "y":
print(" Apply CTE Effect")
img = effects.CTE_Effect(GSImage=img, threshold=27)
# Apply Bad lines
if config["add_badcolumns"].lower() == "y":
img = effects.BadColumns(img, seed=SeedBadColumns, chipid=self.chipID)
# Add Hot Pixels or/and Dead Pixels
rgbadpix = Generator(PCG64(int(SeedDefective+self.chipID)))
badfraction = 5E-5*(rgbadpix.random()*0.5+0.7)
img = effects.DefectivePixels(img, IfHotPix=BoolHotPix, IfDeadPix=BoolDeadPix, fraction=badfraction, seed=SeedDefective+self.chipID, biaslevel=self.bias_level)
# Apply Gain & Quantization
print(" Applying Gain (and 16 channel non-uniformity) & Quantization", flush=True)
img = effects.ApplyGainNonUniform16(
img, gain=self.gain,
nsecy = 2, nsecx=8,
seed=SeedGainNonuni+self.chipID)
img.array[img.array > 65535] = 65535
img.replaceNegative(replace_value=0)
img.quantize()
# img = galsim.ImageUS(img)
# # 16 output channel, with each a single image file
# if config["readout16"].lower() == "y":
# print(" 16 Output Channel simulation")
# for coli in [0, 1]:
# for rowi in range(8):
# sub_img = effects.readout16(
# GSImage=img,
# rowi=rowi,
# coli=coli,
# overscan_value=self.overscan)
# rowcoltag = str(rowi) + str(coli)
# img_name_root = chip_output.img_name.split(".")[0]
# sub_img.write("%s/%s_%s.fits" % (chip_output.subdir, img_name_root, rowcoltag))
# del sub_img
return img
\ No newline at end of file
import galsim
from matplotlib.pyplot import flag
import numpy as np
from numpy.core.fromnumeric import mean, size
from numpy.random import Generator, PCG64
import math
from numba import jit
def AddOverscan(GSImage, overscan=1000, gain=1, widthl=27, widthr=27, widtht=8, widthb=8, read_noise=5):
"""
Add overscan/gain; gain=e-/ADU
widthl: left pre-scan width
widthr: right pre-scan width
widtht: top over-scan width (the top of nd-array with small row-index)
widthb: bottom over-scan width (the bottom of nd-array with large row-index)
"""
imgshape = GSImage.array.shape
newimg = galsim.Image(imgshape[1]+widthl+widthr, imgshape[0]+widtht+widthb, init_value=0)
rng = galsim.UniformDeviate()
NoiseOS = galsim.GaussianNoise(rng, sigma=read_noise)
newimg.addNoise(NoiseOS)
newimg = (newimg+overscan)/gain
newimg.array[widtht:(widtht+imgshape[0]),widthl:(widthl+imgshape[1])] = GSImage.array
newimg.wcs = GSImage.wcs
# if GSImage.wcs is not None:
# newwcs = GSImage.wcs.withOrigin(galsim.PositionD(widthl,widtht))
# newimg.wcs = newwcs
# else:
# pass
return newimg
def DefectivePixels(GSImage, IfHotPix=True, IfDeadPix=True, fraction=1E-4, seed=20210304, biaslevel=500):
# Also called bad pixels, including hot pixels and dead pixels
# Hot Pixel > 20e-/s
# Dead Pixel < 70%*Mean
rgf = Generator(PCG64(int(seed*1.1)))
if IfHotPix==True and IfDeadPix==True:
HotFraction = rgf.random() # fraction in total bad pixels
elif IfHotPix==False and IfDeadPix==False:
return GSImage
elif IfHotPix==True:
HotFraction = 1
else:
HotFraction = 0
NPix = GSImage.array.size
NPixBad = int(NPix*fraction)
NPixHot = int(NPix*fraction*HotFraction)
NPixDead = NPixBad-NPixHot
NPix_y,NPix_x = GSImage.array.shape
mean = np.mean(GSImage.array)
rgp = Generator(PCG64(int(seed)))
yxposfrac = rgp.random((NPixBad,2))
YPositHot = np.array(NPix_y*yxposfrac[0:NPixHot,0]).astype(np.int32)
XPositHot = np.array(NPix_x*yxposfrac[0:NPixHot,1]).astype(np.int32)
YPositDead = np.array(NPix_y*yxposfrac[NPixHot:,0]).astype(np.int32)
XPositDead = np.array(NPix_x*yxposfrac[NPixHot:,1]).astype(np.int32)
rgh = Generator(PCG64(int(seed*1.2)))
rgd = Generator(PCG64(int(seed*1.3)))
if IfHotPix==True:
GSImage.array[YPositHot,XPositHot] += rgh.gamma(2,25*150,size=NPixHot)
if IfDeadPix==True:
GSImage.array[YPositDead,XPositDead] = rgd.random(NPixDead)*(mean-biaslevel)*0.7+biaslevel+rgp.standard_normal()*5
return GSImage
def BadColumns(GSImage, seed=20210309, chipid=1):
# Set bad column values
ysize,xsize = GSImage.array.shape
subarr = GSImage.array[int(ysize*0.1):int(ysize*0.12), int(xsize*0.1):int(xsize*0.12)]
meanimg = np.median(subarr)
stdimg = np.std(subarr)
seed += chipid
rgn = Generator(PCG64(int(seed)))
rgcollen = Generator(PCG64(int(seed*1.1)))
rgxpos = Generator(PCG64(int(seed*1.2)))
rgdn = Generator(PCG64(int(seed*1.3)))
nbadsecA,nbadsecD = rgn.integers(low=1, high=5, size=2)
collen = rgcollen.integers(low=int(ysize*0.1), high=int(ysize*0.7), size=(nbadsecA+nbadsecD))
xposit = rgxpos.integers(low=int(xsize*0.05), high=int(xsize*0.95), size=(nbadsecA+nbadsecD))
signs = 2*rgdn.integers(0,2,size=(nbadsecA+nbadsecD))-1
dn = rgdn.integers(low=meanimg*0.4, high=meanimg*0.8, size=(nbadsecA+nbadsecD))*signs
for badcoli in range(nbadsecA):
GSImage.array[(ysize-collen[badcoli]):ysize,xposit[badcoli]:(xposit[badcoli]+1)] += np.random.random((collen[badcoli],1))*stdimg*3+dn[badcoli]
for badcoli in range(nbadsecD):
GSImage.array[0:collen[badcoli+nbadsecA],xposit[badcoli+nbadsecA]:(xposit[badcoli+nbadsecA]+1)] += np.random.random((collen[badcoli+nbadsecA],1))*stdimg*3+dn[badcoli+nbadsecA]
return GSImage
def AddBiasNonUniform16(GSImage, bias_level = 500, nsecy = 2, nsecx=8, seed=202102):
# Generate Bias and its non-uniformity, and add the 16 bias values to the GS-Image
rg = Generator(PCG64(int(seed)))
Random16 = (rg.random(nsecy*nsecx)-0.5)*20
if int(bias_level)==0:
BiasLevel = np.zeros((nsecy,nsecx))
elif bias_level>0:
BiasLevel = Random16.reshape((nsecy,nsecx)) + bias_level
arrshape = GSImage.array.shape
secsize_x = int(arrshape[1]/nsecx)
secsize_y = int(arrshape[0]/nsecy)
for rowi in range(nsecy):
for coli in range(nsecx):
GSImage.array[rowi*secsize_y:(rowi+1)*secsize_y,coli*secsize_x:(coli+1)*secsize_x] += BiasLevel[rowi,coli]
return GSImage
def MakeBiasNcomb(npix_x, npix_y, bias_level=500, ncombine=1, read_noise=5, gain=1, seed=202102):
# Start with 0 value bias GS-Image
ncombine=int(ncombine)
BiasSngImg0 = galsim.Image(npix_x, npix_y, init_value=0)
BiasSngImg = AddBiasNonUniform16(BiasSngImg0,
bias_level=bias_level,
nsecy = 2, nsecx=8,
seed=int(seed))
BiasCombImg = BiasSngImg*ncombine
rng = galsim.UniformDeviate()
NoiseBias = galsim.GaussianNoise(rng=rng, sigma=read_noise*ncombine**0.5)
BiasCombImg.addNoise(NoiseBias)
if ncombine == 1:
BiasTag = 'Single'
pass
elif ncombine >1:
BiasCombImg /= ncombine
BiasTag = 'Combine'
# BiasCombImg.replaceNegative(replace_value=0)
# BiasCombImg.quantize()
return BiasCombImg, BiasTag
def ApplyGainNonUniform16(GSImage, gain=1, nsecy = 2, nsecx=8, seed=202102):
# Generate Gain non-uniformity, and multipy the different factors (mean~1 with sigma~1%) to the GS-Image
rg = Generator(PCG64(int(seed)))
Random16 = (rg.random(nsecy*nsecx)-0.5)*0.04+1 # sigma~1%
Gain16 = Random16.reshape((nsecy,nsecx))/gain
print("Gain of 16 channels: ",Gain16)
arrshape = GSImage.array.shape
secsize_x = int(arrshape[1]/nsecx)
secsize_y = int(arrshape[0]/nsecy)
for rowi in range(nsecy):
for coli in range(nsecx):
GSImage.array[rowi*secsize_y:(rowi+1)*secsize_y,coli*secsize_x:(coli+1)*secsize_x] *= Gain16[rowi,coli]
return GSImage
def MakeFlatSmooth(GSBounds, seed):
rg = Generator(PCG64(int(seed)))
r1,r2,r3,r4 = rg.random(4)
a1 = -0.5 + 0.2*r1
a2 = -0.5 + 0.2*r2
a3 = r3+5
a4 = r4+5
xmin,xmax,ymin,ymax = GSBounds.getXMin(), GSBounds.getXMax(), GSBounds.getYMin(), GSBounds.getYMax()
Flty, Fltx = np.mgrid[ymin:(ymax+1), xmin:(xmax+1)]
rg = Generator(PCG64(int(seed)))
p1,p2,bg=rg.poisson(1000, 3)
Fltz = 1e-6*(a1 * (Fltx-p1) ** 2 + a2 * (Flty-p2) ** 2 - a3*Fltx - a4*Flty) + bg*20
FlatImg = galsim.ImageF(Fltz)
return FlatImg
def MakeFlatNcomb(flat_single_image, ncombine=1, read_noise=5, gain=1, overscan=500, biaslevel=500, seed_bias=20210311):
ncombine=int(ncombine)
FlatCombImg = flat_single_image*ncombine
rng = galsim.UniformDeviate()
NoiseFlatPoi = galsim.PoissonNoise(rng=rng, sky_level=0)
FlatCombImg.addNoise(NoiseFlatPoi)
NoiseFlatReadN = galsim.GaussianNoise(rng=rng, sigma=read_noise*ncombine**0.5)
FlatCombImg.addNoise(NoiseFlatReadN)
# NoiseFlat = galsim.CCDNoise(rng, gain=gain, read_noise=read_noise*ncombine**0.5, sky_level=0)
for i in range(ncombine):
FlatCombImg = AddBiasNonUniform16(
FlatCombImg,
bias_level=biaslevel,
nsecy=2, nsecx=8,
seed=seed_bias)
if ncombine == 1:
FlatTag = 'Single'
pass
elif ncombine >1:
FlatCombImg /= ncombine
FlatTag = 'Combine'
# FlatCombImg.replaceNegative(replace_value=0)
# FlatCombImg.quantize()
return FlatCombImg, FlatTag
def MakeDarkNcomb(npix_x, npix_y, overscan=500, bias_level=500, seed_bias=202102, darkpsec=0.02, exptime=150, ncombine=10, read_noise=5, gain=1):
ncombine=int(ncombine)
darkpix = darkpsec*exptime
DarkSngImg = galsim.Image(npix_x, npix_y, init_value=darkpix)
rng = galsim.UniformDeviate()
NoiseDarkPoi = galsim.PoissonNoise(rng=rng, sky_level=0)
NoiseReadN = galsim.GaussianNoise(rng=rng, sigma=read_noise*ncombine**0.5)
DarkCombImg = DarkSngImg*ncombine
DarkCombImg.addNoise(NoiseDarkPoi)
DarkCombImg.addNoise(NoiseReadN)
for i in range(ncombine):
DarkCombImg = AddBiasNonUniform16(
DarkCombImg,
bias_level=bias_level,
nsecy = 2, nsecx=8,
seed=int(seed_bias))
if ncombine == 1:
DarkTag = 'Single'
pass
elif ncombine >1:
DarkCombImg /= ncombine
DarkTag = 'Combine'
# DarkCombImg.replaceNegative(replace_value=0)
# DarkCombImg.quantize()
return DarkCombImg, DarkTag
def PRNU_Img(xsize, ysize, sigma=0.01, seed=202101):
rg = Generator(PCG64(int(seed)))
prnuarr = rg.normal(1, sigma, (ysize,xsize))
prnuimg = galsim.ImageF(prnuarr)
return prnuimg
def NonLinearity(GSImage, beta1=1E-7, beta2=1E-10):
NonLinear_f = lambda x, beta_1, beta_2: x - beta_1*x*x + beta_2*x*x*x
GSImage.applyNonlinearity(NonLinear_f, beta1, beta2)
return GSImage
def chargeflow(ndarr, fullwell=10E4):
size_y,size_x = ndarr.shape
satpos_y = np.where(ndarr>=fullwell)[0]
satpos_x = np.where(ndarr>=fullwell)[1]
Nsatpix = len(satpos_y)
if Nsatpix==0:
# make no change for the image array
return ndarr
else:
for i in range(Nsatpix):
ClumpFullwell=True
satcharge = ndarr[satpos_y[i],satpos_x[i]]-fullwell
ndarr[satpos_y[i],satpos_x[i]] = fullwell
satpos_yi0 = satpos_y[i]
# Define the x,y=0,0 element of image array is the lower-left corner. So y decreases being downward.
# print('Charge Clump moves down')
chargedn = ((np.random.random()-0.5)*0.05+0.5)*satcharge
chargeup = satcharge - chargedn
fwi = 1
aa = np.log(chargedn/fullwell)**3*0.9 # blooming length begin to has e- less than fullwell
if aa < 0.05:
ClumpFullwell=False
# Test
try:
while ClumpFullwell==True:
if satpos_y[i]<=0:
break
if ndarr[satpos_y[i]-1,satpos_x[i]]<fullwell:
ndarr[satpos_y[i]-1,satpos_x[i]] = ndarr[satpos_y[i]-1,satpos_x[i]] + chargedn
if ndarr[satpos_y[i]-1,satpos_x[i]]>=fullwell:
fx = 0.5*(math.exp(math.log(fwi)**3/aa)+np.exp(-1*math.log(fwi)**3/aa))
if fx>5:
fx=5
faa= 0.5*(math.exp(aa/aa)+np.exp(-1*aa/aa))
rand_frac = 1-0.1*(fx-1)/(faa-1)
chargedn = ndarr[satpos_y[i]-1,satpos_x[i]] - fullwell
ndarr[satpos_y[i]-1,satpos_x[i]] = fullwell*rand_frac
satpos_y[i] = satpos_y[i]-1
if satpos_y[i]<0:
ClumpFullwell=False
break
else:
ClumpFullwell=False
fwi += 1
else:
satpos_y[i] = satpos_y[i]-1
if satpos_y[i]<0:
ClumpFullwell=False
break
# print('Charge Clump moves up')
ClumpFullwell=True
satpos_y[i] = satpos_yi0
fwi = 1
aa = np.log(chargeup/fullwell)**3*0.9 # blooming length at which it begins to have e- less than fullwell
if aa < 0.05:
ClumpFullwell=False
while ClumpFullwell==True:
if satpos_y[i]>=size_y-1:
break
if ndarr[satpos_y[i]+1,satpos_x[i]]<fullwell:
ndarr[satpos_y[i]+1,satpos_x[i]] = ndarr[satpos_y[i]+1,satpos_x[i]] + chargeup
if ndarr[satpos_y[i]+1,satpos_x[i]]>=fullwell:
fx = 0.5*(math.exp(math.log(fwi)**3/aa)+np.exp(-1*math.log(fwi)**3/aa))
if fx>5:
fx=5
faa= 0.5*(math.exp(aa/aa)+np.exp(-1*aa/aa))
rand_frac = 1-0.1*(fx-1)/(faa-1)
chargeup = ndarr[satpos_y[i]+1,satpos_x[i]] - fullwell
ndarr[satpos_y[i]+1,satpos_x[i]] = fullwell*rand_frac
satpos_y[i] = satpos_y[i]+1
if satpos_y[i]>=size_y-1:
ClumpFullwell=False
break
else:
ClumpFullwell=False
fwi += 1
else:
satpos_y[i] = satpos_y[i]+1
if satpos_y[i]>=size_y-1:
ClumpFullwell=False
break
except Exception as e:
print(e)
print(fwi, aa)
pass
return ndarr
def SaturBloom(GSImage, nsect_x=1, nsect_y=1, fullwell=10e4):
"""
To simulate digital detector's saturation and blooming effect. The blooming is along the read-out direction, perpendicular to the charge transfer direction. Charge clumpy overflows the pixel well will flow to two oposite directions with nearly same charges.
Work together with chargeflow() function.
Parameters:
GSImage: a GalSim Image of a chip;
nsect_x: number of sections divided along x direction;
nsect_y: number of sections divided along y direction.
Return: a saturated image array with the same size of input.
"""
imgarr = GSImage.array
size_y, size_x = imgarr.shape
subsize_y = int(size_y/nsect_y)
subsize_x = int(size_x/nsect_x)
for i in range(nsect_y):
for j in range(nsect_x):
subimg = imgarr[subsize_y*i:subsize_y*(i+1), subsize_x*j:subsize_x*(j+1)]
subimg = chargeflow(subimg, fullwell=fullwell)
imgarr[subsize_y*i:subsize_y*(i+1), subsize_x*j:subsize_x*(j+1)] = subimg
return GSImage
def readout16(GSImage, rowi=0, coli=0, overscan_value=0):
# readout image as 16 outputs of sub-images plus prescan & overscan.
# assuming image width and height sizes are both even.
# assuming image has 2 columns and 8 rows of output channels.
# 00 01
# 10 11
# 20 21
# ...
# return: GS Image Object
npix_y,npix_x = GSImage.array.shape
subheight = int(8+npix_y/2+8)
subwidth = int(16+npix_x/8+27)
OutputSubimg = galsim.ImageUS(subwidth, subheight, init_value=overscan_value)
if rowi<4 and coli==0:
subbounds = galsim.BoundsI(1, int(npix_x/2), int(npix_y/8*rowi+1), int(npix_y/8*(rowi+1)))
subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1))
subimg = GSImage[subbounds]
OutputSubimg.array[27:int(npix_y/8)+27,8:int(npix_x/2)+8] = subimg.array
elif rowi<4 and coli==1:
subbounds = galsim.BoundsI(npix_x/2+1, npix_x, npix_y/8*rowi+1, npix_y/8*(rowi+1))
subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1))
subimg = GSImage[subbounds]
OutputSubimg.array[27:int(npix_y/8)+27,8:int(npix_x/2)+8] = subimg.array
elif rowi>=4 and rowi<8 and coli==0:
subbounds = galsim.BoundsI(1, npix_x/2, npix_y/8*rowi+1, npix_y/8*(rowi+1))
subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1))
subimg = GSImage[subbounds]
OutputSubimg.array[16:int(npix_y/8)+16,8:int(npix_x/2)+8] = subimg.array
elif rowi>=4 and rowi<8 and coli==1:
subbounds = galsim.BoundsI(npix_x/2+1, npix_x, npix_y/8*rowi+1, npix_y/8*(rowi+1))
subbounds = subbounds.shift(galsim.PositionI(GSImage.bounds.getXMin()-1, GSImage.bounds.getYMin()-1))
subimg = GSImage[subbounds]
OutputSubimg.array[16 :int(npix_y/8)+16,8:int(npix_x/2)+8] = subimg.array
else:
print("\n\033[31mError: "+"Wrong rowi or coli assignment. Permitted: 0<=rowi<=7, 0<=coli<=1."+"\033[0m\n")
return OutputSubimg
return OutputSubimg
def CTE_Effect(GSImage, threshold=27, direction='column'):
# Devide the image into 4 sections and apply CTE effect with different trail directions.
# GSImage: a GalSim Image object.
size_y,size_x = GSImage.array.shape
size_sect_y = int(size_y/2)
size_sect_x = int(size_x/2)
imgarr = GSImage.array
if direction == 'column':
imgarr[0:size_sect_y,:] = CTEModelColRow(imgarr[0:size_sect_y,:], trail_direction='down', direction='column', threshold=threshold)
imgarr[size_sect_y:size_y,:] = CTEModelColRow(imgarr[size_sect_y:size_y,:], trail_direction='up', direction='column', threshold=threshold)
elif direction == 'row':
imgarr[:,0:size_sect_x] = CTEModelColRow(imgarr[:,0:size_sect_x], trail_direction='right', direction='row', threshold=threshold)
imgarr[:,size_sect_x:size_x] = CTEModelColRow(imgarr[:,size_sect_x:size_x], trail_direction='left', direction='row', threshold=threshold)
return GSImage
@jit()
def CTEModelColRow(img, trail_direction = 'up', direction='column', threshold=27):
#total trail flux vs (pixel flux)^1/2 is approximately linear
#total trail flux = trail_a * (pixel flux)^1/2 + trail_b
#trail pixel flux = pow(0.5,x)/0.5, normalize to 1
trail_a = 5.651803799619966
trail_b = -2.671933068990729
sh1 = img.shape[0]
sh2 = img.shape[1]
n_img = img*0
idx = np.where(img<threshold)
if len(idx[0]) == 0:
pass
elif len(idx[0])>0:
n_img[idx] = img[idx]
yidx,xidx = np.where(img>=threshold)
if len(yidx) == 0:
pass
elif len(yidx)>0:
# print(index)
for i, j in zip(yidx,xidx):
f = img[i,j]
trail_f = (np.sqrt(f)*trail_a + trail_b)*0.5
# trail_f=5E-5*f**1.5
xy_num = 10
all_trail = np.zeros(xy_num)
xy_upstr = np.arange(1,xy_num,1)
# all_trail_pix = np.sum(pow(0.5,xy_upstr)/0.5)
all_trail_pix = 0
for m in xy_upstr:
a1=12.97059491
b1=0.54286652
c1=0.69093105
a2=2.77298856
b2=0.11231055
c2=-0.01038675
# t_pow = 0
am=1
bm=1
t_pow = am*np.exp(-bm*m)
# if m < 5:
# t_pow = a1*np.exp(-b1*m)+c1
# else:
# t_pow = a2*np.exp(-b2*m)+c2
if t_pow <0:
t_pow = 0
all_trail_pix += t_pow
all_trail[m] = t_pow
trail_pix_eff = trail_f/all_trail_pix
all_trail = trail_pix_eff*all_trail
all_trail[0] = f - trail_f
for m in np.arange(0,xy_num,1):
if direction == 'column':
if trail_direction == 'down':
y_pos = i + m
elif trail_direction == 'up':
y_pos = i - m
if y_pos < 0 or y_pos >=sh1:
break
n_img[y_pos,j] = n_img[y_pos,j] + all_trail[m]
elif direction == 'row':
if trail_direction == 'left':
x_pos = j - m
elif trail_direction == 'right':
x_pos = j + m
if x_pos < 0 or x_pos >=sh2:
break
n_img[i,x_pos] = n_img[i,x_pos] + all_trail[m]
return n_img
#---------- For Cosmic-Ray Simulation ------------
#---------- Zhang Xin ----------------------------
def getYValue(collection, x):
index = 0;
if (collection.shape[1] == 2):
while(x>collection[index,0] and index < collection.shape[0]):
index= index + 1;
if (index == collection.shape[0] or index == 0):
return 0;
deltX = collection[index, 0] - collection[index-1, 0];
deltY = collection[index, 1] - collection[index-1, 1];
if deltX == 0:
return (collection[index, 1] + collection[index-1, 1])/2.0
else:
a = deltY/deltX;
return a * (x - collection[index-1,0]) + collection[index-1, 1];
return 0;
def selectCosmicRayCollection(attachedSizes, xLen, yLen,cr_pixelRatio,CR_max_size):
normalRay = 0.90
nnormalRay = 1-normalRay
max_nrayLen = 100
pixelNum = int(xLen * yLen * cr_pixelRatio * normalRay );
pixelNum_n = int(xLen * yLen * cr_pixelRatio * nnormalRay )
CRPixelNum = 0;
maxValue = max(attachedSizes[:,1])
maxValue += 0.1;
cr_event_num = 0;
CRs = np.zeros(pixelNum);
while (CRPixelNum < pixelNum):
x = CR_max_size * np.random.random();
y = maxValue * np.random.random();
if (y <= getYValue(attachedSizes, x)):
CRs[cr_event_num] = np.ceil(x);
cr_event_num = cr_event_num + 1;
CRPixelNum = CRPixelNum + round(x);
while (CRPixelNum < pixelNum + pixelNum_n):
nx = np.random.random()*(max_nrayLen-CR_max_size)+CR_max_size
CRs[cr_event_num] = np.ceil(nx);
cr_event_num = cr_event_num + 1;
CRPixelNum = CRPixelNum + np.ceil(nx);
return CRs[0:cr_event_num];
def defineEnergyForCR(cr_event_size):
import random
sigma = 0.6 / 2.355;
mean = 3.3;
energys = np.zeros(cr_event_size);
for i in np.arange(cr_event_size):
energy_index = random.normalvariate(mean,sigma);
energys[i] = pow(10, energy_index);
return energys;
def convCR(CRmap=None, addPSF=None, sp_n = 4):
sh = CRmap.shape
# sp_n = 4
subCRmap = np.zeros(np.array(sh)*sp_n)
pix_v0 = 1/(sp_n*sp_n)
for i in np.arange(sh[0]):
i_st = sp_n*i
for j in np.arange(sh[1]):
if CRmap[i,j] ==0:
continue
j_st = sp_n*j
pix_v1 = CRmap[i,j]*pix_v0
for m in np.arange(sp_n):
for n in np.arange(sp_n):
subCRmap[i_st+m, j_st + n] = pix_v1
m_size = addPSF.shape[0]
subCRmap_n = np.zeros(np.array(subCRmap.shape) + m_size -1)
for i in np.arange(subCRmap.shape[0]):
for j in np.arange(subCRmap.shape[1]):
if subCRmap[i,j]>0:
convPix = addPSF*subCRmap[i,j]
subCRmap_n[i:i+m_size,j:j+m_size]+=convPix
CRmap_n = np.zeros((np.array(subCRmap_n.shape)/sp_n).astype(np.int32))
sh_n = CRmap_n.shape
for i in np.arange(sh_n[0]):
i_st = sp_n*i
for j in np.arange(sh_n[1]):
p_v = 0
j_st=sp_n*j
for m in np.arange(sp_n):
for n in np.arange(sp_n):
p_v += subCRmap_n[i_st+m, j_st + n]
CRmap_n[i,j] = p_v
return CRmap_n
def produceCR_Map(xLen, yLen, exTime, cr_pixelRatio, gain, attachedSizes, seed=20210317):
# Return: an 2-D numpy array
# attachedSizes = np.loadtxt('./wfc-cr-attachpixel.dat');
np.random.seed(seed)
CR_max_size = 20.0;
cr_size = selectCosmicRayCollection(attachedSizes, xLen, yLen, cr_pixelRatio, CR_max_size);
cr_event_size = cr_size.shape[0];
cr_energys = defineEnergyForCR(cr_event_size);
CRmap = np.zeros([yLen, xLen]);
## produce conv kernel
from astropy.modeling.models import Gaussian2D
o_size = 4
sp_n = 8
m_size = o_size*sp_n+1
m_cen = o_size*sp_n/2
sigma_psf = 0.2*sp_n
addPSF_ = Gaussian2D(1, m_cen, m_cen, sigma_psf, sigma_psf)
yp, xp = np.mgrid[0:m_size, 0:m_size]
addPSF = addPSF_(xp, yp)
convKernel = addPSF/addPSF.sum()
#################################
for i in np.arange(cr_event_size):
xPos = round((xLen - 1)* np.random.random());
yPos = round((yLen - 1)* np.random.random());
cr_lens = int(cr_size[i]);
if cr_lens ==0:
continue;
pix_energy = cr_energys[i]/gain/cr_lens;
pos_angle = 1/2*math.pi*np.random.random();
crMatrix = np.zeros([cr_lens+1, cr_lens + 1])
for j in np.arange(cr_lens):
x_n = int(np.cos(pos_angle)*j - np.sin(pos_angle)*0);
if x_n < 0:
x_n = x_n + cr_lens+1
y_n = int(np.sin(pos_angle)*j + np.cos(pos_angle)*0);
if x_n<0 or x_n >cr_lens or y_n < 0 or y_n > cr_lens:
continue;
crMatrix[y_n,x_n] = pix_energy;
crMatrix_n = convCR(crMatrix, convKernel, sp_n)
# crMatrix_n = crMatrix
xpix = np.arange(crMatrix_n.shape[0]) + int(xPos)
ypix = np.arange(crMatrix_n.shape[1]) + int(yPos)
sh = CRmap.shape
okx = (xpix >= 0) & (xpix < sh[1])
oky = (ypix >= 0) & (ypix < sh[0])
sly = slice(ypix[oky].min(), ypix[oky].max()+1)
slx = slice(xpix[okx].min(), xpix[okx].max()+1)
CRmap[sly, slx] += crMatrix_n[oky,:][:,okx]
return CRmap.astype(np.int32);
def ShutterEffectArr(GSImage, t_shutter=1.3, dist_bearing=735, dt=1E-3):
# Generate Shutter-Effect normalized image
# t_shutter: time of shutter movement
# dist_bearing: distance between two bearings of shutter leaves
# dt: delta_t of sampling
from scipy import interpolate
SampleNumb = int(t_shutter/dt+1)
DistHalf = dist_bearing/2
t = np.arange(SampleNumb)*dt
a_arr = 5.84*np.sin(2*math.pi/t_shutter*t)
v = np.zeros(SampleNumb)
theta = np.zeros(SampleNumb)
x = np.arange(SampleNumb)/(SampleNumb-1)*dist_bearing
s = np.zeros(SampleNumb)
s1 = np.zeros(SampleNumb)
s2 = np.zeros(SampleNumb)
brt = np.zeros(SampleNumb)
idx = np.arange(SampleNumb)
sidx = np.zeros(SampleNumb)
s1idx = np.zeros(SampleNumb)
s2idx = np.zeros(SampleNumb)
v[0] = 0
theta[0] = 0
for i in range(SampleNumb-1):
v[i+1] = v[i]+a_arr[i]*dt
theta[i+1] = theta[i]+v[i]*dt
s1[i] = DistHalf*np.cos(theta[i])
s2[i] = dist_bearing-DistHalf*np.cos(theta[i])
s1idx[i] = int(s1[i]/dist_bearing*(SampleNumb))
s2idx[i] = int(s2[i]/dist_bearing*(SampleNumb))
brt[(idx>s1idx[i]) & (idx<s2idx[i])] += dt
brt = brt*2+(150-t_shutter*2)
x = (x-dist_bearing/2)*100
intp = interpolate.splrep(x, brt, s=0)
xmin = GSImage.bounds.getXMin()
xmax = GSImage.bounds.getXMax()
ymin = GSImage.bounds.getYMin()
ymax = GSImage.bounds.getYMax()
if xmin<np.min(x) or xmax>np.max(x):
raise LookupError("Out of focal-plane bounds in X-direction.")
if ymin<-25331 or ymax>25331:
raise LookupError("Out of focal-plane bounds in Y-direction.")
sizex = xmax-xmin+1
sizey = ymax-ymin+1
xnewgrid = np.mgrid[xmin:(xmin+sizex)]
expeffect = interpolate.splev(xnewgrid, intp, der=0)
expeffect /= np.max(expeffect)
exparrnormal = np.tile(expeffect, (sizey,1))
# GSImage *= exparrnormal
return exparrnormal
\ 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