Commit 4afd1181 authored by Fang Yuedong's avatar Fang Yuedong
Browse files

add renamed files

parent 4c9c940a
import os
import shutil
import yaml
import galsim
import numpy as np
from astropy.time import Time
from observation_sim.config._util import get_obs_id
import observation_sim.instruments._util as _util
class Pointing(object):
def __init__(self, id=0, ra=0., dec=0., img_pa=0., timestamp=1621915200, sat_x=0., sat_y=0., sat_z=0., sun_x=0., sun_y=0., sun_z=0., sat_vx=0., sat_vy=0., sat_vz=0., exp_time=150., pointing_type='SCI', pointing_type_code='101', pointing_id='00000001', obs_config_file=None, t_shutter_open=1.3, t_shutter_close=1.3):
self.id = id
self.ra = ra
self.dec = dec
self.img_pa = img_pa * galsim.degrees
self.timestamp = timestamp
self.sat_x, self.sat_y, self.sat_z = sat_x, sat_y, sat_z
self.sun_x, self.sun_y, self.sun_z = sun_x, sun_y, sun_z
self.sat_vx, self.sat_vy, self.sat_vz = sat_vx, sat_vy, sat_vz
self.exp_time = exp_time
self.pointing_type = pointing_type
self.pointing_type_code = pointing_type_code
self.obs_id = pointing_id
self.survey_field_type = 'WIDE'
self.jdt = 0.
self.obs_config_file = obs_config_file
self.t_shutter_open = t_shutter_open
self.t_shutter_close = t_shutter_close
self.output_dir = "."
if self.obs_config_file is not None:
with open(self.obs_config_file, "r") as stream:
try:
self.obs_param = yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
if self.obs_param["obs_type"]:
self.pointing_type = self.obs_param["obs_type"]
if self.obs_param["obs_type_code"]:
self.pointing_type_code = self.obs_param["obs_type_code"]
if self.obs_param["obs_id"]:
self.obs_id = str(self.obs_param["obs_id"])
def get_full_depth_exptime(self, filter_type):
if self.survey_field_type == 'WIDE':
if filter_type in _util.SPEC_FILTERS:
return 150. * 4
else:
if filter_type.lower() in ['nuv', 'y']:
return 150. * 4
elif filter_type.lower() in ['u', 'g', 'r', 'i', 'z']:
return 150. * 2
else:
return max(150., self.exp_time) # [TODO] for FGS
elif self.survey_field_type == 'DEEP':
if filter_type in _util.SPEC_FILTERS:
return 250. * 4 * 4
else:
if filter_type.lower() in ['nuv', 'y']:
return 250. * 4 * 4
elif filter_type.lower() in ['u', 'g', 'r', 'i', 'z']:
return 250. * 2 * 4
else:
return max(150., self.exp_time) # [TODO] for FGS
def read_pointing_columns(self, columns, id=0, t=1621915200, pointing_type='SCI'):
self.id = id
col_len = len(columns)
self.ra = float(columns[0])
self.dec = float(columns[1])
self.img_pa = float(columns[4]) * galsim.degrees
# self.pointing_type = pointing_type
if col_len > 5:
jdt = np.double(columns[5])
t_temp = Time(jdt, format='jd')
self.jdt = jdt
self.timestamp = t_temp.unix
self.sat_x = float(columns[6])
self.sat_y = float(columns[7])
self.sat_z = float(columns[8])
self.sun_x = float(columns[9])
self.sun_y = float(columns[10])
self.sun_z = float(columns[11])
self.sat_vx = float(columns[15])
self.sat_vy = float(columns[16])
self.sat_vz = float(columns[17])
self.exp_time = float(columns[18])
is_deep = float(columns[19])
# [TODO] Can also define other survey types
if is_deep != -1.0:
self.survey_field_type = "DEEP"
if not self.obs_config_file:
self.obs_config_file = str(columns[20])
with open(self.obs_config_file, "r") as stream:
try:
self.obs_param = yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
self.pointing_type_code = columns[21][0:3]
self.obs_id = columns[21][3:]
self.pointing_type = self.obs_param["obs_type"]
else:
self.timestamp = t
def make_output_pointing_dir(self, overall_config, copy_obs_config=False):
run_dir = os.path.join(
overall_config["work_dir"], overall_config["run_name"])
if not os.path.exists(run_dir):
try:
os.makedirs(run_dir, exist_ok=True)
except OSError:
pass
self.output_prefix = get_obs_id(
img_type=self.pointing_type,
project_cycle=overall_config["project_cycle"],
run_counter=overall_config["run_counter"],
pointing_id=self.obs_id,
pointing_type_code=self.pointing_type_code)
self.output_dir = os.path.join(run_dir, self.output_prefix)
if not os.path.exists(self.output_dir):
try:
os.makedirs(self.output_dir, exist_ok=True)
except OSError:
pass
if copy_obs_config and self.obs_config_file:
obs_config_output_path = os.path.join(
self.output_dir, os.path.basename(self.obs_config_file))
if not os.path.exists(obs_config_output_path):
try:
shutil.copy(self.obs_config_file, self.output_dir)
except OSError:
pass
from .ChipOutput import ChipOutput
from .Pointing import Pointing
\ No newline at end of file
def get_obs_id(img_type='SCI', project_cycle=6, run_counter=0, pointing_id='00000001', pointing_type_code='101'):
# obs_type = {'SCI': '01', 'BIAS': '03', 'DARK': '07', 'FLAT': '11', 'CRS': '98', 'CRD': '99'}
# obs_type = {'SCIE': '01', 'BIAS': '03', 'DARK': '07', 'FLAT': '11', 'CRS': '98', 'CRD': '99', 'CAL': '01'}
obs_id = pointing_type_code + \
str(int(project_cycle)).rjust(2, '0') + \
str(int(run_counter)) + pointing_id
return obs_id
def get_file_type(img_type='SCI'):
file_type = {'SCI': 'SCI', 'BIAS': 'BIAS', 'DARK': 'DARK', 'FLAT': 'FLAT',
'CRS': 'CRS', 'CRD': 'CRD', 'CALS': 'CALS', 'CALF': 'CALF'}
return file_type[img_type]
This diff is collapsed.
from .ImageHeader import generatePrimaryHeader
from .ImageHeader import generateExtensionHeader
\ No newline at end of file
This diff is collapsed.
XTENSION= 'IMAGE ' / extension type BITPIX = 16 / bits per data value NAXIS = 2 / number of data axes NAXIS1 = 9216 / length of first array axis NAXIS2 = 9232 / length of second array axis PCOUNT = 0 GCOUNT = 1 EXTNAME = 'SCI ' EXTVER = 1 BSCALE = 1 BZERO = 32768 BUNIT = 'ADU ' / physical unit of array values COMMENT ========================================================================COMMENT Detector information COMMENT ========================================================================CAMERA = 'MS' / camera of main survey DETSN = '12345678' / detector serial number DETNAME = 'CCD' / detector type DETTEMP1= 173.0 / detector temperature at EXPSTART(in Kelvin) DETTEMP2= 173.0 / detector temperature at EXPEND(in Kelvin) DETTEMP3= 173.0 / detector temperature at READT1(in Kelvin) DETSIZE = '9560x9264' / detector size DATASECT= '9216x9232' / data section PIXSCAL1= 0.074 / pixel scale for axis 1 PIXSCAL2= 0.074 / pixel scale for axis 2 PIXSIZE1= 10 / pixel size for axis 1 (in um) PIXSIZE2= 10 / pixel size for axis 2 (in um) COMMENT ========================================================================COMMENT CCD chip information COMMENT ========================================================================CHIPID = '08' / chip ID CHIPLAB = 'y-1' / chip label FILTER = 'y' / filter name NCHAN = 16 / number of readout channels PSCAN1 = 27 / horizontal prescan width, per readout channel PSCAN2 = 8 / vertical prescan width, per readout channel OSCAN1 = 16 / horizontal overscan width,per readout channel OSCAN2 = 16 / vertical overscan width,per readout channel COMMENT ========================================================================COMMENT WORLD COORDINATE SYSTEM AND RELATED PARAMETERS COMMENT ========================================================================WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = -10017.0 / x-coordinate of reference pixel CRPIX2 = 24876.0 / y-coordinate of reference pixel CRVAL1 = 62.228226 / first axis value at reference pixel CRVAL2 = -42.316932 / second axis value at reference pixel CTYPE1 = 'RA---TAN' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN' / the coordinate type for the second axis CD1_1 = 1.88602083707394E-05 / partial of first axis coordinate w.r.t.x CD1_2 = 8.17455836176000E-06 / partial of first axis coordinate w.r.t.y CD2_1 = -8.1745583617600E-06 / partial of second axis coordinate w.r.t.x CD2_2 = 1.88602083707394E-05 / partial of second axis coordinate w.r.t.y OTHERS = '' / COMMENT ========================================================================COMMENT Readout information COMMENT ========================================================================GAINLVL = '01' / gain level GAIN01 = 1.1 / gain (channel 01) GAIN02 = 1.1 / gain (channel 02) GAIN03 = 1.1 / gain (channel 03) GAIN04 = 1.1 / gain (channel 04) GAIN05 = 1.1 / gain (channel 05) GAIN06 = 1.1 / gain (channel 06) GAIN07 = 1.1 / gain (channel 07) GAIN08 = 1.1 / gain (channel 08) GAIN09 = 1.1 / gain (channel 09) GAIN10 = 1.1 / gain (channel 10) GAIN11 = 1.1 / gain (channel 11) GAIN12 = 1.1 / gain (channel 12) GAIN13 = 1.1 / gain (channel 13) GAIN14 = 1.1 / gain (channel 14) GAIN15 = 1.1 / gain (channel 15) GAIN16 = 1.1 / gain (channel 16) RON01 = 5.0 / read noise (channel 01) RON02 = 5.0 / read noise (channel 02) RON03 = 5.0 / read noise (channel 03) RON04 = 5.0 / read noise (channel 04) RON05 = 5.0 / read noise (channel 05) RON06 = 5.0 / read noise (channel 06) RON07 = 5.0 / read noise (channel 07) RON08 = 5.0 / read noise (channel 08) RON09 = 5.0 / read noise (channel 09) RON10 = 5.0 / read noise (channel 10) RON11 = 5.0 / read noise (channel 11) RON12 = 5.0 / read noise (channel 12) RON13 = 5.0 / read noise (channel 13) RON14 = 5.0 / read noise (channel 14) RON15 = 5.0 / read noise (channel 15) RON16 = 5.0 / read noise (channel 16) READT0 = '2024-00-00T00:00:00'/ readout start time(UTC) READT1 = '2024-00-00T00:00:00'/ readout end time(UTC) ROSPEED = 10.0 / readout speed (in MHz) EXPTIME = 150.0 / exposure duration DARKTIME= 150.0 / dark current time COMMENT ========================================================================COMMENT Shutter information COMMENT ========================================================================SHTSTAT = T / shutter status SHTOPEN0= 0.0 / shutter open time (begin) SHTOPEN1= 0.0 / shutter open time (end) SHTCLOS0= 0.0 / shutter close time (begin) SHTCLOS1= 0.0 / shutter close time (end) COMMENT ========================================================================COMMENT LED information COMMENT ========================================================================LEDFLAG = 0 / main/backup LED LEDSTAT = '00000000000000' / LED status LEDEXPT = 0.0 / LED flash time (s) LEDTEMP = 173.0 / LED temperature (in K) COMMENT ========================================================================COMMENT Other information COMMENT ========================================================================CHECKSUM= '''abcde''' / HDU checksum updated yyyy-mm-ddTHH:MM:SS DATASUM = '''abcde''' / data unit checksum updated yyyy-mm-ddTHH:MM:SS END
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
SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T NEXTEND = 1 / number of array dimensions GROUPS = F / ' ' DATE = '2021-03-04T09:30:00'/ the date on which this file was written FILENAME= 'MSC_MS_210304093000_100000000_06_raw' / file name FILETYPE= 'SCIE ' / observation type TELESCOP= 'CSST ' / telescope used to acquire data INSTRUME= 'MSC ' / instrument used to acquire data RADECSYS= 'ICRS ' / reference coordinates system EQUINOX = 2000.0 / FITSCREA= 'C6' / FITS create software version COMMENT ========================================================================COMMENT Object information COMMENT ========================================================================OBJECT = '00000000' / object name TARGET = '+000000000000' / target name (hhmmss+ddmmss) OBSID = '00000000' / observation ID OBJ_RA = 62.228226 / R.A. of the object (degrees) OBJ_DEC = -42.316932 / declination of the object (degrees) COMMENT ========================================================================COMMENT Telescope information COMMENT ========================================================================REFFRAME= 'CSSTGSC-1.0' / guide star catalog version DATE-OBS= '2021-03-04T09:30:00'/ date of the observation (yyyy-mm-dd hh:mm:ss) SATESWV = '0001' / software version in the satellite EXPSTART= 59130.5 / exposure start time (MJD) CABSTART= 59130.5 / (MJD) SUNANGL0= 50.0 / angle between sun and opt axis at CABSTART MOONANG0= 30.0 / angle between moon and opt axis at CABSTART TEL_ALT0= 20.0 / angle between opt axis and Elimb at CABSTART POS_ANG0= 20.0 / angle between y axis and NP at CABSTART POSI0_X = 0.0 / the orbital position in X at CABSTART POSI0_Y = 0.0 / the orbital position in Y at CABSTART POSI0_Z = 0.0 / the orbital position in Z at CABSTART VELO0_X = 0.0 / the orbital velocity in X at CABSTART VELO0_Y = 0.0 / the orbital velocity in Y at CABSTART VELO0_Z = 0.0 / the orbital velocity in Z at CABSTART EULER0_1= 0.0 / euler angle 1 at CABSTART EULER0_2= 0.0 / euler angle 2 at CABSTART EULER0_3= 0.0 / euler angle 3 at CABSTART RA_PNT0 = 0.0 / RA of the pointing (degrees) at CABSTART DEC_PNT0= 0.0 / DEC of the pointing (degrees) at CABSTART EXPEND = 0.0 / exposure end time (MJD) CABEND = 0.0 / (MJD) SUNANGL1= 50.0 / angle between sun and opt axis at CABEND MOONANG1= 30.0 / angle between moon and opt axis at CABEND TEL_ALT1= 20.0 / angle between opt axis and Elimb at CABEND POS_ANG1= 20.0 / angle between y axis and NP at CABEND POSI1_X = 0.0 / the orbital position in X at CABEND POSI1_Y = 0.0 / the orbital position in Y at CABEND POSI1_Z = 0.0 / the orbital position in Z at CABEND VELO1_X = 0.0 / the orbital velocity in X at CABEND VELO1_Y = 0.0 / the orbital velocity in Y at CABEND VELO1_Z = 0.0 / the orbital velocity in Z at CABEND EULER1_1= 0.0 / euler angle 1 at CABEND EULER1_2= 0.0 / euler angle 2 at CABEND EULER1_3= 0.0 / euler angle 3 at CABEND RA_PNT1 = 0.0 / RA of the pointing (degrees) at CABEND DEC_PNT1= 0.0 / DEC of the pointing (degrees) at CABEND EXPTIME = 150.0 / exposure duration EPOCH = 2000.0 / coordinate epoch COMMENT Other information COMMENT ========================================================================CHECKSUM= 'abcdefg ' / HDU checksum updated yyyy-mm-ddTHH:MM:SS DATASUM = 'abcdefg ' / data unit checksum updated yyyy-mm-ddTHH:MM:SS END
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
import galsim
import pylab as pl
import os
import numpy as np
import gc
import observation_sim.instruments._util as _util
from observation_sim.instruments.FilterParam import FilterParam
from observation_sim.sky_background import Straylight
try:
import importlib.resources as pkg_resources
except ImportError:
# Try backported to PY<37 'importlib_resources'
import importlib_resources as pkg_resources
class Filter(object):
def __init__(self, filter_id, filter_type, filter_param, ccd_bandpass=None):
self.filter_id = filter_id
self.filter_type = filter_type
self.ccd_bandpass = ccd_bandpass
# Load basic filter parameters.
# Assume t_exp = 150s for limiting/saturation magnitudes.
self._getParam(filter_param, filter_type)
self.bandpass_full, self.bandpass_sub_list = self._get_bandpasses()
self.survey_type = self._getSurveyType()
def _getSurveyType(self):
if self.filter_type in _util.SPEC_FILTERS:
return "spectroscopic"
else:
return "photometric"
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_limiting = filter_param.param[filter_type][7]
self.zodical_spec = None
def is_too_bright(self, mag, margin=-2.5):
return mag <= self.mag_saturation + margin
# return mag <= 14.0
def is_too_dim(self, mag, margin=1.0):
return mag >= self.mag_limiting + margin
def _get_bandpasses(self, filter_dir=None, unit='A'):
if self.filter_id < 7: # Photometric
try:
with pkg_resources.files('observation_sim.instruments.data.filters').joinpath(self.filter_type.lower() + '.txt') as filter_file:
self.filter_bandpass = galsim.Bandpass(
str(filter_file), wave_type=unit)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.filters', self.filter_type.lower() + '.txt') as filter_file:
self.filter_bandpass = galsim.Bandpass(
str(filter_file), wave_type=unit)
try:
with pkg_resources.files('observation_sim.instruments.data.throughputs').joinpath(self.filter_type.lower() + '_throughput.txt') as filter_file:
bandpass_full = galsim.Bandpass(
str(filter_file), wave_type=unit)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.throughputs', self.filter_type.lower() + '_throughput.txt') as filter_file:
bandpass_full = galsim.Bandpass(
str(filter_file), wave_type=unit)
# bandpass_full = bandpass_full * self.ccd_bandpass
# Get sub-bandpasses
bandpass_sub_list = []
try:
with pkg_resources.files('observation_sim.instruments.data.filters').joinpath(self.filter_type.lower() + "_sub.list") as wave_bin_file:
wave_points = open(wave_bin_file).read().splitlines()
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.filters', self.filter_type.lower() + "_sub.list") as wave_bin_file:
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_flux, interpolant='nearest')
bandpass_full = galsim.Bandpass(con_spec, wave_type=unit)
bandpass_sub_list = []
try:
with pkg_resources.files('observation_sim.instruments.data.filters').joinpath(self.filter_type.lower() + "_sub.list") as wave_bin_file:
wave_points = open(wave_bin_file).read().splitlines()
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.filters', self.filter_type.lower() + "_sub.list") as wave_bin_file:
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 _util.photonEnergy(self.effective_wavelength)
def getSkyNoise(self, exptime, gain=1.):
return self.sky_background * exptime / gain
def setFilterStrayLightPixel(self, jtime=2460843., sat_pos=np.array([0, 0, 0]), pointing_radec=np.array([0, 0]), sun_pos=np.array([0, 0, 0])):
sl = Straylight(jtime=jtime, sat_pos=sat_pos,
pointing_radec=pointing_radec, sun_pos=sun_pos)
if self.filter_type in _util.SPEC_FILTERS:
s_pix, spec = sl.calculateStrayLightGrating(
grating=self.filter_type.upper())
if s_pix > 0.8:
s_pix = 0.8
self.sky_background = s_pix
self.zodical_spec = spec
elif self.filter_type.lower() in [x.lower for x in _util.PHOT_FILTERS]:
s_pix = sl.calculateStrayLightFilter(
filter=self.filter_type.lower())
if s_pix > 1:
s_pix = 1
self.sky_background = s_pix
self.zodical_spec = None
del sl
gc.collect()
def update_limit_saturation_mags(self, exptime=150., psf_fwhm=0.1969, skyFn='sky_emiss_hubble_50_50_A.dat', chip=None):
if self.filter_type in _util.SPEC_FILTERS:
return
if chip is not None:
pix_scale = chip.pix_scale
read_noise = chip.read_noise
dark_noise = chip.dark_noise
full_well = chip.full_well
else:
pix_scale = 0.074
read_noise = 5.0
dark_noise = 0.02
full_well = 90000
throughput_file = self.filter_type.lower() + '_throughput.txt'
self.mag_limiting, self.mag_saturation = _util.calculateLimitMag(
psf_fwhm=psf_fwhm, pixelSize=pix_scale, throughputFn=throughput_file, readout=5.0, skyFn=skyFn, darknoise=dark_noise, exTime=exptime, fw=full_well)
print("for filter %s: mag_limiting: %.3f, mag_saturation: %.3f" %
(self.filter_type, self.mag_limiting, self.mag_saturation))
import galsim
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],
# [TODO]
"FGS": [5000.0, 8000.0, 3000.0, 11000.0, 0.6500, 0.164, 0., 30.],
"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
import galsim
import numpy as np
class FocalPlane(object):
def __init__(self, chip_list=None, survey_type='Photometric', bad_chips=None):
"""Get the focal plane layout
"""
self.nchips = 42
self.ignore_chips = []
if bad_chips == None:
self.bad_chips = []
else:
self.bad_chips = bad_chips
for chip_id in bad_chips:
self.ignore_chips.append(chip_id)
if chip_list is not None:
for i in range(42):
if not (i+1 in chip_list):
self.ignore_chips.append(i+1)
elif survey_type == 'Photometric':
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)
for i in range(31, 43):
self.ignore_chips.append(i)
elif survey_type == 'Spectroscopic':
for i in range(6, 26):
if i == 10 or i == 21:
continue
else:
self.ignore_chips.append(i)
for i in range(31, 43):
self.ignore_chips.append(i)
elif survey_type == 'FGS':
for i in range(1, 31):
self.ignore_chips.append(i)
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
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
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
try:
import importlib.resources as pkg_resources
except ImportError:
# Try backported to PY<37 'importlib_resources'
import importlib_resources as pkg_resources
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)
else:
try:
with pkg_resources.files('observation_sim.instruments.data').joinpath('mirror_ccdnote.txt') as optEffCurve_path:
self.efficiency = self._get_efficiency(optEffCurve_path)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data', 'mirror_ccdnote.txt') as optEffCurve_path:
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
from .Telescope import Telescope
from .Filter import Filter
from .FilterParam import FilterParam
from .FocalPlane import FocalPlane
from .chip import Chip
import numpy as np
import os
import math
from pylab import *
from scipy import interpolate
try:
import importlib.resources as pkg_resources
except ImportError:
# Try backported to PY<37 'importlib_resources'
import importlib_resources as pkg_resources
VC_A = 2.99792458e+18 # speed of light: A/s
VC_M = 2.99792458e+8 # speed of light: m/s
H_PLANK = 6.626196e-27 # Plank constant: erg s
ALL_FILTERS = ["NUV", "u", "g", "r", "i", "z", "y", "GU", "GV", "GI", "FGS"]
PHOT_FILTERS = ["NUV", "u", "g", 'r', 'i', 'z', 'y', 'FGS']
SPEC_FILTERS = ["GI", "GV", "GU"]
def rotate_conterclockwise(x0, y0, x, y, angle):
"""
Rotate a point counterclockwise by a given angle around a given origin.
The angle should be given in radians.
"""
angle = np.deg2rad(angle)
qx = x0 + np.cos(angle)*(x - x0) - np.sin(angle) * (y - y0)
qy = y0 + np.sin(angle)*(x - x0) + np.cos(angle) * (y - y0)
return qx, qy
def photonEnergy(lambd):
""" The energy of photon at a given wavelength
Parameter:
lambd: the wavelength in unit of Angstrom
Return:
eph: energy of photon in unit of erg
"""
nu = VC_A / lambd
eph = H_PLANK * nu
return eph
def calculateLimitMag(aperture=2.0, psf_fwhm=0.1969, pixelSize=0.074, pmRation=0.8, throughputFn='i_throughput.txt', readout=5.0, skyFn='sky_emiss_hubble_50_50_A.dat', darknoise=0.02, exTime=150, exNum=1, fw=90000):
'''
description:
param {*} aperture: unit m, default 2 m
param {*} psf_fwhm: psf fwhm, default 0.1969"
param {*} pixelSize: pixel size, default 0.074"
param {*} pmRation: the ratio of souce flux in the limit mag calculation
param {*} throughputFn: throuput file name
param {*} readout: unit, e-/pixel
param {*} skyFn: sky sed file name, average of hst, 'sky_emiss_hubble_50_50_A.dat'
param {*} darknoise: unit, e-/pixel/s
param {*} exTime: exposure time one time, default 150s
param {*} exNum: exposure number, defautl 1
param {*} fw, full well value( or saturation value),default 90000e-/pixel
return {*} limit mag and saturation mag
'''
try:
with pkg_resources.files('observation_sim.instruments.data.throughputs').joinpath(throughputFn) as data_file:
throughput_f = np.loadtxt(data_file)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.throughputs', throughputFn) as data_file:
throughput_f = np.loadtxt(data_file)
thr_i = interpolate.interp1d(
throughput_f[:, 0]/10, throughput_f[:, 1]) # wavelength in anstrom
f_s = 200
f_e = 1100
delt_f = 0.5
data_num = int((f_e-f_s)/delt_f+1)
eff = np.zeros([data_num, 2])
eff[:, 0] = np.arange(f_s, f_e+delt_f, delt_f)
eff[:, 1] = thr_i(eff[:, 0])
wave = np.arange(f_s, f_e+delt_f, delt_f)
wavey = np.ones(wave.shape[0])
try:
with pkg_resources.files('observation_sim.instruments.data.throughputs').joinpath(skyFn) as data_file:
skydata = np.loadtxt(data_file)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.throughputs', skyFn) as data_file:
skydata = np.loadtxt(data_file)
skydatai = interpolate.interp1d(skydata[:, 0]/10, skydata[:, 1]*10)
sky_data = np.zeros([data_num, 2])
sky_data[:, 0] = np.arange(f_s, f_e+delt_f, delt_f)
sky_data[:, 1] = skydatai(sky_data[:, 0])
flux_sky = trapz((sky_data[:, 1])*eff[:, 1], sky_data[:, 0])
skyPix = flux_sky*pixelSize*pixelSize*pi*(aperture*aperture/4)
# limit mag
r_pix = psf_fwhm*0.7618080243778568/pixelSize # radius RE80, pixel
cnum = math.pi * r_pix * r_pix
sn = 5
d = skyPix*exTime*exNum*cnum + darknoise * \
exTime*exNum*cnum+readout*readout*cnum*exNum
a = 1
b = -sn*sn
c = -sn*sn*d
flux = (-b+sqrt(b*b-4*a*c))/(2*a)/pmRation
limitMag = -2.5*log10(flux/(54799275581.04437 * trapz(wavey *
eff[:, 1]/wave, wave, 0.1)*exTime*exNum*pi*(aperture/2)*(aperture/2)))
# saturation mag
from astropy.modeling.models import Gaussian2D
m_size = int(20 * psf_fwhm/pixelSize)
if m_size % 2 == 0:
m_size + 1
m_cen = m_size//2
psf_sigma = psf_fwhm/2.355/pixelSize
gaussShape = Gaussian2D(1, m_cen, m_cen, psf_sigma, psf_sigma)
yp, xp = np.mgrid[0:m_size, 0:m_size]
psfMap = gaussShape(xp, yp)
maxRatio = np.amax(psfMap)/np.sum(psfMap)
# print(maxRatio)
flux_sat = fw/maxRatio*exNum
satMag = -2.5*log10(flux_sat/(54799275581.04437 * trapz(wavey *
eff[:, 1]/wave, wave, 0.1)*exTime*exNum*pi*(aperture/2)*(aperture/2)))
return limitMag, satMag
import galsim
import os
import numpy as np
import pickle
import json
from astropy.table import Table
from numpy.random import Generator, PCG64
from astropy.io import fits
from datetime import datetime
import observation_sim.instruments._util as _util
from observation_sim.instruments.chip import effects
from observation_sim.instruments.FocalPlane import FocalPlane
from observation_sim.config.header import generatePrimaryHeader, generateExtensionHeader
from observation_sim.instruments._util import rotate_conterclockwise
from observation_sim.instruments.chip import chip_utils
from observation_sim.instruments.chip.libCTI.CTI_modeling import CTI_sim
try:
import importlib.resources as pkg_resources
except ImportError:
# Try backported to PY<37 'importlib_resources'
import importlib_resources as pkg_resources
class Chip(FocalPlane):
def __init__(self, chipID, ccdEffCurve_dir=None, CRdata_dir=None, sls_dir=None, config=None, treering_func=None, logger=None):
# Get focal plane (instance of paraent class) info
super().__init__()
self.nsecy = 2
self.nsecx = 8
self.gain_channel = np.ones(self.nsecy * self.nsecx)
self._set_attributes_from_config(config)
self.logger = logger
# A chip ID must be assigned
self.chipID = int(chipID)
self.chip_name = str(chipID).rjust(2, '0')
# Get corresponding filter info
self.filter_id, self.filter_type = self.getChipFilter()
self.survey_type = self._getSurveyType()
if self.filter_type != "FGS":
self._getChipRowCol()
# Set the relavent specs for detectors
try:
with pkg_resources.files('observation_sim.instruments.data.ccd').joinpath("chip_definition.json") as chip_definition:
with open(chip_definition, "r") as f:
chip_dict = json.load(f)[str(self.chipID)]
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.ccd', "chip_definition.json") as chip_definition:
with open(chip_definition, "r") as f:
chip_dict = json.load(f)[str(self.chipID)]
for key in chip_dict:
setattr(self, key, chip_dict[key])
self.fdModel = None
if self.filter_type == "FGS":
fgs_name = self.chip_name[0:4]
try:
with pkg_resources.files('observation_sim.instruments.data.field_distortion').joinpath("FieldDistModelGlobal_pr4_%s.pickle" % (fgs_name.lower())) as field_distortion:
with open(field_distortion, "rb") as f:
self.fdModel = pickle.load(f)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.field_distortion', "FieldDistModelGlobal_pr4_%s.pickle" % (fgs_name.lower())) as field_distortion:
with open(field_distortion, "rb") as f:
self.fdModel = pickle.load(f)
else:
# Get the corresponding field distortion model
try:
with pkg_resources.files('observation_sim.instruments.data.field_distortion').joinpath("FieldDistModel_v2.0.pickle") as field_distortion:
with open(field_distortion, "rb") as f:
self.fdModel = pickle.load(f)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.field_distortion', "FieldDistModelGlobal_mainFP_v1.0.pickle") as field_distortion:
with open(field_distortion, "rb") as f:
self.fdModel = pickle.load(f)
# Get boundary (in pix)
self.bound = self.getChipLim()
self.ccdEffCurve_dir = ccdEffCurve_dir
self.CRdata_dir = CRdata_dir
slsconfs = chip_utils.getChipSLSConf(chipID=self.chipID)
if np.size(slsconfs) == 1:
try:
with pkg_resources.files('observation_sim.instruments.data.sls_conf').joinpath(slsconfs) as conf_path:
self.sls_conf = str(conf_path)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.sls_conf', slsconfs) as conf_path:
self.sls_conf = str(conf_path)
else:
# self.sls_conf = [os.path.join(self.sls_dir, slsconfs[0]), os.path.join(self.sls_dir, slsconfs[1])]
self.sls_conf = []
try:
with pkg_resources.files('observation_sim.instruments.data.sls_conf').joinpath(slsconfs[0]) as conf_path:
self.sls_conf.append(str(conf_path))
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.sls_conf', slsconfs[0]) as conf_path:
self.sls_conf.append(str(conf_path))
try:
with pkg_resources.files('observation_sim.instruments.data.sls_conf').joinpath(slsconfs[1]) as conf_path:
self.sls_conf.append(str(conf_path))
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.sls_conf', slsconfs[1]) as conf_path:
self.sls_conf.append(str(conf_path))
self.effCurve = self._getChipEffCurve(self.filter_type)
self._getCRdata()
# # Define the sensor model
self.sensor = galsim.Sensor()
self.flat_cube = None # for spectroscopic flat field cube simulation
def _set_attributes_from_config(self, config):
# Default setting
self.read_noise = 5.0 # e/pix
self.dark_noise = 0.02 # e/pix/s
self.rotate_angle = 0.
self.overscan = 1000
# Override default values
# for key in ["gain", "bias_level, dark_exptime", "flat_exptime", "readout_time", "full_well", "read_noise", "dark_noise", "overscan"]:
# if key in config["ins_effects"]:
# setattr(self, key, config["ins_effects"][key])
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 _util.SPEC_FILTERS:
return "spectroscopic"
elif self.filter_type in _util.PHOT_FILTERS:
return "photometric"
# elif self.filter_type in ["FGS"]:
# return "FGS"
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', 'FGS']:
# TODO, need to switch to the right efficiency curvey for FGS CMOS
filename = 'Astro_MB.txt'
if filter_type in ['i', 'z', 'y', 'GI']:
filename = 'Basic_NIR.txt'
try:
with pkg_resources.files('observation_sim.instruments.data.ccd').joinpath(filename) as ccd_path:
table = Table.read(ccd_path, format='ascii')
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data.ccd', filename) as ccd_path:
table = Table.read(ccd_path, format='ascii')
throughput = galsim.LookupTable(
x=table['col1'], f=table['col2'], interpolant='linear')
bandpass = galsim.Bandpass(throughput, wave_type='nm')
return bandpass
def _getCRdata(self):
try:
with pkg_resources.files('observation_sim.instruments.data').joinpath("wfc-cr-attachpixel.dat") as cr_path:
self.attachedSizes = np.loadtxt(cr_path)
except AttributeError:
with pkg_resources.path('observation_sim.instruments.data', "wfc-cr-attachpixel.dat") as cr_path:
self.attachedSizes = np.loadtxt(cr_path)
# def loadSLSFLATCUBE(self, flat_fn='flat_cube.fits'):
# try:
# with pkg_resources.files('observation_sim.instruments.data').joinpath(flat_fn) as data_path:
# flat_fits = fits.open(data_path, ignore_missing_simple=True)
# except AttributeError:
# with pkg_resources.path('observation_sim.instruments.data', flat_fn) as data_path:
# flat_fits = fits.open(data_path, ignore_missing_simple=True)
# fl = len(flat_fits)
# fl_sh = flat_fits[0].data.shape
# assert fl == 4, 'FLAT Field Cube is Not 4 layess!!!!!!!'
# self.flat_cube = np.zeros([fl, fl_sh[0], fl_sh[1]])
# for i in np.arange(0, fl, 1):
# self.flat_cube[i] = flat_fits[i].data
def getChipFilter(self, chipID=None):
"""Return the filter index and type for a given chip #(chipID)
"""
filter_type_list = _util.ALL_FILTERS
if chipID == None:
chipID = self.chipID
# updated configurations
if chipID > 42 or chipID < 1:
raise ValueError("!!! Chip ID: [1,42]")
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"
if chipID in range(31, 43):
filter_type = 'FGS'
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 / spectroscopic observation.
Parameters:
chipID: int
the index of the chip
Returns:
A galsim BoundsD object
"""
xmin, xmax, ymin, ymax = 1e10, -1e10, 1e10, -1e10
xcen = self.x_cen / self.pix_size
ycen = self.y_cen / self.pix_size
sign_x = [-1., 1., -1., 1.]
sign_y = [-1., -1., 1., 1.]
for i in range(4):
x = xcen + sign_x[i] * self.npix_x / 2.
y = ycen + sign_y[i] * self.npix_y / 2.
x, y = _util.rotate_conterclockwise(
x0=xcen, y0=ycen, x=x, y=y, angle=self.rotate_angle)
xmin, xmax = min(xmin, x), max(xmax, x)
ymin, ymax = min(ymin, y), max(ymax, y)
return galsim.BoundsD(xmin, xmax, ymin, ymax)
def getSkyCoverage(self, wcs):
# print("In getSkyCoverage: xmin = %.3f, xmax = %.3f, ymin = %.3f, ymax = %.3f"%(self.bound.xmin, self.bound.xmax, self.bound.ymin, self.bound.ymax))
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=None, dec_obj=None, x_image=None, y_image=None, wcs=None, margin=1):
# magin in number of pix
if (ra_obj is not None) and (dec_obj is not None):
if wcs is None:
wcs = self.img.wcs
pos_obj = wcs.toImage(galsim.CelestialCoord(
ra=ra_obj*galsim.degrees, dec=dec_obj*galsim.degrees))
x_image, y_image = pos_obj.x, pos_obj.y
elif (x_image is None) or (y_image is None):
raise ValueError(
"Either (ra_obj, dec_obj) or (x_image, y_image) should be given")
xmin, xmax = self.bound.xmin - margin, self.bound.xmax + margin
ymin, ymax = self.bound.ymin - margin, self.bound.ymax + margin
if (x_image - xmin) * (x_image - xmax) > 0.0 or (y_image - ymin) * (y_image - ymax) > 0.0:
return False
return True
def getChipNoise(self, exptime=150.0):
noise = self.dark_noise * exptime + self.read_noise**2
return noise
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