From 633fbbabf587c15a6ef61efdfca9fdf6b6cedb46 Mon Sep 17 00:00:00 2001 From: fangyuedong Date: Thu, 18 Jan 2024 02:40:29 +0800 Subject: [PATCH] first test --- ObservationSim/Config/Header/ImageHeader.py | 7 +- ObservationSim/Config/Pointing.py | 14 +- ObservationSim/Config/_util.py | 5 +- ObservationSim/Instrument/Chip/ChipUtils.py | 2 +- ObservationSim/Instrument/FocalPlane.py | 13 +- ObservationSim/ObservationSim.py | 782 ++++++++++---------- ObservationSim/SimSteps.py | 456 ++++++++++++ ObservationSim/_util.py | 38 +- config/config_overall.yaml | 3 - config/obs_config_SCI_WIDE.yaml | 25 - config/obs_config_SCI_WIDE_phot.yaml | 61 ++ run_sim.py | 8 +- 12 files changed, 979 insertions(+), 435 deletions(-) create mode 100644 ObservationSim/SimSteps.py delete mode 100644 config/obs_config_SCI_WIDE.yaml create mode 100644 config/obs_config_SCI_WIDE_phot.yaml diff --git a/ObservationSim/Config/Header/ImageHeader.py b/ObservationSim/Config/Header/ImageHeader.py index 171e7ca..764f241 100644 --- a/ObservationSim/Config/Header/ImageHeader.py +++ b/ObservationSim/Config/Header/ImageHeader.py @@ -342,7 +342,7 @@ def WCS_def(xlen = 9216, ylen = 9232, gapy = 898.0, gapx1 = 534, gapx2 = 1309, r #TODO project_cycle is temporary, is not in header defined, delete in future -def generatePrimaryHeader(xlen = 9216, ylen = 9232, pointNum = '1', ra = 60, dec = -40, pixel_scale = 0.074, date='200930', time_obs='120000', im_type = 'MS', exptime=150., sat_pos = [0.,0.,0.], sat_vel = [0., 0., 0.], project_cycle=6, run_counter=0, chip_name="01"): +def generatePrimaryHeader(xlen = 9216, ylen = 9232, pointNum = '1', ra = 60, dec = -40, pixel_scale = 0.074, date='200930', time_obs='120000', im_type = 'SCIE', exptime=150., sat_pos = [0.,0.,0.], sat_vel = [0., 0., 0.], project_cycle=6, run_counter=0, chip_name="01"): # array_size1, array_size2, flux, sigma = int(argv[1]), int(argv[2]), 1000.0, 5.0 @@ -391,7 +391,8 @@ def generatePrimaryHeader(xlen = 9216, ylen = 9232, pointNum = '1', ra = 60, dec # # Define file types # file_type = {'SCI':'SCIE', 'BIAS':'BIAS', 'DARK':'DARK', 'FLAT':'FLAT', 'CRS':'CRS', 'CRD':'CRD','CALS':'CALS','CALF':'CALF'} # h_prim['FILETYPE'] = file_type[im_type] - h_prim['FILETYPE'] = get_file_type(img_type=im_type) + # h_prim['FILETYPE'] = get_file_type(img_type=im_type) + h_prim['FILETYPE'] = im_type co = coord.SkyCoord(ra, dec, unit='deg') @@ -450,7 +451,7 @@ def generatePrimaryHeader(xlen = 9216, ylen = 9232, pointNum = '1', ra = 60, dec return h_prim def generateExtensionHeader(chip, xlen = 9216, ylen = 9232,ra = 60, dec = -40, pa = -23.433, gain = 1.0, readout = 5.0, dark = 0.02, saturation=90000, pixel_scale = 0.074, pixel_size=1e-2, - extName='SCI', row_num = None, col_num = None, xcen=None, ycen=None, timestamp = 1621915200,exptime = 150., readoutTime = 40.): + extName='SCIE', row_num = None, col_num = None, xcen=None, ycen=None, timestamp = 1621915200,exptime = 150., readoutTime = 40.): e_header_fn = os.path.split(os.path.realpath(__file__))[0] + '/extension_header.header' f = open(os.path.split(os.path.realpath(__file__))[0] + '/filter.lst') diff --git a/ObservationSim/Config/Pointing.py b/ObservationSim/Config/Pointing.py index dccf918..3db8a98 100644 --- a/ObservationSim/Config/Pointing.py +++ b/ObservationSim/Config/Pointing.py @@ -1,5 +1,6 @@ import numpy as np import galsim +import yaml from astropy.time import Time import ObservationSim.Instrument._util as _util @@ -42,13 +43,13 @@ class Pointing(object): return max(150., self.exp_time) # [TODO] for FGS - def read_pointing_columns(self, columns, id=0, t=1621915200, pointing_type='SCI'): + def read_pointing_columns(self, columns, id=0, t=1621915200, pointing_type='SCIE'): 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 + # self.pointing_type = pointing_type if col_len > 5: jdt = np.double(columns[5]) t_temp = Time(jdt, format='jd') @@ -68,5 +69,14 @@ class Pointing(object): # [TODO] Can also define other survey types if is_deep != -1.0: self.survey_field_type = "DEEP" + + # Load the configuration file for this particular pointing + self.obs_config_file = "/share/home/fangyuedong/20231211/csst-simulation/config/obs_config_SCI_WIDE_phot.yaml" + 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 = self.obs_config_file["obs_type"] else: self.timestamp = t diff --git a/ObservationSim/Config/_util.py b/ObservationSim/Config/_util.py index 964d5a2..b7beed9 100644 --- a/ObservationSim/Config/_util.py +++ b/ObservationSim/Config/_util.py @@ -1,8 +1,7 @@ -def get_obs_id(img_type='SCI', project_cycle=6, run_counter=0, pointing_num=0): +def get_obs_id(img_type='SCIE', project_cycle=6, run_counter=0, pointing_num=0): # obs_type = {'SCI': '01', 'BIAS': '03', 'DARK': '07', 'FLAT': '11', 'CRS': '98', 'CRD': '99'} - obs_type = {'SCI': '01', 'BIAS': '03', 'DARK': '07', 'FLAT': '11', 'CRS': '98', 'CRD': '99', 'CAL': '01'} - # obs_id = '1'+ obs_type[img_type] + str(int(project_cycle)) + str(int(run_counter)).rjust(2, '0') + str(pointing_num).rjust(5,'0') + obs_type = {'SCIE': '01', 'BIAS': '03', 'DARK': '07', 'FLAT': '11', 'CRS': '98', 'CRD': '99', 'CAL': '01'} obs_id = '1'+ obs_type[img_type] + str(int(project_cycle)).rjust(2, '0') + str(int(run_counter)) + str(pointing_num).rjust(8,'0') return obs_id diff --git a/ObservationSim/Instrument/Chip/ChipUtils.py b/ObservationSim/Instrument/Chip/ChipUtils.py index 8eb95f3..4b7aa65 100644 --- a/ObservationSim/Instrument/Chip/ChipUtils.py +++ b/ObservationSim/Instrument/Chip/ChipUtils.py @@ -87,7 +87,7 @@ def generateHeader(chip, ra_cen, dec_cen, img_rot, im_type, pointing_ID, exptime pixel_size=chip.pix_size, xcen=chip.x_cen, ycen=chip.y_cen, - extName='SCI', + extName='SCIE', timestamp = timestamp, exptime = exptime, readoutTime = chip.readout_time) diff --git a/ObservationSim/Instrument/FocalPlane.py b/ObservationSim/Instrument/FocalPlane.py index ddca78f..70ccbef 100755 --- a/ObservationSim/Instrument/FocalPlane.py +++ b/ObservationSim/Instrument/FocalPlane.py @@ -2,17 +2,24 @@ import galsim import numpy as np class FocalPlane(object): - def __init__(self, config=None, survey_type='Photometric', bad_chips=None): + def __init__(self, config=None, 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) - self.ignore_chips = [] - if survey_type == 'Photometric': + 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) diff --git a/ObservationSim/ObservationSim.py b/ObservationSim/ObservationSim.py index 4ca354d..9151075 100755 --- a/ObservationSim/ObservationSim.py +++ b/ObservationSim/ObservationSim.py @@ -2,7 +2,6 @@ import os import numpy as np import mpi4py.MPI as MPI import galsim -import logging import psutil import gc from astropy.io import fits @@ -14,40 +13,44 @@ from ObservationSim.Config import config_dir, ChipOutput from ObservationSim.Config.Header import generatePrimaryHeader, generateExtensionHeader from ObservationSim.Instrument import Telescope, Filter, FilterParam, FocalPlane, Chip from ObservationSim.Instrument.Chip import Effects +from ObservationSim.Instrument.Chip import ChipUtils as chip_utils from ObservationSim.Straylight import calculateSkyMap_split_g from ObservationSim.PSF import PSFGauss, FieldDistortion, PSFInterp, PSFInterpSLS from ObservationSim._util import get_shear_field, makeSubDir_PointingList from ObservationSim.Astrometry.Astrometry_util import on_orbit_obs_position from ObservationSim.MockObject import FlatLED +from ObservationSim.SimSteps import SimSteps, SIM_STEP_TYPES + class Observation(object): def __init__(self, config, Catalog, work_dir=None, data_dir=None): self.path_dict = config_dir(config=config, work_dir=work_dir, data_dir=data_dir) self.config = config self.tel = Telescope() - self.focal_plane = FocalPlane(survey_type=self.config["obs_setting"]["survey_type"]) self.filter_param = FilterParam() - self.chip_list = [] - self.filter_list = [] - self.all_filter = [] self.Catalog = Catalog - # Construct chips & filters: - for i in range(self.focal_plane.nchips): - chipID = i + 1 + def prepare_chip_for_exposure(self, chip, ra_cen, dec_cen, pointing): + # Get WCS for the focal plane + if wcs_fp == None: + wcs_fp = self.focal_plane.getTanWCS(ra_cen, dec_cen, pointing.img_pa, chip.pix_scale) - # Make Chip & Filter lists - chip = Chip( - chipID=chipID, - config=self.config) - filter_id, filter_type = chip.getChipFilter() - filt = Filter(filter_id=filter_id, - filter_type=filter_type, - filter_param=self.filter_param) - if not self.focal_plane.isIgnored(chipID=chipID): - self.chip_list.append(chip) - self.filter_list.append(filt) - self.all_filter.append(filt) + # Create chip Image + chip.img = galsim.ImageF(chip.npix_x, chip.npix_y) + chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin) + chip.img.wcs = wcs_fp + + # Get random generators for this chip + chip.rng_poisson, chip.poisson_noise = chip_utils.get_poisson( + seed=int(self.config["random_seeds"]["seed_poisson"]) + pointing.id*30 + chip.chipID, sky_level=0.) + + # Get flat, shutter, and PRNU images + _, chip.flat_normal = chip_utils.get_flat(img=chip.img, seed=int(self.config["random_seeds"]["seed_flat"])) + chip.shuttimg = Effects.ShutterEffectArr(chip.img, t_shutter=1.3, dist_bearing=735, dt=1E-3) + chip.prnu_img = Effects.PRNU_Img(xsize=chip.npix_x, ysize=chip.npix_y, sigma=0.01, + seed=int(self.config["random_seeds"]["seed_prnu"]+chip.chipID)) + + return chip def run_one_chip(self, chip, filt, pointing, chip_output, wcs_fp=None, psf_model=None, cat_dir=None, sed_dir=None): @@ -61,19 +64,6 @@ class Observation(object): chip_output.Log_info('Chip : %d' % chip.chipID) chip_output.Log_info(':::::::::::::::::::::::::::END:::::::::::::::::::::::::::::::::::') - if self.config["psf_setting"]["psf_model"] == "Gauss": - psf_model = PSFGauss(chip=chip, psfRa=self.config["psf_setting"]["psf_rcont"]) - elif self.config["psf_setting"]["psf_model"] == "Interp": - if chip.survey_type == "spectroscopic": - psf_model = PSFInterpSLS(chip, filt,PSF_data_prefix=self.path_dict["psf_sls_dir"]) - else: - psf_model = PSFInterp(chip=chip, npsf=chip.n_psf_samples, PSF_data_file=self.path_dict["psf_dir"]) - else: - chip_output.Log_error("unrecognized PSF model type!!", flush=True) - - # Figure out shear fields - self.g1_field, self.g2_field, self.nshear = get_shear_field(config=self.config) - # Apply astrometric simulation for pointing if self.config["obs_setting"]["enable_astrometric_model"]: dt = datetime.utcfromtimestamp(pointing.timestamp) @@ -102,249 +92,272 @@ class Observation(object): ra_cen = pointing.ra dec_cen = pointing.dec - # Get WCS for the focal plane - if wcs_fp == None: - wcs_fp = self.focal_plane.getTanWCS(ra_cen, dec_cen, pointing.img_pa, chip.pix_scale) + # # Get WCS for the focal plane + # if wcs_fp == None: + # wcs_fp = self.focal_plane.getTanWCS(ra_cen, dec_cen, pointing.img_pa, chip.pix_scale) - # Create chip Image - chip.img = galsim.ImageF(chip.npix_x, chip.npix_y) - chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin) - chip.img.wcs = wcs_fp + # # Create chip Image + # chip.img = galsim.ImageF(chip.npix_x, chip.npix_y) + # chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin) + # chip.img.wcs = wcs_fp + chip = self.prepare_chip_for_exposure(chip, ra_cen, dec_cen, pointing) - if self.config["obs_setting"]["enable_straylight_model"]: - filt.setFilterStrayLightPixel(jtime = pointing.jdt, sat_pos = np.array([pointing.sat_x, pointing.sat_y, pointing.sat_z]), pointing_radec = np.array([pointing.ra,pointing.dec]), sun_pos = np.array([pointing.sun_x,pointing.sun_y,pointing.sun_z])) + # Load catalogues + self.cat = self.Catalog(config=self.config, chip=chip, pointing=pointing, cat_dir=cat_dir, sed_dir=sed_dir, chip_output=chip_output, filt=filt) - chip_output.Log_info("========================sky pix========================") - chip_output.Log_info(filt.sky_background) + # Initialize SimSteps + sim_steps = SimSteps(overall_config=self.config, chip_output=chip_output, all_filters=self.all_filters) - if chip.survey_type == "photometric": - sky_map = None - elif chip.survey_type == "spectroscopic": - # chip.loadSLSFLATCUBE(flat_fn='flat_cube.fits') - flat_normal = np.ones_like(chip.img.array) - if self.config["ins_effects"]["flat_fielding"] == True: - chip_output.Log_info("SLS flat preprocess,CHIP %d : Creating and applying Flat-Fielding"%chip.chipID) - msg = str(chip.img.bounds) - chip_output.Log_info(msg) - flat_img = Effects.MakeFlatSmooth( - chip.img.bounds, - int(self.config["random_seeds"]["seed_flat"])) - flat_normal = flat_normal * flat_img.array / np.mean(flat_img.array) - if self.config["ins_effects"]["shutter_effect"] == True: - chip_output.Log_info("SLS flat preprocess,CHIP %d : Apply shutter effect"%chip.chipID) - shuttimg = Effects.ShutterEffectArr(chip.img, t_shutter=1.3, dist_bearing=735, - dt=1E-3) # shutter effect normalized image for this chip - flat_normal = flat_normal*shuttimg - flat_normal = np.array(flat_normal,dtype='float32') - sky_map = calculateSkyMap_split_g( - skyMap=flat_normal, - blueLimit=filt.blue_limit, - redLimit=filt.red_limit, - conf=chip.sls_conf, - pixelSize=chip.pix_scale, - isAlongY=0, - flat_cube=chip.flat_cube, zoldial_spec = filt.zodical_spec) - sky_map = sky_map+filt.sky_background - del flat_normal - - if pointing.pointing_type == 'SCI': - # Load catalogues and templates - self.cat = self.Catalog(config=self.config, chip=chip, pointing=pointing, cat_dir=cat_dir, sed_dir=sed_dir, chip_output=chip_output, filt=filt) - chip_output.create_output_file() - self.nobj = len(self.cat.objs) - - for ifilt in range(len(self.all_filter)): - temp_filter = self.all_filter[ifilt] - # Update the limiting magnitude using exposure time in pointing - temp_filter.update_limit_saturation_mags(exptime=pointing.get_full_depth_exptime(temp_filter.filter_type), chip=chip) - - # Select cutting band filter for saturation/limiting magnitude - if temp_filter.filter_type.lower() == self.config["obs_setting"]["cut_in_band"].lower(): - cut_filter = temp_filter - - if self.config["ins_effects"]["field_dist"] == True: - self.fd_model = FieldDistortion(chip=chip, img_rot=pointing.img_pa.deg) - else: - self.fd_model = None - - # Loop over objects - missed_obj = 0 - bright_obj = 0 - dim_obj = 0 + for step in pointing.obs_param["call_sequence"]: + obs_param = pointing.obs_param["call_sequence"][step] + step_name = SIM_STEP_TYPES[step] + try: + step_func = getattr(sim_steps, step_name) + chip, filt, tel, pointing = step_func( + chip=chip, + filt=filt, + tel=tel, + pointing=pointing, + catalog=self.cat, + obs_param=obs_param) + except Exception as e: + traceback.print_exc() + chip_output.Log_error(e) + continue + + # if self.config["obs_setting"]["enable_straylight_model"]: + # filt.setFilterStrayLightPixel(jtime = pointing.jdt, sat_pos = np.array([pointing.sat_x, pointing.sat_y, pointing.sat_z]), pointing_radec = np.array([pointing.ra,pointing.dec]), sun_pos = np.array([pointing.sun_x,pointing.sun_y,pointing.sun_z])) + # chip_output.Log_info("========================sky pix========================") + # chip_output.Log_info(filt.sky_background) + + # if chip.survey_type == "photometric": + # sky_map = None + # elif chip.survey_type == "spectroscopic": + # # chip.loadSLSFLATCUBE(flat_fn='flat_cube.fits') + # flat_normal = np.ones_like(chip.img.array) + # if self.config["ins_effects"]["flat_fielding"] == True: + # chip_output.Log_info("SLS flat preprocess,CHIP %d : Creating and applying Flat-Fielding"%chip.chipID) + # msg = str(chip.img.bounds) + # chip_output.Log_info(msg) + # flat_img = Effects.MakeFlatSmooth( + # chip.img.bounds, + # int(self.config["random_seeds"]["seed_flat"])) + # flat_normal = flat_normal * flat_img.array / np.mean(flat_img.array) + # if self.config["ins_effects"]["shutter_effect"] == True: + # chip_output.Log_info("SLS flat preprocess,CHIP %d : Apply shutter effect"%chip.chipID) + # shuttimg = Effects.ShutterEffectArr(chip.img, t_shutter=1.3, dist_bearing=735, + # dt=1E-3) # shutter effect normalized image for this chip + # flat_normal = flat_normal*shuttimg + # flat_normal = np.array(flat_normal,dtype='float32') + # sky_map = calculateSkyMap_split_g( + # skyMap=flat_normal, + # blueLimit=filt.blue_limit, + # redLimit=filt.red_limit, + # conf=chip.sls_conf, + # pixelSize=chip.pix_scale, + # isAlongY=0, + # flat_cube=chip.flat_cube, zoldial_spec = filt.zodical_spec) + # sky_map = sky_map+filt.sky_background + # del flat_normal + + # if pointing.pointing_type == 'SCI': + # # Load catalogues and templates + # self.cat = self.Catalog(config=self.config, chip=chip, pointing=pointing, cat_dir=cat_dir, sed_dir=sed_dir, chip_output=chip_output, filt=filt) + # chip_output.create_output_file() + # self.nobj = len(self.cat.objs) + + # for ifilt in range(len(self.all_filter)): + # temp_filter = self.all_filter[ifilt] + # # Update the limiting magnitude using exposure time in pointing + # temp_filter.update_limit_saturation_mags(exptime=pointing.get_full_depth_exptime(temp_filter.filter_type), chip=chip) + + # # Select cutting band filter for saturation/limiting magnitude + # if temp_filter.filter_type.lower() == self.config["obs_setting"]["cut_in_band"].lower(): + # cut_filter = temp_filter + + # if self.config["ins_effects"]["field_dist"] == True: + # self.fd_model = FieldDistortion(chip=chip, img_rot=pointing.img_pa.deg) + # else: + # self.fd_model = None + + # # Loop over objects + # missed_obj = 0 + # bright_obj = 0 + # dim_obj = 0 - h_ext = generateExtensionHeader( - chip=chip, - xlen=chip.npix_x, - ylen=chip.npix_y, - ra=pointing.ra, - dec=pointing.dec, - pa=pointing.img_pa.deg, - gain=chip.gain, - readout=chip.read_noise, - dark=chip.dark_noise, - saturation=90000, - pixel_scale=chip.pix_scale, - pixel_size=chip.pix_size, - xcen=chip.x_cen, - ycen=chip.y_cen, - extName='SCI', - timestamp = pointing.timestamp, - exptime = pointing.exp_time, - readoutTime = chip.readout_time) + # h_ext = generateExtensionHeader( + # chip=chip, + # xlen=chip.npix_x, + # ylen=chip.npix_y, + # ra=pointing.ra, + # dec=pointing.dec, + # pa=pointing.img_pa.deg, + # gain=chip.gain, + # readout=chip.read_noise, + # dark=chip.dark_noise, + # saturation=90000, + # pixel_scale=chip.pix_scale, + # pixel_size=chip.pix_size, + # xcen=chip.x_cen, + # ycen=chip.y_cen, + # extName='SCI', + # timestamp = pointing.timestamp, + # exptime = pointing.exp_time, + # readoutTime = chip.readout_time) - chip_wcs = galsim.FitsWCS(header=h_ext) + # chip_wcs = galsim.FitsWCS(header=h_ext) - for j in range(self.nobj): + # for j in range(self.nobj): - # # (DEBUG) - # if j >= 10: - # break - - obj = self.cat.objs[j] - - # (DEBUG) - # if obj.getMagFilter(filt)>20: - # continue - - # load and convert SED; also caculate object's magnitude in all CSST bands - try: - sed_data = self.cat.load_sed(obj) - norm_filt = self.cat.load_norm_filt(obj) - obj.sed, obj.param["mag_%s"%filt.filter_type.lower()], obj.param["flux_%s"%filt.filter_type.lower()] = self.cat.convert_sed( - mag=obj.param["mag_use_normal"], - sed=sed_data, - target_filt=filt, - norm_filt=norm_filt, - ) - _, obj.param["mag_%s"%cut_filter.filter_type.lower()], obj.param["flux_%s"%cut_filter.filter_type.lower()] = self.cat.convert_sed( - mag=obj.param["mag_use_normal"], - sed=sed_data, - target_filt=cut_filter, - norm_filt=norm_filt, - ) - except Exception as e: - traceback.print_exc() - chip_output.Log_error(e) - continue + # # # (DEBUG) + # # if j >= 10: + # # break + + # obj = self.cat.objs[j] + + # # (DEBUG) + # # if obj.getMagFilter(filt)>20: + # # continue + + # # load and convert SED; also caculate object's magnitude in all CSST bands + # try: + # sed_data = self.cat.load_sed(obj) + # norm_filt = self.cat.load_norm_filt(obj) + # obj.sed, obj.param["mag_%s"%filt.filter_type.lower()], obj.param["flux_%s"%filt.filter_type.lower()] = self.cat.convert_sed( + # mag=obj.param["mag_use_normal"], + # sed=sed_data, + # target_filt=filt, + # norm_filt=norm_filt, + # ) + # _, obj.param["mag_%s"%cut_filter.filter_type.lower()], obj.param["flux_%s"%cut_filter.filter_type.lower()] = self.cat.convert_sed( + # mag=obj.param["mag_use_normal"], + # sed=sed_data, + # target_filt=cut_filter, + # norm_filt=norm_filt, + # ) + # except Exception as e: + # traceback.print_exc() + # chip_output.Log_error(e) + # continue - # [TODO] Testing - # chip_output.Log_info("mag_%s = %.3f"%(filt.filter_type.lower(), obj.param["mag_%s"%filt.filter_type.lower()])) - - # Exclude very bright/dim objects (for now) - if cut_filter.is_too_bright( - mag=obj.param["mag_%s"%self.config["obs_setting"]["cut_in_band"].lower()], - margin=self.config["obs_setting"]["mag_sat_margin"]): - chip_output.Log_info("obj %s too birght!! mag_%s = %.3f"%(obj.id, cut_filter.filter_type, obj.param["mag_%s"%self.config["obs_setting"]["cut_in_band"].lower()])) - bright_obj += 1 - obj.unload_SED() - continue - if filt.is_too_dim( - mag=obj.getMagFilter(filt), - margin=self.config["obs_setting"]["mag_lim_margin"]): - chip_output.Log_info("obj %s too dim!! mag_%s = %.3f"%(obj.id, filt.filter_type, obj.getMagFilter(filt))) - dim_obj += 1 - obj.unload_SED() - continue - - # Get corresponding shear values - if self.config["shear_setting"]["shear_type"] == "constant": - if obj.type == 'star': - obj.g1, obj.g2 = 0., 0. - else: - obj.g1, obj.g2 = self.g1_field, self.g2_field - elif self.config["shear_setting"]["shear_type"] == "catalog": - pass - else: - chip_output.Log_error("Unknown shear input") - raise ValueError("Unknown shear input") - - # Get position of object on the focal plane - pos_img, offset, local_wcs, real_wcs, fd_shear = obj.getPosImg_Offset_WCS(img=chip.img, fdmodel=self.fd_model, chip=chip, verbose=False, chip_wcs=chip_wcs, img_header=h_ext) - - # [TODO] For now, only consider objects which their centers (after field distortion) are projected within the focal plane - # Otherwise they will be considered missed objects - # if pos_img.x == -1 or pos_img.y == -1 or (not chip.isContainObj(x_image=pos_img.x, y_image=pos_img.y, margin=0.)): - if pos_img.x == -1 or pos_img.y == -1: - chip_output.Log_info('obj_ra = %.6f, obj_dec = %.6f, obj_ra_orig = %.6f, obj_dec_orig = %.6f'%(obj.ra, obj.dec, obj.ra_orig, obj.dec_orig)) - chip_output.Log_error("Objected missed: %s"%(obj.id)) - missed_obj += 1 - obj.unload_SED() - continue - - # Draw object & update output catalog - try: - if self.config["run_option"]["out_cat_only"]: - isUpdated = True - obj.real_pos = obj.getRealPos(chip.img, global_x=obj.posImg.x, global_y=obj.posImg.y, img_real_wcs=obj.chip_wcs) - pos_shear = 0. - elif chip.survey_type == "photometric" and not self.config["run_option"]["out_cat_only"]: - isUpdated, pos_shear = obj.drawObj_multiband( - tel=self.tel, - pos_img=pos_img, - psf_model=psf_model, - bandpass_list=filt.bandpass_sub_list, - filt=filt, - chip=chip, - g1=obj.g1, - g2=obj.g2, - exptime=pointing.exp_time, - fd_shear=fd_shear) - - elif chip.survey_type == "spectroscopic" and not self.config["run_option"]["out_cat_only"]: - isUpdated, pos_shear = obj.drawObj_slitless( - tel=self.tel, - pos_img=pos_img, - psf_model=psf_model, - bandpass_list=filt.bandpass_sub_list, - filt=filt, - chip=chip, - g1=obj.g1, - g2=obj.g2, - exptime=pointing.exp_time, - normFilter=norm_filt, - fd_shear=fd_shear) - - if isUpdated == 1 and self.config["run_option"]["out_psf"]: - obj.drawObj_PSF( - tel=self.tel, - pos_img=pos_img, - psf_model=psf_model, - bandpass_list=filt.bandpass_sub_list, - filt=filt, - chip=chip, - g1=obj.g1, - g2=obj.g2, - exptime=pointing.exp_time, - fd_shear=fd_shear, - chip_output=chip_output) - - if isUpdated == 1: - # TODO: add up stats - chip_output.cat_add_obj(obj, pos_img, pos_shear) - pass - elif isUpdated == 0: - missed_obj += 1 - chip_output.Log_error("Objected missed: %s"%(obj.id)) - else: - chip_output.Log_error("Draw error, object omitted: %s"%(obj.id)) - continue - except Exception as e: - traceback.print_exc() - chip_output.Log_error(e) + # # [TODO] Testing + # # chip_output.Log_info("mag_%s = %.3f"%(filt.filter_type.lower(), obj.param["mag_%s"%filt.filter_type.lower()])) + + # # Exclude very bright/dim objects (for now) + # if cut_filter.is_too_bright( + # mag=obj.param["mag_%s"%self.config["obs_setting"]["cut_in_band"].lower()], + # margin=self.config["obs_setting"]["mag_sat_margin"]): + # chip_output.Log_info("obj %s too birght!! mag_%s = %.3f"%(obj.id, cut_filter.filter_type, obj.param["mag_%s"%self.config["obs_setting"]["cut_in_band"].lower()])) + # bright_obj += 1 + # obj.unload_SED() + # continue + # if filt.is_too_dim( + # mag=obj.getMagFilter(filt), + # margin=self.config["obs_setting"]["mag_lim_margin"]): + # chip_output.Log_info("obj %s too dim!! mag_%s = %.3f"%(obj.id, filt.filter_type, obj.getMagFilter(filt))) + # dim_obj += 1 + # obj.unload_SED() + # continue + + # # Get corresponding shear values + # if self.config["shear_setting"]["shear_type"] == "constant": + # if obj.type == 'star': + # obj.g1, obj.g2 = 0., 0. + # else: + # obj.g1, obj.g2 = self.g1_field, self.g2_field + # elif self.config["shear_setting"]["shear_type"] == "catalog": + # pass + # else: + # chip_output.Log_error("Unknown shear input") + # raise ValueError("Unknown shear input") + + # # Get position of object on the focal plane + # pos_img, offset, local_wcs, real_wcs, fd_shear = obj.getPosImg_Offset_WCS(img=chip.img, fdmodel=self.fd_model, chip=chip, verbose=False, chip_wcs=chip_wcs, img_header=h_ext) + + # # [TODO] For now, only consider objects which their centers (after field distortion) are projected within the focal plane + # # Otherwise they will be considered missed objects + # # if pos_img.x == -1 or pos_img.y == -1 or (not chip.isContainObj(x_image=pos_img.x, y_image=pos_img.y, margin=0.)): + # if pos_img.x == -1 or pos_img.y == -1: + # chip_output.Log_info('obj_ra = %.6f, obj_dec = %.6f, obj_ra_orig = %.6f, obj_dec_orig = %.6f'%(obj.ra, obj.dec, obj.ra_orig, obj.dec_orig)) + # chip_output.Log_error("Objected missed: %s"%(obj.id)) + # missed_obj += 1 + # obj.unload_SED() + # continue + + # # Draw object & update output catalog + # try: + # if self.config["run_option"]["out_cat_only"]: + # isUpdated = True + # obj.real_pos = obj.getRealPos(chip.img, global_x=obj.posImg.x, global_y=obj.posImg.y, img_real_wcs=obj.chip_wcs) + # pos_shear = 0. + # elif chip.survey_type == "photometric" and not self.config["run_option"]["out_cat_only"]: + # isUpdated, pos_shear = obj.drawObj_multiband( + # tel=self.tel, + # pos_img=pos_img, + # psf_model=psf_model, + # bandpass_list=filt.bandpass_sub_list, + # filt=filt, + # chip=chip, + # g1=obj.g1, + # g2=obj.g2, + # exptime=pointing.exp_time, + # fd_shear=fd_shear) + + # elif chip.survey_type == "spectroscopic" and not self.config["run_option"]["out_cat_only"]: + # isUpdated, pos_shear = obj.drawObj_slitless( + # tel=self.tel, + # pos_img=pos_img, + # psf_model=psf_model, + # bandpass_list=filt.bandpass_sub_list, + # filt=filt, + # chip=chip, + # g1=obj.g1, + # g2=obj.g2, + # exptime=pointing.exp_time, + # normFilter=norm_filt, + # fd_shear=fd_shear) + + # if isUpdated == 1 and self.config["run_option"]["out_psf"]: + # obj.drawObj_PSF( + # tel=self.tel, + # pos_img=pos_img, + # psf_model=psf_model, + # bandpass_list=filt.bandpass_sub_list, + # filt=filt, + # chip=chip, + # g1=obj.g1, + # g2=obj.g2, + # exptime=pointing.exp_time, + # fd_shear=fd_shear, + # chip_output=chip_output) + + # if isUpdated == 1: + # # TODO: add up stats + # chip_output.cat_add_obj(obj, pos_img, pos_shear) + # pass + # elif isUpdated == 0: + # missed_obj += 1 + # chip_output.Log_error("Objected missed: %s"%(obj.id)) + # else: + # chip_output.Log_error("Draw error, object omitted: %s"%(obj.id)) + # continue + # except Exception as e: + # traceback.print_exc() + # chip_output.Log_error(e) - # # [C6 TEST] - # chip_output.Log_info("check running:1: pointing-{:} chip-{:} pid-{:} memory-{:6.2}GB".format(pointing.id, chip.chipID, os.getpid(), (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024) )) - # chip_output.Log_info('draw object %s'%obj.id) - # chip_output.Log_info('mag = %.3f'%obj.param['mag_use_normal']) - - # Unload SED: - obj.unload_SED() - del obj - gc.collect() + # # # [C6 TEST] + # # chip_output.Log_info("check running:1: pointing-{:} chip-{:} pid-{:} memory-{:6.2}GB".format(pointing.id, chip.chipID, os.getpid(), (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024) )) + # # chip_output.Log_info('draw object %s'%obj.id) + # # chip_output.Log_info('mag = %.3f'%obj.param['mag_use_normal']) + + # # Unload SED: + # obj.unload_SED() + # del obj + # gc.collect() - del psf_model - del self.cat - gc.collect() + # del psf_model + # del self.cat + # gc.collect() chip_output.Log_info("check running:1: pointing-%d chip-%d pid-%d memory-%6.2fGB"%(pointing.id, chip.chipID, os.getpid(), (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024) )) @@ -352,79 +365,79 @@ class Observation(object): # =========================================================== # whether to output zero, dark, flat calibration images. - if not self.config["run_option"]["out_cat_only"]: - chip.img = chip.addEffects( - config=self.config, - img=chip.img, - chip_output=chip_output, - filt=filt, - ra_cen=pointing.ra, - dec_cen=pointing.dec, - img_rot=pointing.img_pa, - exptime=pointing.exp_time, - pointing_ID=pointing.id, - timestamp_obs=pointing.timestamp, - pointing_type=pointing.pointing_type, - sky_map=sky_map, tel = self.tel, - logger=chip_output.logger) + # if not self.config["run_option"]["out_cat_only"]: + # chip.img = chip.addEffects( + # config=self.config, + # img=chip.img, + # chip_output=chip_output, + # filt=filt, + # ra_cen=pointing.ra, + # dec_cen=pointing.dec, + # img_rot=pointing.img_pa, + # exptime=pointing.exp_time, + # pointing_ID=pointing.id, + # timestamp_obs=pointing.timestamp, + # pointing_type=pointing.pointing_type, + # sky_map=sky_map, tel = self.tel, + # logger=chip_output.logger) - if pointing.pointing_type == 'SCI': - datetime_obs = datetime.utcfromtimestamp(pointing.timestamp) - date_obs = datetime_obs.strftime("%y%m%d") - time_obs = datetime_obs.strftime("%H%M%S") - h_prim = generatePrimaryHeader( - xlen=chip.npix_x, - ylen=chip.npix_y, - pointNum = str(pointing.id), - ra=pointing.ra, - dec=pointing.dec, - pixel_scale=chip.pix_scale, - date=date_obs, - time_obs=time_obs, - exptime=pointing.exp_time, - im_type='SCI', - sat_pos=[pointing.sat_x, pointing.sat_y, pointing.sat_z], - sat_vel=[pointing.sat_vx, pointing.sat_vy, pointing.sat_vz], - project_cycle=self.config["project_cycle"], - run_counter=self.config["run_counter"], - chip_name=str(chip.chipID).rjust(2, '0')) - h_ext = generateExtensionHeader( - chip=chip, - xlen=chip.npix_x, - ylen=chip.npix_y, - ra=pointing.ra, - dec=pointing.dec, - pa=pointing.img_pa.deg, - gain=chip.gain, - readout=chip.read_noise, - dark=chip.dark_noise, - saturation=90000, - pixel_scale=chip.pix_scale, - pixel_size=chip.pix_size, - xcen=chip.x_cen, - ycen=chip.y_cen, - extName='SCI', - timestamp=pointing.timestamp, - exptime=pointing.exp_time, - readoutTime=chip.readout_time) + # if pointing.pointing_type == 'SCIE': + # datetime_obs = datetime.utcfromtimestamp(pointing.timestamp) + # date_obs = datetime_obs.strftime("%y%m%d") + # time_obs = datetime_obs.strftime("%H%M%S") + # h_prim = generatePrimaryHeader( + # xlen=chip.npix_x, + # ylen=chip.npix_y, + # pointNum = str(pointing.id), + # ra=pointing.ra, + # dec=pointing.dec, + # pixel_scale=chip.pix_scale, + # date=date_obs, + # time_obs=time_obs, + # exptime=pointing.exp_time, + # im_type='SCI', + # sat_pos=[pointing.sat_x, pointing.sat_y, pointing.sat_z], + # sat_vel=[pointing.sat_vx, pointing.sat_vy, pointing.sat_vz], + # project_cycle=self.config["project_cycle"], + # run_counter=self.config["run_counter"], + # chip_name=str(chip.chipID).rjust(2, '0')) + # h_ext = generateExtensionHeader( + # chip=chip, + # xlen=chip.npix_x, + # ylen=chip.npix_y, + # ra=pointing.ra, + # dec=pointing.dec, + # pa=pointing.img_pa.deg, + # gain=chip.gain, + # readout=chip.read_noise, + # dark=chip.dark_noise, + # saturation=90000, + # pixel_scale=chip.pix_scale, + # pixel_size=chip.pix_size, + # xcen=chip.x_cen, + # ycen=chip.y_cen, + # extName='SCI', + # timestamp=pointing.timestamp, + # exptime=pointing.exp_time, + # readoutTime=chip.readout_time) - chip.img = galsim.Image(chip.img.array, dtype=np.uint16) - hdu1 = fits.PrimaryHDU(header=h_prim) - hdu1.add_checksum() - hdu1.header.comments['CHECKSUM'] = 'HDU checksum' - hdu1.header.comments['DATASUM'] = 'data unit checksum' - hdu2 = fits.ImageHDU(chip.img.array, header=h_ext) - hdu2.add_checksum() - hdu2.header.comments['XTENSION'] = 'extension type' - hdu2.header.comments['CHECKSUM'] = 'HDU checksum' - hdu2.header.comments['DATASUM'] = 'data unit checksum' - hdu1 = fits.HDUList([hdu1, hdu2]) - fname = os.path.join(chip_output.subdir, h_prim['FILENAME'] + '.fits') - hdu1.writeto(fname, output_verify='ignore', overwrite=True) - - chip_output.Log_info("# objects that are too bright %d out of %d"%(bright_obj, self.nobj)) - chip_output.Log_info("# objects that are too dim %d out of %d"%(dim_obj, self.nobj)) - chip_output.Log_info("# objects that are missed %d out of %d"%(missed_obj, self.nobj)) + # chip.img = galsim.Image(chip.img.array, dtype=np.uint16) + # hdu1 = fits.PrimaryHDU(header=h_prim) + # hdu1.add_checksum() + # hdu1.header.comments['CHECKSUM'] = 'HDU checksum' + # hdu1.header.comments['DATASUM'] = 'data unit checksum' + # hdu2 = fits.ImageHDU(chip.img.array, header=h_ext) + # hdu2.add_checksum() + # hdu2.header.comments['XTENSION'] = 'extension type' + # hdu2.header.comments['CHECKSUM'] = 'HDU checksum' + # hdu2.header.comments['DATASUM'] = 'data unit checksum' + # hdu1 = fits.HDUList([hdu1, hdu2]) + # fname = os.path.join(chip_output.subdir, h_prim['FILENAME'] + '.fits') + # hdu1.writeto(fname, output_verify='ignore', overwrite=True) + + # chip_output.Log_info("# objects that are too bright %d out of %d"%(bright_obj, self.nobj)) + # chip_output.Log_info("# objects that are too dim %d out of %d"%(dim_obj, self.nobj)) + # chip_output.Log_info("# objects that are missed %d out of %d"%(missed_obj, self.nobj)) del chip.img chip_output.Log_info("check running:2: pointing-%d chip-%d pid-%d memory-%6.2fGB"%(pointing.id, chip.chipID, os.getpid(), (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024) )) @@ -561,38 +574,57 @@ class Observation(object): chip_output.Log_info("check running:2: pointing-%d chip-%d pid-%d memory-%6.2fGB" % ( pointing.id, chip.chipID, os.getpid(), (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024))) - def runExposure_MPI_PointingList(self, pointing_list,chips=None, use_mpi=False): + def runExposure_MPI_PointingList(self, pointing_list, chips=None, use_mpi=False): if use_mpi: comm = MPI.COMM_WORLD ind_thread = comm.Get_rank() num_thread = comm.Get_size() - if chips is None: - nchips_per_fp = len(self.chip_list) - run_chips = self.chip_list - run_filts = self.filter_list - else: - # Only run a particular set of chips - run_chips = [] - run_filts = [] - nchips_per_fp = len(chips) - for ichip in range(len(self.chip_list)): - chip = self.chip_list[ichip] - filt = self.filter_list[ichip] - if chip.chipID in chips: - run_chips.append(chip) - run_filts.append(filt) - - + process_counter = 0 for ipoint in range(len(pointing_list)): + # Construct chips & filters: + pointing = pointing_list[ipoint] + pointing_ID = pointing.id + self.focal_plane = FocalPlane(chip_list=pointing.obs_param["run_chips"]) + # Make Chip & Filter lists + for i in range(self.focal_plane.nchips): + chipID = i + 1 + self.chip_list = [] + self.filter_list = [] + self.all_filters = [] + chip = Chip(chipID=chipID, config=self.config) + filter_id, filter_type = chip.getChipFilter() + filt = Filter( + filter_id=filter_id, + filter_type=filter_type, + filter_param=self.filter_param) + if not self.focal_plane.isIgnored(chipID=chipID): + self.chip_list.append(chip) + self.filter_list.append(filt) + self.all_filters.append(filt) + + if chips is None: + # Run all chips defined in configuration of this pointing + run_chips = self.chip_list + run_filts = self.filter_list + nchips_per_fp = len(self.chip_list) + else: + # Only run a particular set of chips (defined in the overall config file) + run_chips = [] + run_filts = [] + for ichip in range(len(self.chip_list)): + chip = self.chip_list[ichip] + filt = self.filter_list[ichip] + if chip.chipID in chips: + run_chips.append(chip) + run_filts.append(filt) + nchips_per_fp = len(chips) + for ichip in range(nchips_per_fp): - i = ipoint*nchips_per_fp + ichip - pointing = pointing_list[ipoint] - pointing_ID = pointing.id + i_process = process_counter + ichip if use_mpi: - if i % num_thread != ind_thread: + if i_process % num_thread != ind_thread: continue - pid = os.getpid() sub_img_dir, prefix = makeSubDir_PointingList(path_dict=self.path_dict, config=self.config, pointing_ID=pointing_ID) @@ -611,20 +643,26 @@ class Observation(object): subdir=sub_img_dir, prefix=prefix) chip_output.Log_info("running pointing#%d, chip#%d, at PID#%d..."%(pointing_ID, chip.chipID, pid)) - if self.config["obs_setting"]["survey_type"] == "CALIBRATION": - self.run_one_chip_calibration(chip=chip, - filt=filt, - chip_output=chip_output, - pointing=pointing, - skyback_level = self.config["obs_setting"]["FLAT_LEVEL"], - sky_level_filt = self.config["obs_setting"]["FLAT_LEVEL_FIL"]) - else: - self.run_one_chip( - chip=chip, - filt=filt, - chip_output=chip_output, - pointing=pointing) + self.run_one_chip( + chip=chip, + filt=filt, + chip_output=chip_output, + pointing=pointing) + # if self.config["obs_setting"]["survey_type"] == "CALIBRATION": + # self.run_one_chip_calibration(chip=chip, + # filt=filt, + # chip_output=chip_output, + # pointing=pointing, + # skyback_level = self.config["obs_setting"]["FLAT_LEVEL"], + # sky_level_filt = self.config["obs_setting"]["FLAT_LEVEL_FIL"]) + # else: + # self.run_one_chip( + # chip=chip, + # filt=filt, + # chip_output=chip_output, + # pointing=pointing) chip_output.Log_info("finished running chip#%d..."%(chip.chipID)) for handler in chip_output.logger.handlers[:]: chip_output.logger.removeHandler(handler) gc.collect() + process_counter += nchips_per_fp diff --git a/ObservationSim/SimSteps.py b/ObservationSim/SimSteps.py new file mode 100644 index 0000000..b111993 --- /dev/null +++ b/ObservationSim/SimSteps.py @@ -0,0 +1,456 @@ +import os +import galsim +import traceback +import gc +import psutil +import numpy as np +from astropy.io import fits +from datetime import datetime +from numpy.random import Generator, PCG64 + +from ObservationSim._util import get_shear_field +from ObservationSim.Straylight import calculateSkyMap_split_g +from ObservationSim.Config.Header import generatePrimaryHeader, generateExtensionHeader +from ObservationSim.PSF import PSFGauss, FieldDistortion, PSFInterp, PSFInterpSLS +from ObservationSim.Instrument.Chip import ChipUtils as chip_utils +from ObservationSim.Instrument.Chip import Effects +from ObservationSim.Instrument.Chip.libCTI.CTI_modeling import CTI_sim + +class SimSteps: + def __init__(self, overall_config, chip_output, all_filters): + self.overall_config = overall_config + self.chip_output = chip_output + self.all_filters = all_filters + + def prepare_headers(self, chip, pointing): + datetime_obs = datetime.utcfromtimestamp(pointing.timestamp) + date_obs = datetime_obs.strftime("%y%m%d") + time_obs = datetime_obs.strftime("%H%M%S") + self.h_prim = generatePrimaryHeader( + xlen=chip.npix_x, + ylen=chip.npix_y, + pointNum = str(pointing.id), + ra=pointing.ra, + dec=pointing.dec, + pixel_scale=chip.pix_scale, + date=date_obs, + time_obs=time_obs, + exptime=pointing.exp_time, + im_type=pointing.pointing_type, + sat_pos=[pointing.sat_x, pointing.sat_y, pointing.sat_z], + sat_vel=[pointing.sat_vx, pointing.sat_vy, pointing.sat_vz], + project_cycle=self.overall_config["project_cycle"], + run_counter=self.overall_config["run_counter"], + chip_name=str(chip.chipID).rjust(2, '0')) + self.h_ext = generateExtensionHeader( + chip=chip, + xlen=chip.npix_x, + ylen=chip.npix_y, + ra=pointing.ra, + dec=pointing.dec, + pa=pointing.img_pa.deg, + gain=chip.gain, + readout=chip.read_noise, + dark=chip.dark_noise, + saturation=90000, + pixel_scale=chip.pix_scale, + pixel_size=chip.pix_size, + xcen=chip.x_cen, + ycen=chip.y_cen, + extName=pointing.pointing_type, + timestamp = pointing.timestamp, + exptime = pointing.exp_time, + readoutTime = chip.readout_time) + return self.h_prim, self.h_ext + + def add_sky_background(self, chip, filt, tel, pointing, catalog, obs_param): + flat_normal = np.ones_like(chip.img.array) + if obs_param["flat_fielding"] == True: + flat_normal = flat_normal * chip.flat_img.array / np.mean(chip.flat_img.array) + if obs_param["shutter_effect"] == True: + flat_normal = flat_normal * chip.shutter_img + flat_normal = np.array(flat_normal, dtype='float32') + + if obs_param["enable_straylight_model"]: + # Filter.sky_background, Filter.zodical_spec will be updated + filt.setFilterStrayLightPixel( + jtime = pointing.jdt, + sat_pos = np.array([pointing.sat_x, pointing.sat_y, pointing.sat_z]), + pointing_radec = np.array([pointing.ra,pointing.dec]), + sun_pos = np.array([pointing.sun_x, pointing.sun_y, pointing.sun_z])) + self.chip_output.Log_info("================================================") + self.chip_output.Log_info("sky background + stray light pixel flux value: %.5f"%(filt.sky_background)) + + if chip.survey_type == "photometric": + sky_map = filt.getSkyNoise(exptime = obs_param["exptime"]) + sky_map = sky_map * np.ones_like(chip.img.array) * flat_normal + sky_map = galsim.Image(array=sky_map) + else: + # chip.loadSLSFLATCUBE(flat_fn='flat_cube.fits') + sky_map = calculateSkyMap_split_g( + skyMap=flat_normal, + blueLimit=filt.blue_limit, + redLimit=filt.red_limit, + conf=chip.sls_conf, + pixelSize=chip.pix_scale, + isAlongY=0, + flat_cube=chip.flat_cube, + zoldial_spec = filt.zodical_spec) + sky_map = sky_map + filt.sky_background + + sky_map = sky_map * tel.pupil_area * obs_param["exptime"] + chip.img += sky_map + return chip, filt, tel, pointing + + def add_objects(self, chip, filt, tel, pointing, catalog, obs_param): + + # Prepare output file(s) for this chip + self.chip_output.create_output_file() + + # Prepare the PSF model + if self.overall_config["psf_setting"]["psf_model"] == "Gauss": + psf_model = PSFGauss(chip=chip, psfRa=self.overall_config["psf_setting"]["psf_rcont"]) + elif self.overall_config["psf_setting"]["psf_model"] == "Interp": + if chip.survey_type == "spectroscopic": + psf_model = PSFInterpSLS(chip, filt, PSF_data_prefix=self.overall_config["psf_setting"]["psf_sls_dir"]) + else: + psf_model = PSFInterp(chip=chip, npsf=chip.n_psf_samples, PSF_data_file=self.overall_config["psf_setting"]["psf_pho_dir"]) + else: + self.chip_output.Log_error("unrecognized PSF model type!!", flush=True) + + # Apply field distortion model + if obs_param["field_dist"] == True: + fd_model = FieldDistortion(chip=chip, img_rot=pointing.img_pa.deg) + else: + fd_model = None + + # Update limiting magnitudes for all filters based on the exposure time + # Get the filter which will be used for magnitude cut + for ifilt in range(len(self.all_filters)): + temp_filter = self.all_filters[ifilt] + temp_filter.update_limit_saturation_mags(exptime=pointing.get_full_depth_exptime(temp_filter.filter_type), chip=chip) + if temp_filter.filter_type.lower() == self.overall_config["obs_setting"]["cut_in_band"].lower(): + cut_filter = temp_filter + + # Read in shear values from configuration file if the constant shear type is used + if self.overall_config["shear_setting"]["shear_type"] == "constant": + g1_field, g2_field, _ = get_shear_field(config=self.overall_config) + self.chip_output.Log_info("Use constant shear: g1=%.5f, g2=%.5f"%(g1_field, g2_field)) + + # Get chip WCS + if not hasattr(self, 'h_ext'): + _, _ = self.prepare_headers(chip=chip, pointing=pointing) + chip_wcs = galsim.FitsWCS(header = self.h_ext) + + # Loop over objects + nobj = len(catalog.objs) + missed_obj = 0 + bright_obj = 0 + dim_obj = 0 + for j in range(nobj): + # # [DEBUG] [TODO] + # if j >= 10: + # break + obj = catalog.objs[j] + + # load and convert SED; also caculate object's magnitude in all CSST bands + try: + sed_data = catalog.load_sed(obj) + norm_filt = catalog.load_norm_filt(obj) + obj.sed, obj.param["mag_%s"%filt.filter_type.lower()], obj.param["flux_%s"%filt.filter_type.lower()] = catalog.convert_sed( + mag=obj.param["mag_use_normal"], + sed=sed_data, + target_filt=filt, + norm_filt=norm_filt, + ) + _, obj.param["mag_%s"%cut_filter.filter_type.lower()], obj.param["flux_%s"%cut_filter.filter_type.lower()] = catalog.convert_sed( + mag=obj.param["mag_use_normal"], + sed=sed_data, + target_filt=cut_filter, + norm_filt=norm_filt, + ) + except Exception as e: + traceback.print_exc() + self.chip_output.Log_error(e) + continue + + # [TODO] Testing + # self.chip_output.Log_info("mag_%s = %.3f"%(filt.filter_type.lower(), obj.param["mag_%s"%filt.filter_type.lower()])) + + # Exclude very bright/dim objects (for now) + if cut_filter.is_too_bright( + mag=obj.param["mag_%s"%self.overall_config["obs_setting"]["cut_in_band"].lower()], + margin=self.overall_config["obs_setting"]["mag_sat_margin"]): + self.chip_output.Log_info("obj %s too birght!! mag_%s = %.3f"%(obj.id, cut_filter.filter_type, obj.param["mag_%s"%self.overall_config["obs_setting"]["cut_in_band"].lower()])) + bright_obj += 1 + obj.unload_SED() + continue + if filt.is_too_dim( + mag=obj.getMagFilter(filt), + margin=self.overall_config["obs_setting"]["mag_lim_margin"]): + self.chip_output.Log_info("obj %s too dim!! mag_%s = %.3f"%(obj.id, filt.filter_type, obj.getMagFilter(filt))) + dim_obj += 1 + obj.unload_SED() + continue + + # Get corresponding shear values + if self.overall_config["shear_setting"]["shear_type"] == "constant": + if obj.type == 'star': + obj.g1, obj.g2 = 0., 0. + else: + # Figure out shear fields from overall configuration shear setting + obj.g1, obj.g2 = g1_field, g2_field + elif self.overall_config["shear_setting"]["shear_type"] == "catalog": + pass + else: + self.chip_output.Log_error("Unknown shear input") + raise ValueError("Unknown shear input") + + # Get position of object on the focal plane + pos_img, _, _, _, fd_shear = obj.getPosImg_Offset_WCS(img=chip.img, fdmodel=fd_model, chip=chip, verbose=False, chip_wcs=chip_wcs, img_header=self.h_ext) + + # [TODO] For now, only consider objects which their centers (after field distortion) are projected within the focal plane + # Otherwise they will be considered missed objects + # if pos_img.x == -1 or pos_img.y == -1 or (not chip.isContainObj(x_image=pos_img.x, y_image=pos_img.y, margin=0.)): + if pos_img.x == -1 or pos_img.y == -1: + self.chip_output.Log_info('obj_ra = %.6f, obj_dec = %.6f, obj_ra_orig = %.6f, obj_dec_orig = %.6f'%(obj.ra, obj.dec, obj.ra_orig, obj.dec_orig)) + self.chip_output.Log_error("Objected missed: %s"%(obj.id)) + missed_obj += 1 + obj.unload_SED() + continue + + # Draw object & update output catalog + try: + if self.overall_config["run_option"]["out_cat_only"]: + isUpdated = True + obj.real_pos = obj.getRealPos(chip.img, global_x=obj.posImg.x, global_y=obj.posImg.y, img_real_wcs=obj.chip_wcs) + pos_shear = 0. + elif chip.survey_type == "photometric" and not self.overall_config["run_option"]["out_cat_only"]: + isUpdated, pos_shear = obj.drawObj_multiband( + tel=tel, + pos_img=pos_img, + psf_model=psf_model, + bandpass_list=filt.bandpass_sub_list, + filt=filt, + chip=chip, + g1=obj.g1, + g2=obj.g2, + exptime=pointing.exp_time, + fd_shear=fd_shear) + + elif chip.survey_type == "spectroscopic" and not self.overall_config["run_option"]["out_cat_only"]: + isUpdated, pos_shear = obj.drawObj_slitless( + tel=tel, + pos_img=pos_img, + psf_model=psf_model, + bandpass_list=filt.bandpass_sub_list, + filt=filt, + chip=chip, + g1=obj.g1, + g2=obj.g2, + exptime=pointing.exp_time, + normFilter=norm_filt, + fd_shear=fd_shear) + + if isUpdated == 1: + # TODO: add up stats + self.chip_output.cat_add_obj(obj, pos_img, pos_shear) + pass + elif isUpdated == 0: + missed_obj += 1 + self.chip_output.Log_error("Objected missed: %s"%(obj.id)) + else: + self.chip_output.Log_error("Draw error, object omitted: %s"%(obj.id)) + continue + except Exception as e: + traceback.print_exc() + self.chip_output.Log_error(e) + + # Unload SED: + obj.unload_SED() + del obj + gc.collect() + del psf_model + gc.collect() + + self.chip_output.Log_info("Running checkpoint #1 (Object rendering finished): pointing-%d chip-%d pid-%d memory-%6.2fGB"%(pointing.id, chip.chipID, os.getpid(), (psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024 / 1024) )) + + self.chip_output.Log_info("# objects that are too bright %d out of %d"%(bright_obj, nobj)) + self.chip_output.Log_info("# objects that are too dim %d out of %d"%(dim_obj, nobj)) + self.chip_output.Log_info("# objects that are missed %d out of %d"%(missed_obj, nobj)) + + # Apply flat fielding (with shutter effects) + flat_normal = np.ones_like(chip.img.array) + if obs_param["flat_fielding"] == True: + flat_normal = flat_normal * chip.flat_img.array / np.mean(chip.flat_img.array) + if obs_param["shutter_effect"] == True: + flat_normal = flat_normal * chip.shutter_img + flat_normal = np.array(flat_normal, dtype='float32') + chip.img *= flat_normal + del flat_normal + + return chip, filt, tel, pointing + + def add_cosmic_rays(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info(msg=" Adding Cosmic-Ray", logger=self.logger) + chip.img, crmap_gsimg, cr_event_num = chip_utils.add_cosmic_rays( + img=chip.img, + chip=chip, + exptime=pointing.exptime, + seed=self.overall_config["random_seeds"]["seed_CR"]+pointing.id*30+chip.chipID) + # [TODO] output cosmic ray image + return chip, filt, tel, pointing + + def apply_PRNU(self, chip, filt, tel, pointing, catalog, obs_param): + chip.img *= chip.prnu_img + return chip, filt, tel, pointing + + def add_poisson_and_dark(self, chip, filt, tel, pointing, catalog, obs_param): + # Add dark current & Poisson noise + InputDark = False + if obs_param["add_dark"] == True: + if InputDark: + chip.img = chip_utils.add_inputdark(img=chip.img, chip=chip, exptime=pointing.exptime) + else: + chip.img, _ = chip_utils.add_poisson(img=chip.img, chip=chip, exptime=pointing.exptime, poisson_noise=chip.poisson_noise) + else: + chip.img, _ = chip_utils.add_poisson(img=chip.img, chip=self, exptime=pointing.exptime, poisson_noise=chip.poisson_noise, dark_noise=0.) + return chip, filt, tel, pointing + + def add_brighter_fatter(self, chip, filt, tel, pointing, catalog, obs_param): + chip.img = chip_utils.add_brighter_fatter(img=chip.img) + return chip, filt, tel, pointing + + def add_detector_defects(self, chip, filt, tel, pointing, catalog, obs_param): + # Add Hot Pixels or/and Dead Pixels + rgbadpix = Generator(PCG64(int(self.overall_config["random_seeds"]["seed_defective"]+chip.chipID))) + badfraction = 5E-5*(rgbadpix.random()*0.5+0.7) + chip.img = Effects.DefectivePixels( + chip.img, + IfHotPix=obs_param["hot_pixels"], + IfDeadPix=obs_param["dead_pixels"], + fraction=badfraction, + seed=self.overall_config["random_seeds"]["seed_defective"]+chip.chipID, biaslevel=0) + # Apply Bad columns + if obs_param["bad_columns"] == True: + chip.img = Effects.BadColumns(chip.img, seed=self.overall_config["random_seeds"]["seed_badcolumns"], chipid=chip.chipID) + return chip, filt, tel, pointing + + def add_nonlinearity(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info(" Applying Non-Linearity on the chip image") + chip.img = Effects.NonLinearity(GSImage=chip.img, beta1=5.e-7, beta2=0) + return chip, filt, tel, pointing + + def add_blooming(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info(" Applying CCD Saturation & Blooming") + chip.img = Effects.SaturBloom(GSImage=chip.img, nsect_x=1, nsect_y=1, fullwell=int(chip.full_well)) + return chip, filt, tel, pointing + + def apply_CTE(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info(" Apply CTE Effect") + ### 2*8 -> 1*16 img-layout + img = chip_utils.formatOutput(GSImage=chip.img) + chip.nsecy = 1 + chip.nsecx = 16 + + img_arr = img.array + ny, nx = img_arr.shape + dx = int(nx/chip.nsecx) + dy = int(ny/chip.nsecy) + newimg = galsim.Image(nx, int(ny+chip.overscan_y), init_value=0) + for ichannel in range(16): + print('\n***add CTI effects: pointing-{:} chip-{:} channel-{:}***'.format(pointing.id, chip.chipID, ichannel+1)) + noverscan, nsp, nmax = self.overscan_y, 3, 10 + beta, w, c = 0.478, 84700, 0 + t = np.array([0.74, 7.7, 37],dtype=np.float32) + rho_trap = np.array([0.6, 1.6, 1.4],dtype=np.float32) + trap_seeds = np.array([0, 1000, 10000],dtype=np.int32) + ichannel + chip.chipID*16 + release_seed = 50 + ichannel + pointing.id*30 + chip.chipID*16 + newimg.array[:, 0+ichannel*dx:dx+ichannel*dx] = CTI_sim(img_arr[:, 0+ichannel*dx:dx+ichannel*dx],dx,dy,noverscan,nsp,nmax,beta,w,c,t,rho_trap,trap_seeds,release_seed) + newimg.wcs = img.wcs + del img + img = newimg + + ### 1*16 -> 2*8 img-layout + chip.img = chip_utils.formatRevert(GSImage=img) + chip.nsecy = 2 + chip.nsecx = 8 + + # [TODO] make overscan_y == 0 + chip.overscan_y = 0 + return chip, filt, tel, pointing + + def add_prescan_overscan(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info("Apply pre/over-scan") + chip.img = chip_utils.AddPreScan(GSImage=chip.img, + pre1=chip.prescan_x, + pre2=chip.prescan_y, + over1=chip.overscan_x, + over2=chip.overscan_y) + return chip, filt, tel, pointing + + def add_bias(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info(" Adding Bias level and 16-channel non-uniformity") + if obs_param["bias_16channel"] == True: + chip.img = Effects.AddBiasNonUniform16(chip.img, + bias_level=float(chip.bias_level), + nsecy = chip.nsecy, nsecx=chip.nsecx, + seed=self.overall_config["random_seeds"]["seed_biasNonUniform"]+chip.chipID) + elif obs_param["bias_16channel"] == False: + chip.img += self.bias_level + return chip, filt, tel, pointing + + def add_readout_noise(self, chip, filt, tel, pointing, catalog, obs_param): + seed = int(self.overall_config["random_seeds"]["seed_readout"]) + pointing.id*30 + chip.chipID + rng_readout = galsim.BaseDeviate(seed) + readout_noise = galsim.GaussianNoise(rng=rng_readout, sigma=chip.read_noise) + chip.img.addNoise(readout_noise) + return chip, filt, tel, pointing + + def apply_gain(self, chip, filt, tel, pointing, catalog, obs_param): + self.chip_output.Log_info(" Applying Gain") + if obs_param["gain_16channel"] == True: + chip.img, chip.gain_channel = Effects.ApplyGainNonUniform16( + chip.img, gain=chip.gain, + nsecy = self.nsecy, nsecx=self.nsecx, + seed=self.overall_config["random_seeds"]["seed_gainNonUniform"]+chip.chipID) + elif obs_param["gain_16channel"] == False: + chip.img /= chip.gain + return chip, filt, tel, pointing + + def quantization_and_output(self, chip, filt, tel, pointing, catalog, obs_param): + chip.img.array[chip.img.array > 65535] = 65535 + chip.img.replaceNegative(replace_value=0) + chip.img.quantize() + chip.img = galsim.Image(chip.img.array, dtype=np.uint16) + hdu1 = fits.PrimaryHDU(header=self.h_prim) + hdu1.add_checksum() + hdu1.header.comments['CHECKSUM'] = 'HDU checksum' + hdu1.header.comments['DATASUM'] = 'data unit checksum' + hdu2 = fits.ImageHDU(chip.img.array, header=self.h_ext) + hdu2.add_checksum() + hdu2.header.comments['XTENSION'] = 'extension type' + hdu2.header.comments['CHECKSUM'] = 'HDU checksum' + hdu2.header.comments['DATASUM'] = 'data unit checksum' + hdu1 = fits.HDUList([hdu1, hdu2]) + fname = os.path.join(self.chip_output.subdir, self.h_prim['FILENAME'] + '.fits') + hdu1.writeto(fname, output_verify='ignore', overwrite=True) + return chip, filt, tel, pointing + +SIM_STEP_TYPES = { + "scie_obs": "add_objects", + "sky_background": "add_sky_background", + "cosmic_rays": "add_cosmic_rays", + "PRNU_effect": "apply_PRNU", + "poisson_and_dark": "add_poisson_and_dark", + "bright_fatter": "add_brighter_fatter", + "detector_defects": "add_detector_defects", + "nonlinearity": "add_nonlinearity", + "blooming": "add_blooming", + "CTE_effect": "apply_CTE", + "prescan_overscan": "add_prescan_overscan", + "bias": "add_bias", + "readout_noise": "add_readout_noise", + "gain": "apply_gain", + "quantization_and_output": "quantization_and_output" +} \ No newline at end of file diff --git a/ObservationSim/_util.py b/ObservationSim/_util.py index 67ac785..d7059fa 100755 --- a/ObservationSim/_util.py +++ b/ObservationSim/_util.py @@ -96,7 +96,7 @@ def make_run_dirs(work_dir, run_name, pointing_list): os.makedirs(imgDir, exist_ok=True) except OSError: pass - prefix = "MSC_" + # prefix = "MSC_" # for pointing in pointing_list: # fname=prefix + str(pointing.id).rjust(7, '0') # subImgDir = os.path.join(imgDir, fname) @@ -107,26 +107,26 @@ def make_run_dirs(work_dir, run_name, pointing_list): # pass return imgDir -def imgName(tt=0): - ut = datetime.utcnow() - eye, emo, eda, eho, emi, ese = str(ut.year), str(ut.month), str(ut.day), str(ut.hour), str(ut.minute), str(ut.second) - emse = str(ut.microsecond) - if int(emo)<10: emo = "0%s"%emo - if int(eda)<10: eda = "0%s"%eda - if int(eho)<10: eho = "0%s"%eho - if int(emi)<10: emi = "0%s"%emi - if int(ese)<10: ese = "0%s"%ese +# def imgName(tt=0): +# ut = datetime.utcnow() +# eye, emo, eda, eho, emi, ese = str(ut.year), str(ut.month), str(ut.day), str(ut.hour), str(ut.minute), str(ut.second) +# emse = str(ut.microsecond) +# if int(emo)<10: emo = "0%s"%emo +# if int(eda)<10: eda = "0%s"%eda +# if int(eho)<10: eho = "0%s"%eho +# if int(emi)<10: emi = "0%s"%emi +# if int(ese)<10: ese = "0%s"%ese - if tt==0: - namekey = "CSST%s%s%sT%s%s%s"%(eye,emo,eda,eho,emi,ese) - elif tt==1: - namekey = "%s-%s-%sT%s:%s:%s.%s"%(eye,emo,eda,eho,emi,ese,emse) - elif tt==2: - namekey = "%s%s%s%s%s%s"%(eye,emo,eda,eho,emi,ese) - else: - raise ValueError("!!! Give a right 'tt' value.") +# if tt==0: +# namekey = "CSST%s%s%sT%s%s%s"%(eye,emo,eda,eho,emi,ese) +# elif tt==1: +# namekey = "%s-%s-%sT%s:%s:%s.%s"%(eye,emo,eda,eho,emi,ese,emse) +# elif tt==2: +# namekey = "%s%s%s%s%s%s"%(eye,emo,eda,eho,emi,ese) +# else: +# raise ValueError("!!! Give a right 'tt' value.") - return namekey +# return namekey def makeSubDir_PointingList(path_dict, config, pointing_ID=0): imgDir = os.path.join(path_dict["work_dir"], config["run_name"]) diff --git a/config/config_overall.yaml b/config/config_overall.yaml index 5cf6fd8..b1d4d0d 100644 --- a/config/config_overall.yaml +++ b/config/config_overall.yaml @@ -73,9 +73,6 @@ obs_setting: # Whether to enable astrometric modeling enable_astrometric_model: True - # Whether to enable straylight model - enable_straylight_model: True - # Cut by saturation magnitude in which band? cut_in_band: "z" diff --git a/config/obs_config_SCI_WIDE.yaml b/config/obs_config_SCI_WIDE.yaml deleted file mode 100644 index 346f7f5..0000000 --- a/config/obs_config_SCI_WIDE.yaml +++ /dev/null @@ -1,25 +0,0 @@ ---- -############################################### -# -# Configuration file for CSST simulation -# For single exposure type: -# SCI-WIDE -# CSST-Sim Group, 2024/01/08 -# -############################################### - -# Observation type -obs_type: "SCIE" - -# Define simulation sequence -call_sequence: - # Sky background simulation - sky_background: - exptime: 150. # [s] - shutter_effect: YES - flat_fielding: YES - scie_obs: - exptime: 150. # [s] - shutter_effect: YES - flat_fielding: YES -... \ No newline at end of file diff --git a/config/obs_config_SCI_WIDE_phot.yaml b/config/obs_config_SCI_WIDE_phot.yaml new file mode 100644 index 0000000..2002fb4 --- /dev/null +++ b/config/obs_config_SCI_WIDE_phot.yaml @@ -0,0 +1,61 @@ +--- +############################################### +# +# Configuration file for CSST simulation +# For single exposure type: +# SCI-WIDE +# CSST-Sim Group, 2024/01/08 +# +############################################### + +# Observation type +obs_type: "SCIE" + +# Define list of chips +run_chips: [6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25] + +# Define observation sequence +call_sequence: + # Accumulate fluxes from objects + scie_obs: + exptime: 150. # [s] + shutter_effect: YES + flat_fielding: YES + field_dist: YES + # Accumulate fluxes from sky background + sky_background: + exptime: 150. # [s] + shutter_effect: YES + flat_fielding: YES + enable_straylight_model: True + # Apply PRNU to accumulated photons + PRNU_effect: {} + # Accumulate photons caused by cosmic rays + cosmic_rays: {} + # Add Poission noise and dark current + poisson_and_dark: + add_dark: YES + # Simulate brighter fatter effects + bright_fatter: {} + # Add detector defects: hot/warm pixels, bad columns + detector_defects: + hot_pixels: YES + dead_pixels: YES + bad_columns: YES + # Apply response nonlinearity + nonlinearity: {} + # Apply CCD Saturation & Blooming + blooming: {} + # Run CTE simulation + CTE_effect: {} + # Add prescan and overscan + prescan_overscan: {} + # Add bias + bias: + bias_16channel: YES + # Add readout noise + readout_noise: {} + # Apply gain + gain: + gain_16channel: YES +... \ No newline at end of file diff --git a/run_sim.py b/run_sim.py index b0a3428..c9d1ade 100755 --- a/run_sim.py +++ b/run_sim.py @@ -50,10 +50,10 @@ def run_sim(): config['work_dir'] = args.work_dir # Some default values - if "bias_16channel" not in config["ins_effects"]: - config["ins_effects"]["bias_16channel"] = False - if "gain_16channel" not in config["ins_effects"]: - config["ins_effects"]["gain_16channel"] = False + # if "bias_16channel" not in config["ins_effects"]: + # config["ins_effects"]["bias_16channel"] = False + # if "gain_16channel" not in config["ins_effects"]: + # config["ins_effects"]["gain_16channel"] = False if "mag_sat_margin" not in config["obs_setting"]: config["obs_setting"]["mag_sat_margin"] = -2.5 if "mag_lim_margin" not in config["obs_setting"]: -- GitLab